kubernetes 上手指南:前言

大家好,我叫謝偉,是一名程序員。

今天的主題:kubernetes 學習前言,主要和容器相關。

部署方案

容器技術誕生后,成為云計算領域的絕對主角,但容器本身價值并不大,任何互聯網領域都涉及到部署,容器編排才重要。創造docker 的dotCloud 的公司并沒有獲取到云計算領域的紅利,雖然之后也推出的自家的 docker swarm 應用于容器編排,相比設計理念更為先進的 kubernetes,存在更多的問題,事實上 k8s 已經成為容器編排領域的領頭羊。幾乎所有的互聯網公司,云計算公司都使用 k8s 用于容器編排。

簡單的說容器編排的意思是:將應用容器化,按照一套規則自動在節點按照用戶的需求部署。這套規則由k8s 規定,開發者應用容器化,按照規則編寫編排腳本即可。

整體上 k8s設計理念先進,得益于 Google 領域內的多年實踐總結,盡管如此,對開發者而言,掌握 k8s 知識,卻有點復雜。

復雜體現在兩方面:

  • 集群部署

集群部署有規格要求,如果你是初學者,想要親手部署這樣一套集群服務,環境可能都沒有。基礎的容器組件拉取也可能是問題,不過這一情況正在好轉。

  • 概念繁多

開發者需掌握 k8s 諸多的概念,另外得掌握編排腳本語法約束,不過,只要肯花時間都可以掌握。

簡單的說 k8s 適用于復雜的部署,多節點,多應用,系統越復雜,上 k8s 調度起來更方便。簡單的系統,應用容器化,運行容器即可,如果多應用使用單節點部署方案 docker-compose 即可。

容器

容器技術給應用創造一個完全獨立的環境,可以跨平臺使用,我認為是每一個開發者需要掌握的技術之一。

如何使用容器技術?

  • 開者專注自身業務需求,編寫代碼
  • 編寫鏡像制作腳本,將應用打包
  • 服務器上拉取制作的鏡像
  • 容器啟動,運行服務

容器技術主要包四大組成部分:

  • 鏡像 image
  • 容器 container
  • 網絡 network
  • 數據卷 volume

何為鏡像:簡單的說,包含虛擬運行環境的文件包,是一堆文件的合集,服務在該系統之上能夠運行起來。docker 鏡像采用了分層架構。

何為容器:簡單的說,鏡像的運行狀態,用來隔離虛擬環境的基礎設施。主要包含:鏡像、運行環境、指令集

何為網絡:網絡是應用之間通訊的媒介。

何為數據卷:應用肯定會涉及到數據持久化操作,數據卷就是用于宿主機和容器之間共享或者持久化。

容器技術的實現: 得益于以下三點技術

  • Namespace命名空間 : 作用是隔離
  • Control Groups控制組:作用是限制計算機資源的使用
  • Union File System 聯合文件系統:作用是實現不同目錄掛載到同一目錄

問題一: Namespace 怎么就能做到隔離?

容器本質上是進程,Linux操作系統提供了 PID, Mount, Network 等 namespace, 使被隔離的進程只能看到當前的環境狀態。

比方說 PID namespace, 啟動容器執行命令,為什么該進程的 PID 是 1 呢?

>> ps -ef | grep 1
root         1     0  0 14:08 ?        00:00:01 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything
root        15     0  0 15:20 pts/0    00:00:00 bash
root        33    15  0 15:22 pts/0    00:00:00 ps -ef
root        34    15  0 15:22 pts/0    00:00:00 grep 1

其中 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything 是我自己的容器啟動時的命令。

主要原因是:Linux 創建進程可以指定 PID

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

類似的其他也可以依靠對應的 namespace 技術做到隔離。

問題二:control Groups 怎么就能夠現在計算機資源的使用了?

首先限制的資源包括:CPU, 內存,磁盤,網絡等。如果你經常使用 docker,一定會經常看到一些目錄: /sys/fs/cgroup

>> mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)

以 CPU 為例,如何實現對資源的限制呢?

首先查看我本機運行的容器:

>>  docker ps --format "{{.ID}}: {{.Status}}"

81f4b0f829c9: Up 4 days
226749434f46: Up 4 days
3102306bf580: Up 4 days
>> ls /sys/fs/cgroup/cpu/docker

