Docker系列技术分享(二) Docker基础操作

上文 Docker系列技术分享(一) 容器技术和Docker 介绍了容器的历史和应用场景,本文将介绍如何从零上手使用Docker,帮助读者更直观的理解容器技术

Docker安装

Docker支持 Linux 、Windows 和 MacOS 等多个平台,当然主阵地还是Linux,因为服务器大多使用Linux操作系统,其他系统的Docker可以作为开发环境来体验


企业微信截图_20220424103227.png

各个平台的安装流程:官网安装文档 已经介绍的很详细了,不在赘述。这里以CentOS为例走下流程

系统要求

CentOS 7/8

额外注意的点:

  1. 必须启用 centos-extras ,默认情况下是开启的,如果手动禁用了,需用重新打开,参考文档 https://wiki.centos.org/AdditionalResources/Repositories
  2. Docker官方推荐使用 overlay2 存储驱动

卸载老版本

首先先要卸载老版本,防止多个版本冲突,不好管理。直接使用如下命令一键卸载。
老版本的Docker叫 docker 或者 docker-engine ,新版本的Docker引擎叫 docker-ce。

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

开始安装

Docker提供了多种安装方式,推荐使用第一种。

  1. 通过yum安装,推荐!
  2. 通过rpm包进行安装,参考文档,没网的时候可以使用,其余场景不推荐,rpm依赖处理繁琐,推荐使用Linux包管理工具yum安装。
  3. 使用官方一键安装脚本,参考文档,不推荐,虽然可以一键安装,但是安装脚本是个黑盒,一旦安装出了问题就需要额外熟悉脚本内容来定位,学习成本较高,推荐采用标准yum安装。

以下介绍yum安装流程:

设置yum仓库

添加官方yum仓库

$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

据说因为国内的网络环境限制,可能拉取有问题或者网速较慢,我是香港机器所以没遇到这个问题,如果官方源使用有问题可以试下国内的阿里云仓库

$ sudo yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

上述仓库默认只开启稳定版,这也是官方推荐的版本,类似于一个软件的release版本,如果需要最新版本或者测试版本,可以使用enable开启,要注意新版本测试不充分,未经过大规模生产验证,可能存在bug。

 $ sudo yum-config-manager --enable docker-ce-nightly  #最新版本
 $ sudo yum-config-manager --enable docker-ce-test   # 测试版本

安装 Docker Engine

yum一键安装,默认安装的是最新版Docker

$ sudo yum install docker-ce docker-ce-cli containerd.io

如果需要安装老的版本的Docker,可以用list查询历史版本。

[root@VM-4-15-centos ~]# yum list docker-ce --showduplicates | sort -r
Loading mirror speeds from cached hostfile
Loaded plugins: fastestmirror, langpacks
docker-ce.x86_64            3:20.10.9-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.8-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.7-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.6-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.5-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.4-3.el7                     docker-ce-stable
docker-ce.x86_64            3:20.10.3-3.el7                     docker-ce-stable

然后指定版本进行安装,比如如下安装3:20.10.9这个版本的Docker。

$ sudo yum install docker-ce-3:20.10.9 docker-ce-cli-3:20.10.9 containerd.io

启动 Docker

Docker引擎使用Systemd管理,可以直接start启动

[root@VM-4-15-centos ~]# sudo systemctl start docker

启动完可以status看下运行状态


企业微信截图_20220424120628.png

或者使用ps也可以看到docker daemon进程是否存活

[root@VM-4-15-centos ~]# ps aux|grep docker
root       971  0.0  0.7 1170136 60464 ?       Ssl  11:57   0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root      2166  0.0  0.0 112812   980 pts/0    R+   12:04   0:00 grep --color=auto docker

docker官方也提供了一个 hello-world 镜像来验证 Docker Engine是否正确安装运行,该镜像会打印一条语句并退出。
可以docker run跑起来看下效果。

[root@VM-4-15-centos ~]# sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete 
Digest: sha256:10d7d58d5ebd2a652f4d93fdd86da8f265f5318c6a73cc5b6a9798ff6d2b2e67
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Docker工作流程

