容器网络笔记(二)

二、Kubernetes和其网络实现

2.1 kubernetes 基础

        单主机容器不能满足性能、高可靠、高可用的需求:

        资源调度和分配难度较大

        性能受主机资源的限制

        不支持高可用

        Docker诞生之初,只是在一个主机上去部署容器,而现在面临分布式、多主机、高可用的场景架构需求,出现了容器的编排工具:

           Docker的 Docker Swarm

            Apache Mesos

            Google kubernetes

        通过容器编排工具,可以在集群中统一管理容器,提供基于容器的大型的,弹性的,分布式应用服务。

(1) K8s 架构

    类似于任何云计算,K8S也有自己的管理平面,两类节点: master node 与 worker node

(2) Master Node

 由四部分组成

        etcd: 可持久化,高可用的K-V数据库,用来存放K8S数据。本身是一个独立的项目,一个可以持久化的数据库。

        kube-apiserver:整个K8S集群的管理入口,向上提供REST API接口,用户可以使用这个接口,管理整个集群,可水平扩展,如果想提升性能,扩展多个APIServer实例就可以。常用的cubectl命令也是将请求转换成REST发送给kube-apiserver。

        kube-scheduler: 为新创建的容器选择Worder Node(所有应用都应用在WorkerNode上), 用来做部署。选WorkerNode。

       kube-controller-manager: K8S资源管理,每个资源都可以对应一个controller,由controller检测和管理的。每个controlle是独立的进程,这样每个controller才不会相互影响。K8S是由go语言开发的,它的高并发性特点,使的它可以同时调用CPU的多个核,controller是可以并发的,所以多个controller是由kube-controller-manager来统一运行协同工作的。为K8S资源运行controller用来检测并管理这些资源,确保K8S管理的容器运行在正常运行状态。

(3) K8S Worker Node

        kubelet:管理Worker Node 上的容器,从MasterNode获取相关信息,来启动容器,并确保运行状态

        kube-proxy:配置Worker Node上的容器网络

        Container Runtime : 实际运行容器的程序,支持多种容器,K8S支持多种Container Runtime,其中包括Docker, rkt, runc等等。一般用Docker。

(4) kubectl

         K8S的命令行工具,配置容器,可以创建、更新、删除容器。

         常见K8S流程:

        *通过kubectl 下发创建容器的命令

        *位于Master Node 的kube-apiserver收到请求(rest api)

        *调用kube-scheduler。位于Master Node的kube-scheduler选择一个或多个Worker Node 用来部署容器(根据当前资源情况或者是策略)

        *kube-scheduler 将选择的Worker Node 通知给kube-apiserver, kube-apiserver 再转发给相应Worker Node上的kubelet

        *位于Worker Node的kubelet进程,收到来自Master Node 的请求(kube-apiserver) ,调用自身的container runtime, 例如Docker, 创建容器

        *创建完容器后,位于Master Node的kube-controller-manager 不停的检测容器的状态,并作出调整:如果发现某个Worker Node下线了,kube-controller-manager会从其他的Worker Node中重新启动容器

2.2 K8S Pod 网络介绍*

         Pod 是K8S的最小单元,包括一个或多个位于同一主机的容器,以及它们共享的存储,网络与运行信息

        对于Docker来说,Container,或者容器,是应用程序运行的逻辑主机。

        一般来说一个进程,运行在一个Container里面,对于K8S来说呢,Pod是应用程序运行的逻辑主机,一般的,一组进程运行在一个Pod里面。

        为什么要以Pod为管理单元呢,这是与现在的应用程序有关,现在的应用程序并不是由一个进程组成,而是有可能由多个进程组成,协同工作,一起构成一个应用。但是为了进程间高效协同,所有要放在同一主机上,IP可达,这样增加了管理复杂度,如何解决:

        用不同的Docker Container 运行不同的进程,但是通过一个管理平台来确保这些Container运行在一个主机上,并通过这个管理平台使这些Container共享资源(网络协议栈,这样的话Container之间访问就不用通过IP地址,而是通过类似127.0.0.1这样的localhost IP来实现),这就是K8S的方法。

           *更好检测

           *解耦软件依赖

            *模块化通用程序