drwxr-xr-x 2 root root 0 Dec  1 07:31 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f
drwxr-xr-x 2 root root 0 Nov 26 08:44 3102306bf58037012bd5d2b6f595ae00450125d4152ed53d2db71d35ab297439
drwxr-xr-x 2 root root 0 Nov 26 14:17 81f4b0f829c9558228691c979af188b30cfaec01dd78b97d90e95aff00cbfb99
-rw-r--r-- 1 root root 0 Dec  1 07:30 cgroup.clone_children
-rw-r--r-- 1 root root 0 Dec  1 07:30 cgroup.procs
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.shares
-r--r--r-- 1 root root 0 Dec  1 07:30 cpu.stat
-rw-r--r-- 1 root root 0 Dec  1 07:30 notify_on_release
-rw-r--r-- 1 root root 0 Dec  1 07:30 tasks

>> cd 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f

-rw-r--r-- 1 root root 0 Dec  1 07:31 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 26 14:06 cgroup.procs
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.shares
-r--r--r-- 1 root root 0 Dec  1 07:31 cpu.stat
-rw-r--r-- 1 root root 0 Dec  1 07:31 notify_on_release
-rw-r--r-- 1 root root 0 Dec  1 07:31 tasks

沒錯,docker 實現對資源的限制靠的是寫文件的形式,即對類似這樣的文件進行限制:假設:cpu.cfs_period_us(10000),cpu.cfs_quota_us(2000)表明只能使用到20 % 的CPU 資源。

cpu.cfs_period_us
cpu.cfs_quota_us
...

問題三:聯合文件系統是什么意思?

將多個位置的不同目錄掛載到同一目錄下。

比如:

>> tree
├── A
│  ├── a
│  └── x
└── B
  ├── b
  └── x

掛載到 C 目錄下:


$ tree C
├── a
├── b
└── x

當然實際的實現比我說的要復雜的多。docker 正是依靠這三種技術,使的可以為我們創造一個完全隔離的沙箱,這樣做的好處是:環境的一致性,方便部署。你會越來越少的問這樣一個問題:我本地可以運行啊?為什么在服務器上就不行。

Docker 架構

docker 采用典型的 C/S 架構,你安裝 docker 軟件。默認會在本機啟動一個守護進程 docker dameon , 同時提供一個命令行客戶端 docker cli . 你可以使用命令行操作包括:鏡像、容器等各種資源。

根據 docker 的四大組成部分,docker cli 的命令主要圍繞這四個命令展開:

  • docker image // 操作鏡像
  • docker container // 操作容器
  • docker network // 操作網絡
  • docker volume // 操作數據卷

你可能會覺得命令行命令太多,怎么辦?創造高頻使用環境即可,即:日常工作中頻繁的不斷的使用docker 即可。

制作鏡像

Docker Hub 是最大的鏡像托管平臺,作用是:1. 鏡像存儲 2. 鏡像分發。無數的開源軟件的官方鏡像托管在該平臺,用戶可以完全免費的從該平臺拉取鏡像,完成自己的任務。

那么對于開發者如何制作自己的鏡像呢?

答案是:編寫 Dockerfile 文件。

docker 公司對于制作鏡像,約束了一套語法規范,其實命令也就 10幾個。

先不談具體的命令是什么,假設你自己開發了一套系統,沒有容器之前,你部署應用會執行什么操作?

  • 應用運行涉及依賴,你會提前在主機上安裝相應的依賴軟件
  • 指定應用目錄
  • 拷貝,將應用程序拷貝至主機,不管是本機直接拷貝還是代碼托管平臺克隆代碼,都算
  • 應用訪問涉及端口開放,應用多了,你還得考慮端口會不會沖突不是?
  • 應用啟動

這是不是大概的操作流程?

是的,dockerfile 將這一系列動作指令化。

舉個例子:我這邊自己寫了個服務,對外暴露的是 API,我現在想制作鏡像,我會怎么做呢?

FROM golang:1.13.4

MAINTAINER XieWei(1156143589@qq.com)

EXPOSE 8888 8081

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime;\
    echo "Asia/Shanghai" > /etc/timezone;\
    dpkg-reconfigure -f noninteractive tzdata

WORKDIR /go/src/github.com/wuxiaoxiaoshen/go-anything
RUN echo $PWD
COPY . /go/src/github.com/wuxiaoxiaoshen/go-anything

RUN apt-get update && apt-get install -q -y vim nginx  git openssh-client cron && apt-get clean;\
    go mod vendor;\
    make remove;\
    make prod;\
    echo Succeed!
CMD ["bash","-c", "/go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything"]