接着来看下 docker 的工作流程,先介绍下docker的一些基本概念:

  • 镜像 :镜像本质是一个容器的快照,提供了容器运行时所需的信息:程序、库、资源、配置等。因为镜像的存在,Docker才可以做到一次构建,到处运行,通过将服务打包到镜像里,其他环境只需要拉取镜像就可以在本地把容器运行起来。
  • 镜像仓库 :类似于Git仓库,可以把构建好的镜像上传到镜像仓库,这样部署的时候无需手动传输大的镜像文件,只需要一句命令就可以从镜像仓库拉取并执行。
  • 容器 :镜像run起来就叫容器,就跟进程和程序的概念一样,静态的是镜像文件,而镜像的运行时就是容器
  • Docker daemon :Docker最核心的后台进程,它负责响应来自Docker client的请求,通过实现这些请求来完成容器的管理,包括容器的元数据管理,镜像管理等
  • docker客户端 :官方提供的客户端,提供了一些docker的常用操作,如编译镜像、拉取镜像、运行容器等

我们基本上大多数的操作都是通过docker这个客户端来进行的,通常的使用流程是:

  1. 把我们要跑的程序打包到一个镜像里,我们需要编写一个文本文件Dockerfile,该文件用于描述镜像,然后使用docker build就可以编译出来一个镜像了
  2. docker build出来的镜像是在本地,为了一次构建,任何地方都可以使用,我们需要使用docker push把镜像推送到镜像仓库
  3. 镜像仓库有镜像了之后,就可以在任何地方使用docker pull来拉取镜像
  4. 拉取下来镜像后使用docker run将容器允许起来(前提是要有docker环境)
1650782963446.png

Docker仓库

Docker官方提供了一个公共仓库 Docker Hub,用来给社区开发者存放镜像,里面有开发者上传的海量镜像,大部分系统镜像比如各种操作系统如ubuntu、centos等都可以找到,我们可以拿来当作应用的运行基础镜像,一些常用的开源软件如nginx、redis等也可以找到对应的应用镜像下载使用。
企业用户为了安全一般会选择自建Docker仓库,用法上差异不大,以下演示下Docker Hub的常用操作。

仓库地址:https://hub.docker.com/ , Docker Hub有个可视化的Web端,可以查到仓库内所有镜像,也可以进行条件筛选出想要的镜像。

企业微信截图_20220424163028.png

点击需要使用的镜像,里面有该镜像的各个版本提供选择,点击拷贝右侧命令在自己机器上执行就可以拉取镜像了。


企业微信截图_20220424165734.png

如下在本机拉取mysql最新镜像:这个拉取的过程中也可以看到一个docker镜像是由多个层组成的,这就是docker镜像的分层概念,后面会讲到。

[root@VM-4-15-centos ~]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
4be315f6562f: Pull complete 
96e2eb237a1b: Pull complete 
8aa3ac85066b: Pull complete 
ac7e524f6c89: Pull complete 
f6a88631064f: Pull complete 
15bb3ec3ff50: Pull complete 
ae65dc337dcb: Pull complete 
654aa78d12d6: Pull complete 
6dd1a07a253d: Pull complete 
a32905dc9e58: Pull complete 
152d41026e44: Pull complete 
42e0f73ebe32: Pull complete 
Digest: sha256:fc77d54cacef90ad3d75964837fad0f2a9a368b69e7d799665a3f4e90e600c2d
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest

也可以指定版本号来拉取某个版本的镜像

$ docker pull mysql:8.0.28-debian # 拉取指定版本

然后可以查看本地镜像有没有,如下命令列出本地镜像

[root@VM-4-15-centos ~]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
mysql         latest    f2ad9f23df82   3 days ago     521MB
hello-world   latest    feb5d9fea6a5   7 months ago   13.3kB

除了页面上可视化查看镜像,docker客户端也提供了命令可以操作镜像仓库,用search可以搜索到想要的镜像。