(1)pod 内所有的容器公用一个网络空间,公用一个虚拟网卡,公用一个IP地址;网路共享,其他隔离

(2)pod内的容器可以通过localhost访问彼此

(3)pod 这样的网络模型增加了管理难度,为了便于管理网络,每个pod启用一个专门的pause容器来管理网络,因为是一个资源占用的容器,所以不启动,始终是pause状态,不占用系统资源,pause容器是pod的核心,它为pod内的容器提供了虚拟的网络环境。

2.3 Kubenet

      K8S支持的network-plugin 

            * None  , k8s 放弃了对网路的管理

            *cni  ,支持定制的网络插件, 例如flannel , Calico

            *kubenet,  kubernetes 原生的网络实现方案

        (1)kubenet 是kubelete 进程中的一部分    

                创建Linuxbridge cbr0

                 通过kubelet 的参数来分配pod的IP地址

         (2)kubenet 在pod 内与pod 间通信

            同在同一个主机里docker 中不同container通信一样:通过linux bridge 来做转发

           (3) 跨Worker Node 间的Pod间通信

            通过路由器和交换机


2.4 kubernetes 安装

在已经安装好Docker的基础上,安装Kubernetes

(1)更新get ,以及可以通过https进行传输,添加源是阿里云apt-key

sudo apt-get update && sudo apt-get install -y apt-transport-https curl

(2) 添加Kubernetes源

sudo bash -c  'cat<<EOF > /etc/apt/sources.list.d/kubernetes.list

(3)更新包信息

(4)安装kubernetes 核心组件

apt-get install -y kubelet kubeadm kubectl kubernetes-cni

        kubelet:kubelet是Kubernets最重要的组件,它要运行在集群上所有的机器,用来执行类似于开启pods和容器。

        kubectl:在集群运行时,控制容器的部件。只需要将其运行在主节点上,但是它将对所有节点都有效。

2.5 创建第一个Pod

2.6 YAML简介

      是一种语言格式,用来描述系统配置以及相应资源,与JSON完全兼容,是可以转换为json文件的,由Map和List两类资源组成。

    (1) YAML Map

以上示例是由YAML定义的MAP,它由两个键值对,如第一个的name是apiVersion,value是v1;第二个name是kind,value是pod。

"---"是MAP之间的分隔符,是用来区分不同MAP的。这样可以在一个YAML文件里来定义多个MAP。

第三个键值对的name是matedate,value是另外一个map,另一个键值对:name是 name, value是test-pod。

(2) YAML List

如上,name是args,value是“sleep”,"1000","message","Bring back Firefly!" 共同构成list。

2.7 常见调试命令

(1) kubectl describe <resource type> <resource name>

        显示资源的详细信息

(2)kubectl get events

         显示Kubenetes系统中最近的一些事件。查看K8S运行状态,是否正常。

(3) kubectl get <resource type> <resource name> -o yaml 

           以YAML格式来详细的输出资源信息。

(4) kubectl logs -f <pod name> <container name>

            持续输出容器中主要进程的log, 类似于docker logs。

(5)kubectl exec -it <pod name> --container <container name> -- <command>

            在容器中执行命令,类似于docker exec

(6)kubectl run -it --rm --restart=Never busybox --image=busybox sh

            启动一个Pod, 并直接登录到pod中,常用来调试网络。

node 与 pod 的关系

(7)kubectl attach <pod name> -c <container name> - it

            加入到一个现有的容器console中。


2.8Deployment 与 DaemonSet

2.8.1 Deployment

回顾一下Docker与K8S的最小管理单元,Docker里的最小管理单元是Container容器,Docker的命令是直接控制到容器。

在K8S里Pod是原子单元,一个Pod可以包括多个进程,代表一个应用单元。