簡單的說:

  • FORM 依賴的基礎鏡像,我這個服務是 Go 編寫的,依賴 Go 的運行環境,所以繼承官方基礎鏡像
  • MAINTAINER 維護者,將被拋棄的命令,使用替代指令 LABEL
  • EXPOSE 暴露端口,意思是容器會暴露8888,8081 兩個端口
  • RUN 將一些命令發送到終端執行,安裝一些依賴
  • WORKDIR 指定目錄
  • COPY 拷貝上下文內容至指定目錄
  • CMD 容器啟動時執行的命令

dockerfile 文件編寫并不用太復雜,就可以按照開發者的約束制作鏡像。非常便利。如果記不住命令,仍然建議自身創造高頻使用環境,在日常工作中,有意無意的多使用。

一般我會把制作好的鏡像直接推送至 Docker Hub, 需要使用到再從 Docker Hub 上拉取,當然你需要先注冊個賬號。

多應用

還是我這個應用:go-anything, 實質它是個 web 服務,為創造稍微復雜的系統,這個系統,我使用到了 MySQL, Redis, Kafka 等技術集成進來。

如果這些 MySQL, Redis, Kafka 等都選擇的是云服務,go-anything 中指定相應的云服務地址即可,相當于沒有外部依賴。當然我沒錢買云服務,這些服務都選擇的是在本地啟動相應的容器,這么說,這其實是個多應用的編排的問題。

如果是單節點或者測試環境,那么我推薦使用 docker-compose 來鏈接和啟動多服務。我這個服務依賴于上面三個服務,這三個服務啟動之后,才能正確的運行服務。

那么什么是 docker-compose, 簡單的說,是用 Python 編寫的命令行工具,用來定義和運行由多個容器組成的應用。它規定了一套語法規范,這些規范的關鍵字都和上文提到的容器相關。

就我這個應用,我怎么編寫 docker-compose 呢?

version: "3"

services:
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    expose:
      - 6379
    container_name: redis_for_go_anything
    command: redis-server --appendonly yes --requirepass "adminRedis"
    networks:
      - go-anything-network
    volumes:
      - data:/data
  mysql:
    image: mysql:latest
    container_name: mysql_for_go_anything
    networks:
      - go-anything-network
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: adminMysql
      MYSQL_DATABASE: go-anything
      MYSQL_USER: root
    ports:
      - 3306:3306
  kafka:
    image: index.docker.io/wurstmeister/kafka:latest
    container_name: kafka_for_go_anything
    ports:
      - 9092:9092
    environment:
      KAFKA_OFFSETS_TOPIC_REPLIATION_FACTOR: 1
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://127.0.0.1:9092"
      KAFKA_LISTENERS: "PLAINTEXT://:9092"
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_CREATE_TOPICS: "go-anything:20:1:compact"
      KAFKA_LOG_DIRS: /kafka/kafka-logs
    depends_on:
      - zookeeper
    networks:
      - go-anything-network
    volumes:
      - data:/kafka/kafka-logs
  zookeeper:
    image: index.docker.io/wurstmeister/zookeeper:latest
    container_name: zookeeper_for_go_anything
    ports:
      - 2181:2181
    networks:
      - go-anything-network
networks:
  go-anything-network:
    driver: bridge
volumes:
  data: {}

看上去稍微復雜點,其實可以劃分為四個部分:

version: 版本
services: 服務
volume: 數據卷
network: 網絡

其中最重要的是 services,像 network, volume 這些,不寫都可以使用默認的,但我一般都會寫,顯式化得定義 network 和 volume。

單獨抽出 redis 服務來看,其實是關于容器的操作:

  redis:
    image: redis:latest
    ports:
      - 6379:6379
    expose:
      - 6379
    container_name: redis_for_go_anything
    command: redis-server --appendonly yes --requirepass "adminRedis"
    networks:
      - go-anything-network
    volumes:
      - data:/data

redis: 服務的名稱,自定義
image: 鏡像地址和版本
ports: 主機和容器端口映射
expose: 暴露容器端口
container_name: 容器自定義名稱
command: 容器啟動時命令
networks: 指定網絡
volume: 數據卷定義

這些比如端口,執行命令等,開發者有時候記不住,怎么辦? 我也記不住,都是查看 docker hub 上相應的文檔說明,畢竟這是別人制作的鏡像,不看文檔,怎么知道怎么做。

編排好上文三個服務:MySQL, Redis, Kafka,怎么一鍵讓服務運行?

>> docker-compose -f docker-compose.yml up -d

可以命名為 docker-compose.yml,也可以不這樣命名。不指定文件,自動會在當前目錄下尋找 docker-compose.yml。docker-compose 還提供了其他命令,絕大多數也是用來操作鏡像和容器的。