[root@VM-4-15-centos ~]# docker search mysql --limit 5
NAME             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql            MySQL is a widely used, open-source relation…   12453     [OK]       
mariadb          MariaDB Server is a high performing open sou…   4800      [OK]       
percona          Percona Server is a fork of the MySQL relati…   575       [OK]       
phpmyadmin       phpMyAdmin - A web interface for MySQL and M…   512       [OK]       
circleci/mysql   MySQL is a widely used, open-source relation…   25   

docker没有提供获取某个镜像所有历史版本的命令,如果想要拿到某个镜像的历史版本,可以使用如下curl拿到。

[root@VM-4-15-centos ~]# curl https://registry.hub.docker.com/v1/repositories/mysql/tags | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4654    0  4654    0     0   4485      0 --:--:--  0:00:01 --:--:--  4487
[
    {
        "layer": "",
        "name": "latest"
    },
    {
        "layer": "",
        "name": "5"
    },
    {
        "layer": "",
        "name": "5-debian"
    },
    ...
]

docker客户端还有很多好用的命令不在赘述,大家可以看下help自行体验下。

[root@VM-4-15-centos ~]# docker -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

Options:
      --config string      Location of client config files (default "/root/.docker")
  -c, --context string     Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker
                           context use")
  -D, --debug              Enable debug mode
  -H, --host list          Daemon socket(s) to connect to
  -l, --log-level string   Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
      --tls                Use TLS; implied by --tlsverify
      --tlscacert string   Trust certs signed only by this CA (default "/root/.docker/ca.pem")
      --tlscert string     Path to TLS certificate file (default "/root/.docker/cert.pem")
      --tlskey string      Path to TLS key file (default "/root/.docker/key.pem")
      --tlsverify          Use TLS and verify the remote
  -v, --version            Print version information and quit

Docker镜像

除了使用一些公共镜像外,我们更多的需求其实是自己制作镜像,因为自研的软件需要跑在容器里

我们知道内核启动前要先挂载rootfs,根据rootfs来启动内核,然后在运行其他应用程序。
Docker启动也是,先要跑系统内核,才能跑应用程序,所以 Docker 的镜像,其实就是一个rootfs。

我们通过Dockerfile来描述镜像内容,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建,通过Dockerfile也可以直观的看到镜像做了啥。
由于Docker镜像是分层的,当前层必须在上一层上构建,所以Docker要求必须有基础镜像,Dockerfile的第一条指令必须要使用FROM来设置一个基础镜像,基础镜像可以在镜像仓库找到,通常会使用一些系统镜像centos、ubuntu等做来服务的系统环境

#设置基础镜像为Centos
FROM centos

如果实在是不需要基础镜像,Docker也提供了一个空白的镜像 scratch可以作为基础镜像,通常对镜像体积大小有要求的场景会需要这么做,但从零开始搭建运行环境是比较繁琐滴

FROM scratch

ENV用来设置环境变量,无论是后续的指令还是运行在容器里的应用都可以直接使用

ENV TZ=UTC  CGO_ENABLED=1  GOTRACEBACK=crash 

RUN 指令是用来执行shell命令的,常用于服务启动前做一些前置工作。

RUN set -x \
    && mkdir -p /data/testsvr

COPY指令用于将本地文件复制到镜像内目录,比如要运行的二进制文件、配置等。

使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用,特别是源文件采用Git管理时。

COPY ./testsvr \
     /data/testsvr/

还有个ADD指令 功能跟COPY很像,可以添加文件到指定目录,区别是可以自动解压tar包,如果是链接URL会自动wget下载到目标目录。该指令不建议使用,因为语义不直观,解压和下载操作可以通过RUN指令来实现

ADD test.tgz /data

前面提到容器的启动需要通过镜像文件,而容器运行时对当前环境的修改是不会同步修改镜像的,这时候有个问题是如果你在容器运行时修改了一些文件,当容器重启过后这些修改是不生效的。