K8S也支持直接去管理Pod,但是K8S并不建议用户直接去管理Pod, Pod虽然可以确保所有的容器是运行在WorkerNode上,但是当我们在部署一个弹性的,可扩展的应用时,需要同时部署多个相同的Pod,这些Pod最好在不同的WorkerNode上,并且在不同的时间,Pod的数量可以去动态的增减,如在业务高峰期Pod可以动态增加,业务淡季可以删除。所以需要多Pod进行管理,如果容器上的软件进行升级,还需要对不同Pod上的容器进行更新。

这些管理工作,可以交由用户去做,用户可以制作脚本,或是开发中间层,去控制。

但是K8S的设计哲学是,假定用户不是专家,K8S不向用户去暴露这些复杂的,易出错的操作。

因此,K8S提供了Deployment对象,推荐用户使用Deployment 去管理Pod, Deployment在Pod基础之上,利用了ReplicaSet管理Pod。

之前,创建Pod是直接创建,然后K8S在整个集群的WorkerNode里,去选择一个WorkerNode来创建Pod。现在,创建Pod,是先创建一个Deployment,在Deployment里定义ReplicaSet,创建一组相同的Pod,K8S从WorkerNode里走相同数量的WorkerNode来部署这组Pod。

2.8.2 DaemenSet

DaemenSet 是用来管理Pod的复制。

DaemenSet类似于Deployment, 是用来管理Pod的,DaemenSet与Deployment的区别是:Deployment是用户来制定要创建多少个Pod,DaemenSet它是用来确保每个或是部分的WorkerNode来运行同一个Pod,它们都是用来管理同一个Pod的多个复制,DaemenSet确保每一个WorkerNode都有Pod在运行,如果用户向K8S增加一个Node, DaemenSet会增加一个指定的Pod,如果用户向K8S删除Node, 则相应的Pod也被删除。

Deployment是用来管理无状态的Pod,如Web Server,这些Pod可以动态增减。

DaemonSet用来确保每个Node都运行一个Pod, 并且这个Pod会在其他正常Pod前启动。常用来部署管理程序。

如果我们现在要在K8S上来部署一套OpenStack,我们可以把neturon, nova进程等放在DaemonSet上,这样每增加一个新节点时,K8S会为这些新节点的OpenStack管理进程给拉起来。

2.9创建一个Deployment

2.10 Service

1.为什么需要Service

    Pod 是K8S管理的原子单元,Pod的不稳定情况,尤其是网络的不稳定情况如何解决。

     (1)如当一个Worknode 下线后,其内部的Pod,就会被调度到其他的Workernode

     (2)当修改Deployment的ReplicaSets时,Pod会被创建或删除

      Pod的删除或创建会影响其IP地址的变更,这样会对应用带来影响。

      因此,需要一种机制来屏蔽IP地址的变化。通用的解决方法:反向代理,或LoadBalancer,来屏蔽IP地址的变化。

2. LoadBalancer

   (1)LB的IP地址是固定的

   (2) 当应用程序的实例变化了,Pod重建了导致IP地址变化,LB维护更新后的IP列表就可以

   (3)LB收到请求之后转发到后端应用程序的实例上。

3. Service 

    Service 是LB的一种实现。

(1)对应与LB提供服务的IP地址是Service 的Cluster IP

(2)Service 后端管理的是一系列的Pod

(3)Service 如何知道哪些Pod是它的后端(成员)?通过Pod里的label selector来找到对应的Pod

4. Service V.S. Deployment

Deployment侧重的是部署。Deployment指示Etcd中的数据,在Worknode上没有实体,Worknode上只有Pod。Deployment只是在Masternode的Etcd中的数据。

Service是对一组Pod提供应用的逻辑抽象,提供访问入口以及访问策略。Service 需要多Worknode做相应的配置和修改。Service是在Worknode上的配置

5. LabelSelector

(1)Label 

Label是挂载在K8S实体--Pod 上的键值对,用来标识对象。K8S可以借助Label来进行有选择的Pod上应用部署