假設我把我的應用已經推送至 docker hub 上:wuxiaoshen/go-anything:v0.19

我現在應該怎么啟動我的服務呢?

>> docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
wuxiaoshen/go-anything               v0.19               1788461daca4        30 hours ago        1.43GB

>> docker run --name go-anything-2  --link mysql_for_go_anything --link redis_for_go_anything --link kafka_for_go_anything --net go-anything_go-anything-network -p 8081:8888 -d 1788461daca4

其中:

--name 指定容器名稱
--link 鏈接服務
-p 端口映射
-d 后臺運行

為什么要 --link 服務呢?因為我項目的配置文件是這樣的:

  mysql:
    port: "3306"
    db: go-anything
    user: root
    passwd: adminMysql
    host: mysql
  redis:
    port: "6379"
    auth: adminRedis
    host: redis
  kafka:
    broker: kafka:9092
    topic: go-anything
    consumerGroup: go-anything-consumer-group

其中對應的 host 和 docker-compose.yml 對應的services 中命名的服務名稱一致。這樣可以直接使用服務名稱代替 host,而不用顯式的指定主機地址(127.0.0.1)

這樣我們就啟動了多應用:

>> docker ps --format "{{.ID}}: {{.Command}}: {{.Ports}}"
c7d820406af2: "bash -c /go/src/git…": 8081/tcp, 0.0.0.0:8081->8888/tcp
d197ec955421: "start-kafka.sh": 0.0.0.0:9092->9092/tcp
50cdda796143: "/bin/sh -c '/usr/sb…": 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp
3cb75161e860: "docker-entrypoint.s…": 0.0.0.0:3306->3306/tcp, 33060/tcp
46d16bd9837e: "docker-entrypoint.s…": 0.0.0.0:6379->6379/tcp

啟動服務中指定了映射端口:將宿主機的 8081 和 容器內的 8888 的端口相互映射,而應用指定的端口是:8888,這樣本機訪問 8081 端口可以訪問到容器 8888 端口。

# 項目內指定端口
router.Run("8888")

測試下:

  • 先查看應用啟動日志
>> docker logs -f c7d820406af2

2019/12/01 14:08:46 Env:  service
2019/12/01 14:08:46 Web Start...
2019/12/01 14:08:46 Step 0: Configs...
2019/12/01 14:08:46 Step 1: Mysql...
2019/12/01 14:08:46 configs: LoadConfigs: key: service.mysql
2019/12/01 14:08:46 Keys: MySQL: map[string]interface {}{"db":"go-anything", "host":"mysql", "passwd":"adminMysql", "port":"3306", "user":"root"}
2019/12/01 14:08:46 root:adminMysql@tcp(mysql:3306)/go-anything?charset=utf8&parseTime=True&loc=Local
2019/12/01 14:08:47 Step 2: Redis...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.redis
2019/12/01 14:08:47 Keys: Redis: map[string]interface {}{"auth":"adminRedis", "host":"redis", "port":"6379"}
2019/12/01 14:08:47 Step 3: Kafka...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.kafka
2019/12/01 14:08:47 Keys: Kafka: map[string]interface {}{"broker":"kafka:9092", "consumergroup":"go-anything-consumer-group", "topic":"go-anything"}
2019/12/01 14:08:47 Step 4: Email...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.email
// 省略
[DBUG] 2019/12/01 14:08 GET: /v1/api/status/health -> github.com/wuxiaoxiaoshen/go-anything/src/Healthz.healthZHandler() and 2 more
// 省略
[DBUG] 2019/12/01 14:08 Application: running using 1 host(s)
[DBUG] 2019/12/01 14:08 Host: addr is :8888
[DBUG] 2019/12/01 14:08 Host: virtual host is 0.0.0.0:8888
[DBUG] 2019/12/01 14:08 Host: register startup notifier
[DBUG] 2019/12/01 14:08 Host: register server shutdown on interrupt(CTRL+C/CMD+C)
Now listening on: http://0.0.0.0:8888
Application started. Press CTRL+C to shut down.
  • 調用下接口:/v1/api/status/health
>> curl http://127.0.0.1:8081/v1/api/status/health | jq .

{
  "code": 200,
  "data": "pong",
  "status": "ok"
}

可以看到完成了多應用的啟動部署。

可以看到單節點上部署多應用,其實 docker-compose 是個很好的工具,那假如多節點呢?docker-compose 完全無用武之地啊。

之后我會仍然使用這個示例在 k8s 集群上進行啟動部署。

下期:k8s 基本概念。


代碼:go-anything

<全文完>

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容