kubernetes运维

1.kubernetes介绍

Kubernetes容器集群管理系统,是Google开源的一个项目,目标是管理跨多个主机的容器,用于自动部署、扩展和管理容器化的应用程序,主要实现语言为Go 语言。

1.1特点:

可移植性、可扩展性、存储编排、自动化部署回滚、自我修复、服务发现负载均衡、机密管理配置管理

2.Kubernetes架构

2.1Kubernetes Master:

Master 节点是Kubernetes 集群的控制节点,负责整个集群的管理和控制。

Master 节点上包含以下组件:

1.kube-apiserver:提供了资源操作的唯一入口并提供认证、授权、访问控制、API 注册和发现等机制
2.kube-scheduler:负责资源的调度
3.kube-controller-manager:负责维护集群的状态比如故障检测、自动扩展、滚动更新等
4.etcd(键值数据库):保存了整个集群的状态,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)

2.2Kubernetes Node:

Node 节点是Kubernetes 集群中的工作节点,Node 上的工作负载由Master 节点分配,工作负载主要是运行容器应用。

Node 节点上包含以下组件:

1.kubelet:负责维持容器的生命周期,同时也负责Volume(CVI,Container Volume Interface)和网络(CNI,Container Network Interface)的管理
2.kube-proxy:负责为Service 提供cluster 内部的服务发现和负载均衡
3.Pod 网络(贯穿整个集群):Pod 要能够相互通信,Kubernetes Cluster 必须
部署Pod 网络Flannel 是其中一个可选方案,还有OpenvSwitch、Calico等

3.Kubernetes关键概念

1.namespace

1.Namespace用于实现多租户的资源隔离,通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
2.每个命名空间内的资源名称都是唯一的。
3.同一个命名空间内的服务可以通过DNS 使用服务名称互访,跨命名空间的服务,可以使用servicename.namespace 的方式进行访问。

查看命令空间:kubectl get namespaces

创建命令空间:kubectl create ns test

1.default --创建资源时如果不指定,将被放到这个Namespace 中
2.kube-system --Kubernetes 自己创建的系统资源将放到这个Namespace 中
3.kube-public --Kubernetes 公共的系统资源将放到这个Namespace 中

2.pod

1.Pod 是Kubernetes 应用程序的基本执行单元(最小资源单位),是一组容器的集合。
2.Pod 内部的容器共享网络栈
3.Pod 内部的容器可以共享存储卷

注释:

1.Pause Container:启动每个Pod都有一个特殊的被称为“根容器”的Pause容器,以它的状态代表整个容器组的状态。Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题。

2.Init Container:在业务容器启动之前,会首先一次性的执行Init Container 的指令,这是一种专用的容器,在应用程序容器启动之前运行,用来包含一些应用镜像中不存在的实用工具或安装脚本。

3.业务容器:自定义容器,用于运行真正的业务代码

2.1在Kubernetes集群中Pod有如下两种使用方式:

1.一个Pod中运行一个容器。“每个Pod中一个容器”的模式是最常见的用法;在这种使用方式中,可以把Pod想象成是单个容器的封装,kuberentes管理的是Pod而不是直接管理容器。

  1. 在一个Pod中同时运行多个容器。一个Pod中也可以同时封装几个需要紧密耦合互相协作的容器,它们之间共享资源。

2.2Pod中可以共享两种资源:网络和存储

2.2.1.网络

每个Pod都会被分配一个唯一的IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。

2.2.2.存储

可以为一个Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。

3.Controller

Kubernetes 通常不会直接创建Pod,而是通过Controller 来管理Pod 的。

1.Controller 中定义了Pod 的部署特性,可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力、在什么样的Node 上运行Pod等。

2.为了满足不同的业务场景,Kubernetes 提供了多种Controller,包括Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等。

3.1 Deployment

是最常用的Controller,定义Deployment创建Pod和ReplicaSet,可以管理Pod 的多个副本,并确保Pod 按照期望的状态运行。典型的应用场景包括:
➢滚动升级和回滚应用
➢扩容和缩容
➢暂停和继续Deployment

3.2 ReplicaSet