(2)LabelSelector

LabelSelector是K8S提供的用来查询带有特定Label的的API

对于Service 和 Deployment,我们都需要定义LabelSelector。

6.Service 类型

Service是LB的一种实现。Service为一组Pod提供了访问入口。Service分为四类。

(1)Cluster IP 

是默认的Service类型, Service带一个私网IP地址,K8S集群内的其他Pod通过这个私网IP地址来访问Service逻辑抽象出来的一组Pod。

(2)NodePort

在Service对应的Pod所在的所有Worknode上,为一组Pod提供访问入口。因为Worknode的IP地址不再是私网地址,因此这组Pod现在可以被K8S集群以外的客户端访问。

(3)LoadBalancer

创建一个外部LoadBalancer, 并且给这个LB分配一个固定的外网地址。通过这个外网地址为一组Pod提供访问入口。

(4)ExternalName

通过DNS提供服务。

2.11创建一个Service

创建一个类型未Cluster IP 的Service

Cluster IP 的Service为K8S集群内的Pod访问Service提供网络连接,Pod的地址是私网地址,是可以通过指定Kubenet的参数,或者是kube-control-manager的参数来分配一个CIDR给Pod。Cluster IP 同样也是一个私网地址,为K8S集群内的Pod访问Service提供连接,这个地址是通过KubeApi的参数来制订的。

通过yaml文件来创建Service:

通过Yaml来创建

创建Service时默认的类型是“Cluster IP”

访问测试:

wget  -o - X.X.X.X

2.12 Cluster IP类型Service

Pod 在任何模式下,都有一个netdev设备(网卡设备)与之对应。Service对应的IP是Cluster IP,但是没有一个设备与之对应。在Pod里或是在WorkNode里也没有关于Service的明细路由。K8S如何为Service提供网络?

Cluster IP 类型的Service是为K8S里面的Pod提供LB服务。

Pod是通过veth对连接在Linux Bridge上的。

在一个K8S集群里Pod的网络是项目联通的,不管Pod是否在同一个WorkNode上。

Kube-proxy是运行在WorkNode上的一个进程,它在Service里的作用就是拦截发往Service的Cluster IP的数据,并修改目的IP地址,它有三种实现:

(1)基于用户空间转发(目前基本不用,性能差)

(2)基于内核的netfilter(iptales),是linux内核的一个LB模块,就是在Linux内核来实现Service,K8S1.9后支持,是Linux内核中基于规则即match-action的包处理引擎,它类似于Openflow,netfilter是一个内核模块,它对应的配置工具是iptables,(是目前主流方案)在iptables中有一个nat表,具体包处理流程:

DNAT处理类型:    随机;    基于源IP。

(3)基于ipvs

2.13 Cluster IP iptables分析


2.14 Endpoint

Endpoint是与Service密切相关的概念,Service与真正提供服务的Pod是两个独立的对象,这个在创建Service时的YAML文件里的Service与Deployment是两个独立的YAML map 就已经说明。

Service是通过label fuleter来找到对应的Pod,但是Pod是不稳定的,它随时会被删除、修改、或者增加新的Pod,所以为了使确保Service能够快速、准确的送到有效的Pod,在K8S里会在Kubecontroller manager针对Service有一个叫Endpoint controller的对象,它负责:

(1)监听Service和对应Pod的变化;

(2)监听到Service被删除,会同步删除与该Service同名的endpoint对象;

(3)监听到新的Service创建,会根据新建Service信息获取Pod列表,创建对应的endpoint对象;

(4)监听到Service被更新,会根据更新后的Service信息获取Pod列表,更新对应的endpoint对象;

(5)监听Pod事件(创建、删除),会更新对应的Service的endpoint对象,将pod IP记录到endpoint中。

所以对于每个Worknode,监听的不是Service对应Pod的变化,而是监听endpoint的变化,就可以知道对应Pod的信息,

2.15 基于Client IP 的 Service

