k8s系列文章6: 资源对象介绍

  前面的文章已经介绍了用kubeadm搭建一套简单的k8s集群,并在上面运行了一个简单的服务示例。下面介绍一下k8s中比较重要的资源对象:

1. Master

  Master是k8s的控制节点,在每个k8s中,都必须有一个Master负责整个集群的管理和控制,基本上k8s所有的控制命令都发给它。如果Master节点宕机,整个集群都将不能使用。Master节点是k8s的“大脑”。Master节点上运行着以下关键进程:

  • Kubernetes API Server(kube-apiserver):提供了http REST接口的关键服务进程,是k8s里所有资源的增删改查等操作的唯一入口,也是集群控制入口进程。
  • Kubernetes Controller Manager(Kube-controller-manager):k8s里所有对象的自动化控制中心,可以把它理解成资源对象的“大总管”。
  • Kubernetes Scheduler(kube-scheduler):负责资源调度(pod调度)的进程,相当于公交公司的调度室。
      同时,在Master上通常还需要部署etcd服务,因为Kubernetes里的所有资源对象的数据都被保存在etcd中。

2. Node

  除了Master节点,k8s集群中的其他机器,无论是虚拟机还是物理机,都是Node节点。每个Node都会被Master分配一些工作负载。Node上运行着以下关键进程:

  • kubelet:负责Pod对应的容器的创建、启停等任务,同时于Master密切协作,实现集群管理的基本功能。
  • kube-proxy:实现Kubernetes Service的通信和负载均衡的重要组件。
  • Docker Engine(docker):Docker引擎,负责本机的容器创建和管理工作。
      Node可以在运行期间动态增加到kubernetes集群中,前提是在这个节点上正确安装、配置和启动了上述关键进程,默认情况下,kubelet会向Master注册自己,这也是Kubernetes推荐的ode管理方式。一旦Node被纳入集群管理范围,kubelet进程就会定时向Master汇报自身的情报,例如操作系统、Docker版本,机器的CPU和内存等情况;以及哪些Pod在运行等。这样Master就可以获知每个Node的资源使用情况。若某个Node在超过指定时间范围内不上报信息时,会被Master判定为“失联”,Node的状态被标记为不可用(Not Ready),随后Master会触发“工作负载大转移”的自动流程。
      执行下列命令查看在集群中有多少个Node:
kubectl get nodes
获取Nodes.png

  然后,通过以下命令查看某个Node的详细信息:

kubectl describe node host-10-5-3-206
Node详细信息

  上述命令展示Node的基本关键信息,如下:

  • Node的基本信息:名称、标签、创建时间等。
  • Node当前的运行状态:Node启动后会做一系列的自检工作。
  • Node的主机地址和主机名。
  • Node上的资源数量。
  • Node的可分配的资源量。
  • 主机系统信息。
  • 当前运行的Pod列表的该要信息。
  • 已分配的资源使用该要信息,例如资源申请的最低、最大允许使用量等。
  • Node相关的Event信息。

3. pod

pod是k8s最基础重要的基本概念,每个pod都有一个特殊的被称为“跟容器”pause容器。pause容器对应的镜像属于k8s平台的一部分,除了Pause容器,每个Pod还包含一个或多个紧密相关的业务容器。
pod的组成示意图

  k8s设计pod的原因如下:

  • 在一组容器作为一个单元的情况下,很难简单地对“整体”进行有效的判断和行动。引入业务无关并且不易死亡的Pause容器作为pod的跟容器,以它的状态代表整个容器的状态,就很巧妙的解决了问题。
  • Pod里的多个业务容器共享Pause容器的IP,共享Pause容挂接的Volume。这样不仅简化了业务容器之间的通信问题,也很好的解决了这一组容器的文件共享问题。
      在k8s中,一个pod容器能直接和另一个主机上的容器进行通信。Pod有两种类型,分别是普通额Pod和静态的 Pod。后者是一种特殊的Pod,它并不存放在k8s的etcd上,而是被存在某个具体的Node的一个主机文件中,并且只在此Node上启动、运行。普通的pod一旦被创建,就会被放在etcd中存储,随后会被Kubernetes Master调度到某个Node上并进行绑定(binding),随后该pod被对应的Node上的kubelet进程示例化成一组相关的Docker容器并启动。在默认情况下,当Pod里面的某个容器停止时,k8s会自动检测这个问题摒弃重新启动这个pod-->实际上就是重启pod中的所有容器,如果pod所在的Node宕机,就回将这个Node上的所有Pod重新调度到其他节点上。pod、node和Master的关系如下图所示:


    pod,node和容器的关系

      每个Pod都可以对其能使用的资源设置限额,当前可以设置限额的计算资源有CPU和Memory两种,其中CPU的资源单位为CPU(code)的数量,是一个绝对值而非相对值。
      对于绝大多数的容器而言,一个CPU是相当大的,k8s中以千分之一的CPU为最小资源单位,用m表示。通常一个容器的CPU的配额是100m~300m。在k8s中,一个计算资源进行配额限定时需要设以下两个参数:

  • Request:该资源的最小申请量,系统必须满足要求;
  • Limits:该资源允许使用的最大量,不能被突破。下面的示例定义mysql容器能使用的资源配额为0.5个CPU及128MiB内存:
spec:
    containers:
    - name: db
      image: mysql
      resources: 
          requests:  #一般情况下
              memory: "64Mi"
              cpu: "250m"
          limits:      #最大值
              memory: "128Mi" 
              cpu: "500m"

4. Labels

  Lables是k8s系统中另一个核心的概念,一个Label是一个key=value形式的键值对,其中key和value由用户自己指定。Label相当于我们熟悉的标签,通过Label Selector进行选择。Label Selector可以理解成是SQL语句中的where语句。目前有两种Label Selector表达式,分别是分别是:

  • Equality-based:eg:name=redis-slave匹配所有具有redis-slave标签的资源
  • Set-based:eg:name in (redis-slave, redis-master) 匹配具有redis-slave和redis-master的对象
    可以通过多个label Selector语句的组合实现复杂的条件筛选,多个表达式之间用“,”连接。matchLabels用于定义一组Label,和直接在Selector中的作用相同。Label Selector在k8s中的重要使用场景如下:
  • kube-controller进程通过在资源对象RC上定义的Label Selector来筛选要监控的pod副本的数量。
  • kube-proxy进程通过Service的Label Selector来选择对应的pod,自动建立每个service到对应pod的请求转发路由表,从而实现service智能负载均衡机制。
  • 通过对某些Node定义特定的Label,并且在Pod定义中使用NodeSelector这种标签,kube-scheduler进程可以实现pod定向调度的特性。
      总之,使用Label可以给对象创建多组标签,Label和LabelSelector共同构成来k8s系统中核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现来整个集群的高可用性。

5. Replication Controller

  RC是k8s系统中的核心概念之一,简单来说,它其实定义了一个期望的场景,即声明某种Pod的副本的数量在任意时刻都符合某个预期值,所以RC定义包括如下几个部分。

  • Pod期待的副本的数量
  • 用于筛选目标Pod的Label Selector
  • 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模版(template)
      下面是一个完整的RC定义的例子,作用是确保拥有tier=frontend标签的这个Pod(运行Tomcat容器)在整个k8s集群中始终只有一个副本:
apiVersion: v1
kind: ReplocationController
metadata: frontend
spec:
    replicas: 1
    selector: 
        tier: frontend
template:
    metadata:
        labels:
            app: app-demo
            tier: frontend
    spec:
        container:
         - name: tomcat-demo
           image: tomcat
           imagePullPolicy: IfNotPresent
           env:
            - name: GET_HOSTS_FROM
              value: dns
           ports:
           - containerPort: 80

  在定义了一个RC并将其提交到k8s集群中后,Master上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保Pod示例的数量刚好等于此RC的期望值,如果有过多的Pod副本的存在运行,系统就会停掉一些Pod,否则系统会自动创建一些Pod。可以说,通过RC,k8s实现了用户应用集群的高可用性,并且大大减少了系统管理员在传统IT环境中需要完成的许多手工运维工作。在运行时,可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行kubectl scale命令来一键完成。

kubectl scale rc redis-slave --replicas=3

  需要注意的是,删除RC并不会影戏那个通过该RC已经创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC。同时,kubectl提供了stop和delete命令来一次性删除RC和RC控制的全部Pod。RC支持滚动升级。Replication Controller由于与k8s中的Replication Controller同名,k8s在1.2之后,升级为另一个新的概念,Replica Set。官方解释是“新一代的RC”,当前的唯一区别是,Replica Sets支持基于Label selector(Set-based selector),而RC只支持基于等式的Label Selector(equality-based selector),这使得Replica Set的功能更强。下面等价于之前RC例子的Replica Set的定义(省去来Pod模版部分的内容):

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
    name: frontend