实现了Pod 的多副本管理。使用Deployment 时会自动创建ReplicaSet,也就是说Deployment是通过ReplicaSet 来管理Pod 的多个副本,我们通常不需要直接使用ReplicaSet。

3.3 StatefulSet

StatefulSet 作为Controller 为Pod 提供唯一的标识。解决有状态服务的Pod 的每个副本在整个生命周期中名称是不变,同时保证副本按照固定的顺序启动、更新或者删除(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:

➢稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现

➢稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现

➢有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于initcontainers来实现

➢有序收缩,有序删除(即从N-1到0)

3.4 daemonset

DaemonSet 确保全部(或者一些)Node 上运行一个Pod 的副本。当有Node 加入集群时,也会为他们新增一个Pod 。当有Node 从集群移除时,这些Pod 也会被回收。删除DaemonSet 将会删除它创建的所有Pod。
使用DaemonSet 的一些典型用法:

➢运行集群存储daemon,例如在每个Node 上运行glusterd、ceph。
➢在每个Node 上运行日志收集daemon,例如fluentd、logstash。
➢在每个Node 上运行监控daemon,例如Prometheus Node Exporter、
collectd、Datadog 代理、New Relic 代理,或Ganglia gmond。

3.5 Job

用于运行一次性任务,运行结束就删除的应用。
保证批处理任务的一个或多个Pod成功结束,而其他类型的Controller 中Pod 通常是长期持续运行。

3.6 CronJob

1.CronJob其实就是在Job的基础上加上了时间调度
2.在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。

这个实际上和Linux中的crontab就非常类似。一个CronJob对象其实就对应中crontab文件中的一行,它根据配置的时间格式周期性地运行一个Job,格式和crontab也是一样的。

4..Kubernetes 创建资源的方式:

4.1Kubernetes命令vs配置文件

  1. 用kubectl 命令直接创建,比如:
    kubectl run httpd-app --image=reg.tstack.com/tstack/httpd:latest --replicas=2 在命令行中通过参数指定资源的属性。
  2. 通过yaml编排文件创建,要完成前面同样的工作,可执行命令:
    kubectl apply -f httpd.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpd-deployment
spec:
  replicas: 2     #副本数
  template:
    metadata:
      labels:
        name: httpd
    spec:
      containers:
      - name: httpd-app
        image: reg.tstack.com/tstack/httpd:latest  #容器镜像

4.2YAML 基础:

kubernetes 集群通常使用YAML 文件来创建相关的资源。

它的基本语法规则如下:
格式规范:
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
井号表示注释,从这个字符一直到行尾,都会被解析器忽略。

4.2.1在kubernetes 中,需要两种结构类型:

Maps:是字典,就是一个key:value
Lists:列表,就是数组
list 的子项也可以是Maps,Maps 的子项也可以是list

#yaml格式:
---
apiVersion:v1
kind:Pod
metadata:
  name:kube100-site
  labels:
    app:web

#json格式:
{
  "apiVersion":"v1",
  "kind":"Pod",
  "metadata":{
    "name":"kube100-site",
    "labels":{
      "app":"web"
    }
  }
}
#yaml格式:
---
apiVersion: v1
kind: Deployment
metadata:
  name: kube100-site
  labels:
    app: web
spec:
  containers:
    - name: front-end
      image: nginx
      ports:
        - containerPort: 80
    - name: flaskapp-demo
      image: jcdemo/flaskapp
      ports:
        - containerPort: 5000

5.Servcie:

1.Pod 可以部署多个副本,每个Pod 都有自己的IP,Pod 很可能会被频繁地销毁和重启,它们的IP 会发生变化,用IP 来访问不太现实。Kubernetes 运行容器(Pod)是由Controller执行,而访问容器(Pod)是Service 执行。

2.Service是将请求进行负载分发到后端的各个pod容器应用上的控制器。它定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector(标签选择器)来实现无缝对接的。

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    tier: frontend

5.1 Pod IP:

每个Pod的IP地址,一个Pod里的容器访问另外一个Pod里的容器时,就是通过Pod IP所在的虚拟二层网络进行通信的,而真实的TCP/IP流量是通过Node IP所在的物理网卡流出。

5.2 Cluster IP :

就是Service 的IP ,是一个虚拟IP地址并由Kubernetes管理和分配。Service 为Pod 提供了负载均衡。

5.3 Node IP:

是Kubernetes集群中每个节点的物理网卡的IP地址,是一个真实存在的物理网络,所有属于这个网络的服务器都能通过这个网络直接通信,不管其中是否有部分节点不属于这个Kubernetes集群。

5.4 NodePort:

通过Cluster IP 只能是kubernetes集群内部的客户端访问到一组Pod,集群外部访问要在Pod所运行的Node上指定一个特定Port转发到Service Port 这个Port就是Node Port。

5.5 内部访问方式ClusterIP

ClusterIP服务是Kubernetes 的默认服务。是一个集群内的服务,集群内的其它应用都可以访问该服务。集群外部无法访问它。在某些场景下我们可以使用Kubernetes 的Proxy 模式来访问服务,比如调试服务时。

---
apiVersion: v1
kind: Namespace
metadata:
  name: test
  namespace: test

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: test
  labels:
    app: web
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-front-end
  namespace: test
spec:
  replicas: 2
  template:
    metadata:
      labels:
        name: nginx-front-end
        app: web
  spec:
    containers:
    - image: nginx
      name: nginx-front-end
      ports:
      - containerPort: 80

5.6 外部访问方式NodePort

NodePort服务是引导外部流量到内部服务的最原始方式。NodePort,正如这个名字所示,在所有节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。

5.6.1 NodePort服务特征如下:

• 每个端口只能是一种服务
• 端口范围只能是30000-32767(可调)
• 不在YAML 配置文件中指定则会分配一个默认端口

5.6.2 建议:

不要在生产环境中使用这种方式暴露服务,大多数时候应该让Kubernetes 来选择端口

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: test
  labels:
    app: web
spec:
  selector:
    app: web
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodeProt: 31005

6.laber

Label标签,是附着到object上(例如Node、Pod、Service、RC等)的键值对。给object加入一些特定信息,通过LabelSelector (标签选择器)进行检索。

◆可以在创建object的时候指定,也可以在object创建后随时指定。

◆一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上。

◆一个Label是一个key=value的键值对,其中key与value由用户自己指定。

6.1 Label案例:控制Pod 启动的位置

默认配置下,Scheduler 会将Pod 调度到所有可用的Node。不过有些情况我们希望将Pod 部署到指定的Node,比如将有大量磁盘I/O 的Pod 部署到配置了SSD 的Node;或者Pod 需要GPU,需要运行在配置了GPU 的节点上。

Kubernetes 是通过label 来实现这个功能,灵活添加各种自定义属性。比如执行如下命令标注k8s-node3 是配置了SSD 的节点。

节点打标签

kubectl label node 172.16.254.23 disktype=ssd

查看节点标签

kubectl get node --show-labels

删除节点上的标签

kubectl label node 172.16.254.23 disktype-

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpd-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        name: httpd
    spec:
      containers:
      - name: httpd-app
        image: reg.tstack.com/tstack/httpd:latest
      nodeSelector:
        disktype: ssd

7.ConfigMap:

分布式系统经常需要使用配置信息,Kubernetes 中使用Configmap 进行配置存储,所有配置参数存储在Etcd中,然后通过Volume映射的方式变成目标Pod内的配置文件,如果数据被修改,则映射到Pod中的“配置文件”也会随之自动更新。

7.1 ConfigMap 也支持四种创建方式:

7.1.1 通过--from-literal:

kubectl create configmap myconfigmap --from-literal=config1=xxx --from-literal=config2=yyy
每个--from-literal 对应一个信息条目。

7.1.2 通过--from-file:

echo -n xxx > ./config1
echo -n yyy > ./config2
kubectl create configmap myconfigmap --from-file=./config1 --from-file=./config2
每个文件内容对应一个信息条目。

7.1.3 通过--from-env-file:

cat << EOF > env.txt
config1=xxx
config2=yyy
EOF
kubectl create configmap myconfigmap --from-env-file=env.txt
文件env.txt 中每行Key=Value 对应一个信息条目。

7.1.4 通过YAML 配置文件:

Configmap 的结构很简单,除了Metadata,就只有一个Data 字段,其中保存了Keyvalue 的信息,例如:

apiVersion:v1
  kind:ConfigMap
  metadata:
    name:myconfigmap
  data:
    key1:value1
    key2:value2

7.1.5 平台内查看对应的configmap

kubectl get cm

如果需要修改服务对应的配置只需要修改对应configmap:kubectl edit cm xxx-config

8. Kubernetes网络——基础介绍

1.CNI(Container Network Interface):

容器网络接口,由一组用于配置Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。CNI 仅关心容器创建时的网络分配,和当容器被删除时释放网络资源。

2.CNI 插件:

负责将网络接口插入容器网络命名空间(例如,vethpair的一端),并在主机上进行任何必要的改变(例如将veth pair的另一端连接到网桥)。然后将IP 分配给接口,并通过调用适当的IPAM 插件来设置与“IP 地址管理”部分一致的路由。

1、同一个Pod中容器间通信:同一个Pod的容器共享同一个网络命名空间,它们之间的访问可以用localhost地址+ 容器端口就可以访问。

2、同一Node中Pod间通信:同一Node中Pod的默认路由都是docker0的地址,由于它们关联在同一个docker0网桥上,地址网段相同,所有它们之间应当是能直接通信的。

3、不同Node中Pod间通信:不同Node中Pod间通信要满足2个条件:Pod的IP不能冲突;将Pod的IP和所在的Node的IP关联起来,通过这个关联让Pod可以互相访问。

8.1 Kubernetes网络——Flannel方案

Flannel是作为一个二进制文件的方式部署在每个node上:

8.1.1 主要实现两个功能:

• 为每个node分配subnet(子网),容器将自动从该子网中获取IP地址
• 当有node加入到网络中时,为每个node增加路由配置

9.Kubernetes网络——外部访问服务方式

9.2 ingress

通常情况下,Service 和Pod 的IP 仅可在集群内部访问。集群外部的请求需要通过负载均衡转发到Service 在Node 上暴露的NodePort上,然后再由kube-proxy 通过边缘路由器(edge router) 将其转发给相关的Pod 或者丢弃。而Ingress 就是为进入集群的请求提供路由规则的集合

10. Kubernetes存储管理

10.1 Kubernetes Volume

容器和Pod 是短暂的,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。使用Kubernetes Volume 可以持久化保存容器的数据,Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但Volume 会被保留。

10.1.1 Kubernetes Volume 类型:

◆ emptyDir:最基础的Volume 类型。正如其名字所示,一个emptyDir Volume 是Host 上的一个空目录。

◆ hostPath Volume:将Docker Host 文件系统中已经存在的目录mount 给Pod 的容器。

◆ 外部Storage Provider:Kubernetes可以对接第三方存储作为Volume 供给,例如NFS、GlusterFS、Ceph等。

10.2 Kubernetes中跟存储相关的概念有PersistentVolume(PV)和PersistentVolumeClaim(PVC):

◆ Persistent Volume(PV、持久卷)是集群之中的一块网络存储。跟Node 一样,也是集群的资源。PV 跟Volume (卷) 类似,不过会有独立于Pod 的生命周期。

◆ Persistent Volume Claim(PVC、持久卷消费者)作为用户对存储资源的需求,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。

10.2.1 PV又分为静态供给PV和动态PV供给:

1.静态供给(Static Provision):提前创建了PV,然后通过PVC 申请PV 并在Pod 中使用

2.动态供给(Dynamical Provision):即如果没有满足PVC 条件的PV,会动态创建PV。相比静态供给,动态供给有明显的优势:不需要提前创建PV,减少了管理员的工作量,效率高。

3.动态供给是通过StorageClass 实现的,StorageClass 定义了如何创建PV

10.2.2 Kubernetes存储管理——PV和PVC以及对应的rbd查看

kubectl get pv

kubectl get pvc

10.3Kubernetes通过访问模式给存储分为三大类:

◆ ReadWriteMany:多路读写,卷能被集群多个节点挂载并读写
◆ ReadWriteOnce:单路读写,卷只能被单个集群节点挂载读写
◆ ReadOnlyMany:多路只读,卷能被多个集群节点挂载且只能读

11.helm介绍

11.1 为什么使用helm?

Kubernetes虽然提供了多种容器编排对象,例如Deployment StatefulSet、DeamonSet、Job等,还有多种基础资源封装例如ConfigMap、Secret、Serivce等,但是一个应用往往有多个服务,有的可能还要依赖持久化存储,当这些服务之间直接互相依赖,需要有一定的组合的情况下,使用YAML文件的方式配置应用往往十分繁琐还容易出错,这时候就需要服务编排工具。

helm 类似于Linux系统下的包管理器,如yum/apt等,可以方便快捷的将之前打包好的yaml文件快速部署进kubernetes内,方便管理维护。只不过helm这儿管理的是在k8s上安装的各种容器。
• helm的作用:像centos7中的yum命令一样,管理软件包。
• tiller的作用:像centos7的软件仓库一样,简单说类似于/etc/yum.repos.d目录下的xxx.repo。

11.2Helm 三个重要概念:

◆ chart:包含了创建Kubernetes的一个应用实例的必要信息
◆ config:包含了应用发布配置信息
◆ release:是一个chart 及其配置的一个运行实例

11.3 Helm具有如下功能:

◆ 创建新的chart
◆ chart 打包成tgz 格式
◆ 上传chart 到chart 仓库或从仓库中下载chart
◆ 在Kubernetes集群中安装或卸载chart
◆ 管理用Helm安装的chart 的发布周期

11.3 chart 的基本结构

Helm的打包格式叫做chart,所谓chart就是一系列文件, 它描述了一组相关的k8s 集群资源。比如Service、Deployment、PersistentVolumeClaim、Secret、ConfigMap 等。Chart中的文件安装特定的目录结构组织, 最简单的chart 目录如下所示:


image.png

◆ charts:保存其他依赖的chart
◆ Chart.yaml:包含Chart的基本信息,包括chart版本,名称等
◆ templates:目录下存放应用一系列k8s 资源的yaml 模板
◆ _helpers.tpl:此文件中定义一些可重用的模板片断,此文件中的定义
在任何资源定义模板中可用
◆ NOTES.txt:介绍chart 部署后的帮助信息,如何使用chart等
◆ values.yaml:包含了必要的值定义(默认值), 用于存储templates
目录中模板文件中用到变量的值

11.4 Helm 架构

Helm 包含两个组件:Helm 客户端和Tiller 服务器。

11.4.1Helm 客户端是终端用户使用的命令行工具,用户可以:

1.在本地开发chart。
2.管理chart 仓库。
3.与Tiller 服务器交互。
4.在远程Kubernetes 集群上安装chart。
5.查看release 信息。
6.升级或卸载已有的release

11.4.2 Tiller 服务器运行在Kubernetes 集群中,它会处理Helm 客户端的请求,与Kubernetes API Server 交互。Tiller 服务器负责:

1.监听来自Helm 客户端的请求。
2.通过chart 构建release。
3.在Kubernetes 中安装chart,并跟踪release 的状态。
4.通过API Server 升级或卸载已有的release。

11.5 Helm 工作原理

11.5.1 Chart Install 过程:

Helm 从指定的目录或者TAR 文件中解析出Chart 结构信息。
Helm 将指定的Chart 结构和Values 信息通过gRPC传递给Tiller。
iller 根据Chart 和Values 生成一个Release。
Tiller 将Release 发送给Kubernetes 用于生成Release。

11.6 Helm 常用命令如下:

helm create:在本地创建新的chart
helm dependency:管理chart 依赖
helm intall:安装chart
helm lint:检查chart 配置是否有误
helm list:列出所有release
helm package:打包本地chart
helm repo:列出、增加、更新、删除chart 仓库
helm rollback:回滚release 到历史版本
helm pull:拉取远程chart 到本地
helm search:使用关键词搜索chart
helm uninstall:卸载release
helm upgrade:升级release
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容