对于一些需要持久化数据的业务,比如说服务打印的日志、数据库文件等,我们希望容器重启后仍然存在,Docker提供了VOLUME,VOLUME类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录/文件会复制到VOLUME中,使用VOLUME可以实现容器运行时针对挂载目录的写入不会随着容器删除而消失。

以下是创建一个匿名卷挂载在/data目录。

VOLUME [ "/data" ]

容器的本质就是一个进程,通常跟我们的服务生命周期绑定,Docker提供了CMD命令用于指定容器的启动命令,这是一个容器最主要做的事情,当启动命令结束,容器的生命周期也就结束了,所以启动命令一般来启动服务进程,如下

#设置容器的启动命令,该配置可用运行时参数覆盖
CMD ["./testsvr", "-conf ../etc/config.yaml"]

Docker还提供了一个ENTRYPOINT指令,该指令也是用来指定容器的启动命令,但是可以使用脚本,我们通常会写一个entry脚本来作为启动脚本,然后CMD来给启动脚本传参,这样可以在启动脚本里做一些前置的准备工作,比如一些初始化、一些依赖安装等

ENTRYPOINT [ "./testsvr.entry" ]
CMD [ "-conf ../etc/config.yaml" ]

如下是entry脚本内容,可以直接使用shell命令

#!/bin/sh

export GOTRACEBACK=crash

cd /data 
echo "start testsvr"
./testsvr  $*

Docker镜像文件常用指令介绍的差不多了,详细指令使用可以查阅文档

有了Dockerfile之后,我们可以通过docker build来编译出镜像,镜像编译输出过程可以很明显的看到每一层的指令执行过程

[root@VM-4-15-centos ~]# docker build -t testsvr:v1 -f ./testsvr.Dockerfile --network=host .
Sending build context to Docker daemon  78.85kB
Step 1/4 : FROM centos
latest: Pulling from library/centos
a1d0c7532777: Pull complete 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
 ---> 5d0da3dc9764
Step 2/4 : ENV TZ=UTC  CGO_ENABLED=1  GOTRACEBACK=crash
 ---> Running in 5bfc6cdf203d
Removing intermediate container 5bfc6cdf203d
 ---> 700b7da62095
Step 3/4 : RUN set -x     && mkdir -p /data/testsvr
 ---> Running in cc7757be117f
+ mkdir -p /data/testsvr
Removing intermediate container cc7757be117f
 ---> 95b95c993e5d
Step 4/4 : CMD [ "curl", "-s", "ifconfig.me" ]
 ---> Running in 01cf6e59216e
Removing intermediate container 01cf6e59216e
 ---> 26ca123fe1c6
Successfully built 26ca123fe1c6
Successfully tagged testsvr:v1

可以看到刚编译好的新鲜镜像

[root@VM-4-15-centos ~]# docker image ls|grep testsvr
testsvr       v1        26ca123fe1c6   3 minutes ago   231MB

本地镜像也可以上传远程仓库方便其他环境下载,需要先在docker仓库里新建一个仓库, 使用docker login进行鉴权后直接push就可以了

docker login  --username ***** --password ***** testsvr
docker push testsvr:v1

Docker运行

我们通过docker run就可以跑起来一个容器,不带任何参数时要求镜像里定义了CMD或ENTRYPOINT,因为容器要靠启动命令来启动

[root@VM-4-15-centos ~]# docker run testsvr:v1
43.154.203.100

docker客户端也支持使用命令行参数设置启动命令的方式,如下 cat /etc/os-release 就是该容器的启动指令

[root@VM-4-15-centos ~]# docker run centos cat /etc/os-release
NAME="CentOS Linux"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Linux 8"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-8"
CENTOS_MANTISBT_PROJECT_VERSION="8"

另外上述提到的大多数镜像指令都可以通过docker客户端的命令行参数使用,不过我们大多数还是使用dockerfile方式,更加直观且可以备份。比如-v参数添加数据卷

docker run -v /tmp/data testsvr:v1

也可以运行交互式bash,比如如下运行centos镜像,可以直接进入容器内部查看