spec:
    selector:
        matchlabels:
            tier: frontend
        matchExpressions:
          - {key: tier, operator: in, values: [frontend]}
    template:
    .....

  当前,我们很少使用Replica Set,它主要被Deployment这个更高级的资源对象所使用,从而形成一整套Pod创建建设、删除、更新的编排机制。Replica Set和Deplotment这两个重要的资源对象逐渐替代了之前的RC,是k8s 1.3里Pod自动弹缩这个告警功能实现的基础。
综上:

  • 在大多数情况下,我们定义一个RC实现Pod的创建及副本数量的自动控制
  • 在RC里包括完整的Pod的定义模版
  • RC通过label Selector机制实现对Pod副本的自动控制
  • 通过改变RC里的Pod副本数量,可以实现Pod的扩容和缩容
  • 通过改变RC里Pod模版中的镜像版本,可以实现Pod的滚动升级

6. Deployment

  Deployment是k8s在1.2版本中引入的概念,用于更好地解决Pod的编排问题。从各种角度来看,Deployment和RC的相似度超过90%。Deployment相对于RC的一个最大升级就是我们可以随时知道Pod“部署”的进度。实际上由于一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一完整过程需要一定的时间,所以 我们期待系统启动N个Pod副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。

7. Horizontal Pod Autoscaler

  有的场景下,需要频繁的触发水平扩容和缩容。1.1版本发布了Horizontal Pod Autoscale(HPA),HPA有以下两种方式作为Pod负载的度量指标。

  • CPUUtilozationPercentage
  • 应用程序自定义的度量指标,比如服务在每秒内的相应请求数量(TPS或QPS)
      前者是一个算数平均值,即目标副本自身的CPU利用率的平均值。一个HPA定义的具体例子如下:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
    name: php-apache
    namespace: default
spec: 
    maxReplicas: 10
    minReplicas:1
    scaleTargetRef:
        kind: Deplotment
    name: php-apache
    targetCPUUtilizationPercentage: 90

  和上面等价的命令行行为:

kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10

8. StatefulSet

  k8s系统中,Pod的管理对象RC、Deployment、DeamonSet和Job都面向无状态的服务。但是有时候是很多的服务是有状态的,特别是一些有着以下特点的集群:

  • 每个节点都有都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并进行通信。
  • 集群的规模是比较固定的,集群规模不能随意变动。
  • 集群中的每个环节都是有状态的,通常会持久化到永久存储中。
  • 如果磁盘损坏,则集群中的某个节点无法正常运行,集群功能受损。
      为了解决服务有状态的问题,k8s引入了StatefulSet,可以将这个对象看成是RC/Deployment的一个变种,它有以下的特性:
  • StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他的成员。假设StatefulSet的名称为kafka,那么第一个Pod叫kafka-0,第2个叫做kafka-1,以此类推。
  • Stateful控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
  • StatefulSet里的pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除 Pod时默认不会删除于StatefulSet相关的存储卷。
      StatefulSet除了要于PV卷捆绑使用以存储Pod的状态数据,还要于Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于那个Headless Service。

9. Service

9.1 概述

  Service服务是k8s的核心资源对象之一,k8s里的每个Service其实就是我们经常提起的微服务架构中的一个微服务,之前讲解Pod、RC等资源对象其实都是为讲解k8s Service做铺垫的。K8s的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service和其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务数量始终符合预期标准。K8s的服务之间通过TCP/IP进行通信。k8s发明了一种很巧妙的服务发现和负载均衡机制。Service不是公用一个负载均衡的IP地址,每个Service都被分配了一个全局唯一的的虚拟IP地址,这个虚拟IP被称为Cluster IP地址。服务调用就变成了最基础的TCP网络通信问题。下面是一个tomcat-service的yaml文件:

apiVersion: v1
kind: Service
metadata:
    name: tomcat-service
spec:
    ports: 
    -port: 8000
    selector:
        tier: frontend

  运行下面的内容进行创建:

kubectl create -f tomcat-server.yaml

  运行下面的命令分查看端口和Cluster IP:

kubectl get endpoints
kubectl get svc tomcat-service -o yaml

  在很多服务中,都存在多端口问题,通常一个端口提供业务服务,另一个端口提供管理功能。k8s支持多个Endpoint,但是要求每一个Endpoint都定义一个名称来区分。下面的 例子是Tomcat多端口service的定义示例:

apVersion: v1
kind: Service
metadata: 
    name: tomcat-service