2.16 向外网发布ClusterIP类型的Service

方法一:添加路由,将类型为Cluster IP服务的目的地址 IP加载在物理路由器上,指向对应的WorkNode,这样外部可以访问WorkNode, 在WorkNode上的kube-proxy会用iptalbes来修改目的IP和目的端口:将目的IP由Service 的IP地址更换为Pod的IP地址,将端口也转换为Server Pod所服务的端口。

实际做的时候,可以在路由器上配置多条等价路由对应多个WorkNode来提高冗余度。

一个Service只用一个Cluster IP地址。

问题:添加路由的方法扩展性不好,在大规模集群中无法实现。

方法二:External IP,为类型为Cluster IP类型的Service指定一个External IP; K8S 增加iptalbes规则匹配External IP 和 Service对应的端口号。匹配之后,还是走到K8S Service对应的iptalbes子链。


2.17 NodePort类型的Service

NodePort类型色Service是可以在WorkNode 的IP地址上直接暴露Service。

当创建一个NodePort类型的Service时,K8S会为此Service分配一个30000-32767之间的端口号。

在每个WorkerNode 上,都可以通过<IP>:<端口号>来访问Service。它也是通过Kubeproxy,使用iptables来实现。

比较特殊的是NodePort会做一个Source IP的变换。

NodePort向外发布服务与Cluster IP向外发布不同,不用在Pod之间的路由器上添加路由,因为它是直接在WorkNode上的Pod上暴露出来的,其次它可以在多个WorkNode上来暴露同一Service。

但是,也存在一些问题:

(1)端口的有限,面临扩展性问题。

(2)虽然在WorkNode上可以有多个Pod用NodePort对外暴露服务,但是当Client来访问时,必须制定一个WorkNode,也就是只能通过某一个WorkNode来访问Service,当WorkNode出现故障后,需要用户自己切换到另外一个WorkNode上来访问Service,虽然NodePort 类型的服务有多个入口,但是需要用户自己去做切换。

2.18 LoadBalancer类型Service 和Ingress

K8S中第三中类型的Service--LoadBalancer 类型Service

(1)LoadBalancer

在NodePort类型的Service中,只能从一个Worknode来访问Service,故障需要人为切换,因此不太适合于生产环境。

这样,提供服务的IP(也就是Pod的IP)会发生变化,解决办法:就是在Pod上再添加Service。

NodePort对外服务的IP是WorkNode的IP,但是这个IP地址也不确定的,是可能变化的,解决方案:在WorkNode上再添加LB实例,屏蔽具体的IP地址变化,此处的LB提供了一个公网的IP地址供用户访问,同时又有多个WorkNode作为后端,当用户访问公网IP时,LB将请求发往LB之后的多个WorkNode,再在WorkNode上再通过IPtables规则,再做一次负载分担,将用户的请求发送到相应的Pod上。所以这样的话,就形成了两层LB:一层是实际的面向用户的LB,它在Worker Node之上做了一次封装;第二层是一个基于Iptables的一个分布式LB。

K8S思想是向用户屏蔽复杂操作,创建LB和公网IP地址等提供以上服务的Service就叫LoadBalancer。

(2)Ingress

是独立的K8S资源,完成http和https的路由。是一个七层的路由。

可以通过一个IP地址对应到多个域名。

通过不同的path分发到不同的service。

需要一个单独的Ingress controller来实现Ingress。(不同与Endpoint Controller 和Deploment Controller这些Controller都是作为Kubecontroller的一部分运行在一个进程里面),但是Ingress controller并不是Kubecontroller里的一部分,需要一个单独的Ingress Controller来实现。Ingress Controller是一个第三方提供的,用来实现七层LB的Controller。Ingress 不是一种Service类型,只是对于LB Service的一种补充,并且也是与LB一样,也是需要有其他云平台配合才能实现。