[root@VM-4-15-centos ~]# docker run -it centos bash
[root@735e1dfb0a57 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

运行中的容器也可以使用exec进入

[root@VM-4-15-centos ~]# docker exec -ti 735e1dfb0a57 bash
[root@735e1dfb0a57 /]# pwd
/

docker ps可以查看运行中的镜像,还可以查看容器进程在宿主机的进程pid等信息

$ docker ps
[root@VM-4-15-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED              STATUS              PORTS     NAMES
735e1dfb0a57   centos    "bash"    About a minute ago   Up About a minute             exciting_bassi

#查看容器进程在系统中的进程号
[root@VM-4-15-centos ~]# docker inspect --format '{{ .State.Pid }}' 735e1dfb0a57
3549

我们也可以来验证下上述提到的数据卷到底有没有实现持久化能力,我们先挂载一个匿名卷到/tmp/data,接着给这个目录写文件

[root@VM-4-15-centos ~]# docker run -v /tmp/data -it centos bash
[root@4fa3b36b96dc /]# echo "volume test" > /tmp/data/volume-test.txt
[root@4fa3b36b96dc /]# ls -ahtl /tmp/data/
total 12K
drwxr-xr-x 2 root root 4.0K May  8 03:21 .
-rw-r--r-- 1 root root   12 May  8 03:21 volume-test.txt
drwxrwxrwt 1 root root 4.0K May  8 03:19 ..

需要注意的是容器挂载目录和宿主机目录不是一一对应的,可以使用inspect查到宿主机该卷的位置,来看下是否生效

[root@VM-4-15-centos ~]# docker inspect 4fa3b36b96dc | grep volumes
                "Source": "/var/lib/docker/volumes/755e1a810b8ba41c35cd3cd2d5cc207b13079ad14fc98289ecd1a7f7703546b5/_data",
[root@VM-4-15-centos ~]# ll -aht /var/lib/docker/volumes/755e1a810b8ba41c35cd3cd2d5cc207b13079ad14fc98289ecd1a7f7703546b5/_data
total 12K
drwxr-xr-x 2 root root 4.0K May  8 11:21 .
-rw-r--r-- 1 root root   12 May  8 11:21 volume-test.txt
drwx-----x 3 root root 4.0K May  8 11:19 ..

我们也可以将主机目录挂载为数据卷,这样就可以人为指定某个卷在宿主机的路径,便于管理

$ docker run -it -v /host/data:/data centos bash

Docker架构

老版本的Docker容器是通过 Docker Daemon 直接来启动的,Docker Client通过命令行与Daemon交互,管理镜像和容器,同时Daemon负责容器生命周期的管理,这样有个问题是一旦Daemon因为Client操作问题挂了或者重启,所有运行中的容器都会挂,所以Docker做了架构上的升级,新架构如下

未命名文件 (1).png

新架构将Daemon功能做了拆分解耦,Daemon进程只负责和 Docker Client 端交互,管理 Docker 镜像和容器,由Containerd 来管理容器的生命周期,并向上为 Docker Daemon 提供 gRPC 接口。

当创建一个容器时,Docker Daemon 不直接帮我们创建,而是请求 Containerd 来创建,Containerd 收到请求后,也不会直接去操作容器,而是创建一个叫做 Containerd-Shim 的进程,让这个进程去操作容器。这里有个好处是一个容器对应一个Containerd-Shim,Containerd-Shim作为容器进程的父进程,避免了如果所有容器都使用Containerd做父进程的时候,Containerd挂了所有的容器都会挂。

Docker提供了RunC来创建一个容器,由他来完成容器运行的前置工作,包括容器的一些基础能力,比如配置Namespace来进行资源隔离、配置Cgroup的来进行资源限制等一系列操作,RunC启动完容器后本身会直接退出,由Containerd-Shim来作为容器进程的父进程,负责收集容器进程的状态,上报给Containerd,监管容器的生命周期,确保容器可以真正运行和正常退出。

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

推荐阅读更多精彩内容