spec:
    ports:
    - port: 8080
    name: service-port
    - port: 8005
    name: shutdown-port
    selector:
        tier: frontend

9.2 k8s的服务发现机制

 &emps;任何分布式系统都会涉及“服务发现”这个基础问题,大部分分布式系统都通过提供特定的API接口来实现服务发现功能,但是这样会导致平台的侵入性比较强,也增加了开发、测试的难度。k8s则采用了直观朴素的思路去解决这个棘手的问题。
  首先,每个k8s中的Service都有唯一的Cluster IP及唯一的名称,而名称是由开发者自己定义的,部署时也没有必要改变,所以完全可以固定到配置中,接下来的问题就是如何通过Service的名称找到对应的Cluster IP。
  k8s通过Add-on增值包引入了DNS系统,将服务名作为域名,这样程序就可以直接使用服务名来建立通信了。后续将会介绍如何部署DNS系统。

9.3 外部系统访问Service的问题

  k8s中有三种IP,弄清他们的区别对于 理解k8s很有必要。

  • Node IP:Node的IP地址
  • Pod IP:Pod的IP地址
  • Cluster IP:Service的IP地址
      Node IP是真实在的IP地址。集群外的节点访问k8s集群中的某个节点或者是服务时,都必须通过Node IP通信。Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络,前面说过,k8s要求位于不同Node上的Pod能够彼此直接通信你,所以Pod都能够彼此直接通信,所以k8s里一个Pod容器访问另一个Pod里的容器时,就是通过Pod IP所在 的虚拟二层网络进行通信的,而真实的流量是通过Node IP所在的物理网卡流出的。cluster IP更像是一个“伪造”的IP网络,因为:
  • Cluster IP仅仅作用于k8s Service这个对象,并由k8s管理和分配的IP地址,来源于Cluster IP池。
  • Cluster IP无法被ping,因为没有一个实体网络来相应。
  • Cluster IP只能结合Service Port组成一个具体的通信端口,单独的IP不具有TCP/IP通信的基础,并且他们属于k8s集群中这样一个封闭的空间,集群外的节点如果要访问这个通信地址,则需要一些额外的工作。
  • 在k8s集群内,Node IP网络、Pod IP网络和Cluster IP网之间的通信,采用的是k8s自己设计的一种编程方式的特殊的路由规则,和我们熟悉的IP路由有很大的不同。

10. Job

  批处理任务通常并行启动多个计算机进程去处理一批工作项,在处理完成后,整个批处理任务结束。从1.2开始,k8s支持批处理类型的应用,可以通过Job这种资源对象定义并启动一个批处理任务的Job。Job控制一组Pod容器,可以将Job看作是一个特殊的Pod副本控制器,同时Job控制Pod副本和RC等控制器的工作机制有以下的区别:

  • Job所控制的Pod副本是短暂运行的,可以将其视为一组Docker容器,起哄的每个Docker容器都仅仅运行一次,当Job控制的所有Pod的副本都运行结束时,对应的Job也就结来。Job生成的副本是不能自动重启的,对应的Pod副本的RestartPolicy都被设置为Never。
  • Job所控制的Pod副本的工作模式能够多实例并行计算。

11. Volume

  Volume是Pod中能够被多个容器访问的共享目录。k8s的Volume概念、用途和目的和Docker的Volume比较类似,但是两者不能等价。首先,k8s的volume被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下;其次,k8s的Volume和Pod的生命周期相同,但是与容器的生命周期不相关,当容器终止或者重启时,Volume中的数据并不会丢失,最后,k8s支持多种类型的Volume。使用Volume,必须要现在Pod声明一个Volume,然后在容器中引用该Volume并挂载(Mount)到容器里的某个目录上。定义一个Volume如下:

# Pod的部分内容
template: 
    metadata:
        labels:
            app: app-demo
            tier: frontend