通常IP地址资源是昂贵的,通过Ingress一种七层的负载均衡可以在一个IP地址基础之上,根据不同的hostname,不同的url,对应不同的后端Service,这样一个IP地址就可以对应多个后端的K8S Service。

2.19 K8S DNS

(1)DNS 基础

  A:  域名与IPv4地址的对应

  AAAA: 域名与IPv6地址的对应

  PTR: 域名与IP地址的对应,区别是A或者AAAA是通过域名获得IP地址,RTP是通过IP地址来获得域名

   CNAME: 记录一个域名与另一个域名的对应关系

   NS: 为当前Zone指定一个DNS server

   SOA: 包含Zone的一些信息,例如首要的DNS server,  管理员邮箱地址, 更新时间等

(2)K8S DNS

K8S 提供一个内部的DNS Server, 所有带有Cluster IP的Service, 都会在这个内部DNS Server中添加一条ClusterIP到Service name对应的A类记录。

在Pod内部, K8S会像Docker一样修改/etc/resolv.conf文件,将DNS server指向K8S内部的DNS Server。

所以,在K8S集群内,可以通过Service name 直接访问Service 。

ClusterIP、NodePort、LoadBalancer 三种Service 都有自己的ClusterIP, 因此都可以通过Service name 直接访问。

(3)K8S DNS的具体实现

早期是由kubeadmin来实现的,但是1.12版本后,kubedns就替代了kubeadmin,成为默认的DNS实现。

kubedns在K8S里是通过一个Pod来实现的,这个Pod内有三个容器:

        kubedns:监听K8S的API,监听有关Service与Endpoint的变化;监听到变化后会去更新dnsmasq的配置。

        dnsmasq:是用来提供DNS Server的功能,是一个轻量级DNS Server的实现。

        execheathz: 为以上两个容器提供健康检查。

       1.12版本之后默认的DNS是CoreNDS , 是CNCF管理的一个项目,在一个容器中实现K8S资源的监测和DNS服务的提供。可以在一个容器内实现DNS的监测和DNS的服务。

    K8S是通过一个多Pod的Deployment来管理这些容器,这样可以确保总是有确定数量的CoreNDSPod在运行。

     K8S还为多个Pod提供一个ClusterIP类型的Service。这样DNS请求被负载分担到多个Pod上。

     DNS的数量可以通过REPLICA的属性来定义。

2.20 ExternalName 类型Service

第四中类型的Service。不依赖之间的三种Service。提供域名映射的方法。

在服务中,不光IP地址可能会发生变化,域名也会发生变化。通常域名是写在应用程序里的,可维护性弱,如果没有其他措施,域名的修改就要启动修改应用程序,并且重启应用,这样代价较大。如果有一种机制可以屏蔽域名的变化。固定域名-->跳转域名。就是将Service name 与Service里服务于外部的一个域名做一个对应。如果我们要访问这个Service 的话,K8S DNS会返回一个CNAME记录,将Service name转到另一个域名。

ExternalName Service的Yaml文件:

2.21 HostPort 与 HostNetwork

(1)HostPort

HostPort类似与DockerPulish Port的功能:将Docker的一个容器的Port映射到主机的一个Port上。HostPort是将K8S的一个Pod上提供服务的Port映射到Pod所在的WorkNode上的一个Port上。

实现方式:Kubenet 通过iptables配置新增子链(chain)

(2)HostNetwork

K8S的HostNetwork与Docker的HostNetwork类似,它将K8S里的Pod直接连接到WorkNode的主机网络空间,就是将Pod与WorkNode的主机网络连通,此时与Docker类似,Pod也没有自己的Network namespaces,因它使用的就是主机的Network namespaces,实际上是放弃了K8S的网络管理,直接将K8S里的容器连接在了主机的网络空间里。

HostPort和HostNetwork都有一个问题:Pod本身是不稳定的,可能迁移到其他的WorkNode,IP地址会发生变化。还有就是Pod服务所映射的端口与主机端口可能发生冲突,需要人为避免冲突。

==================================================================

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

推荐阅读更多精彩内容