spec:
    volumes:
        - name: detavol
        emptydir: {}
    containers:
    - name: tomcat-demo
    image: tomcat
    volumeMounts:
      - mountPath: /mydata-data
        name: datavol
    imagePullpolicy: IfNotPresent

  除了可以让Pod里的多个容器共享文件、让容器的数据可以写到宿主机的磁盘上或者写文件到网络存储中,k8s的Volume还扩展了一种非常有使用价值的功能,就是容器配置文件的集中化定义与 管理,这是通过ConfigMap这种新的资源对象来实现的。k8s实现了丰富的Volume类型的,如下:

  • emptydir:一个emptydir Volume实在Pod分配到Node时创建的。它的初始内容为空,并且无需指定宿主机上对应的目录文件,因为这是k8s自动分配的一个目录,当pod从Node上移除时,它里面的数据会被永久删除,emptydir有下面的一些用途。
      1.临时空间,例如对于某些应用程序运行时所需的临时目录,且无需永久保留
      2.长时间任务中的中间过程CheckPoint的临时保存目录
      3.一个容器需要从另一个容器中获取数据的目录(多容器数据共享)
  • hostPath:hostPath是在Pod容器上挂载宿主机上的文件或者目录,它通常可以用于以下几个方面
      1.容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的高速文件系统进行存储。
      2.需要访问宿主机上Docker引擎内部数据结构的容器应用时,可以通过定义hostsPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。
      在使用这种类型的Volume时,需要注意以下几点:
      1.在不同的Node上具有相同配置的Pod,可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致,
      2.如果使用了资源配额管理,则k8s无法将hostPath在宿主机上使用的资源纳入管理。
      定义一个hostPath类型的Volume的文件如下:
volumes:
    -name: "Persistent-storage"
    hostPath:
        path: "/data"
  • gcePersistentDisk:使用这种Volume表示使用谷歌公有云提供的永久磁盘。此处不做过多解释。
  • awsElasticBlockStore:使用亚马逊公有云提供的EBS Volume存储数据,此处不做过多解释。
  • iscsi:使用iSCSI存储设备上的目录挂载到Pod中
    -gitRepo:通过挂载一个空目录,并从Git库中clone一个git repository以供Pod使用。

12. Persistent Volume

  网络存储是相对独立于计算资源而存在的一种实体资源。比如在使用虚拟机的情况下,我们通常会定义一个网络存储,然后从中划出一个“网盘”并挂载到虚拟机上。Persistent Volume(PV)和与之相关联的Persistent Volume chaim(PVC)也起到了类似的作用。PV可以理解成是k8s集群中的某个网络存储对应的一块存储,它与Volume类似,但是有以下区别:
  1.PV只能是网络存储,不属于任何Node,但是可以被任何Node访问
  2.PV并不是被定义 在Pod上的,而是独立于Pod之外定义的
  下面给出一个NFS类型的PV的yaml定义问价,声明需要 5G的空间:

apiVersion: v1
kind: PersistentVolume
metadata:
    name: pv0003
spec:
    capacity:
        storage: 5Gi
    accessModes:
    nfs:
        path: /somepath
        server: 172.17.0.2
  如果某个Pod想要申请某种类型的PV,则首先需要定义一个Persistent VolumeClaim对象:
```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata: 
    name: myclaim
spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        stoorage: 8Gi

  然后在Pod的Volume定义中引用上述PVC即可

volume:
    - name: mypod
       persistent:VolumeVlaim: 
         claimName: myclaim

  最后说下PV的状态。PV是有状态的对象,它的状态有以下集中
  1.Available:空闲状态
  2.Bound:已经绑定到某个PVC上
  3.Released:对应的PVC已经被删除,但是资源还没有被集群回收
  4.Failed:PV自动回收失败

13. Namespace

  Namespace是k8s系统中另一个非常重要的概念,Namespace在很多情况下用于实现用户的资源隔离。Namespace通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享整个集群资源的同时还能被分别管理。Namespace的定义很简单:

apiVersion: v1
kind: Namespace
metadata:
    name: development

  一旦创建了Namespace,在创建资源的时候,就可以指定资源对象属于哪个Namespace

apiVersion: v1
kind: Pod
metadata:
    name: busybox
    namespace: development
spec:
     containers:
        - image: busybox
         command:
          - sleep
          - "3600"

14. ConfigMap

  为了解决Docker容器运行时修改配置文件的问题,k8s提供了如下巧妙的实现:
  首先,把所有的配置项当作是key-value字符串。value可以来自某个文本文件。配置参数可以作为Map表中的一个项,整个Map的数据可以被持久化存储在etcd数据库中,然后提供API以方便k8s相关组件或者客户应用CRUD操作这些数据,上述专门用来保存配置参数的Map就是K8s的ConfigMap对象。然后k8s提供一种内建机制,将存储在etcd中的ConfigMap通过Volume映射的方式变成目标Pod内的配置文件,不管目标Pod被调度到哪个服务器上,都会自动映射。

以上是k8s中资源的一个概述,后续将会继续根据资源,逐渐进行深入研究。你 ,学废了吗?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容