Skip to content

为什么使用单进程容器

使用容器时,要尽量使用单进程容器,所谓单进程容器,是指在容器运行时,只有一个工作进程。

如果需要存在多个进程协作的时候,要部署为两个容器,比如 PHP 一个容器,MySQL 一个容器,而不要在一个容器中运行这两者。

因为,Docker本身就是一个非常好的守护进程,它可以完美地管理一个进程,但是如果一个容器中存在多个进程时,你就需要自己维护两个进程的运行状态,比如使用 supervisord ,但这就大大增加了容器维护的难度和不稳定性。

比如在一个容器中同时运行 PHP 和 MySQL,那么如果PHP异常退出了,容器该不该连同MySQL一起退出?如果不退出,而是不断重启PHP,那么在容器之外,比如运行 docker ps 是无法了解到PHP运行状态的。

所以,使用docker,就要习惯于单进程容器的方式,既简单,又稳健。

为什么使用无状态容器

所谓状态,是指程序在执行过程中生成的中间数据,而无状态容器,是指容器在运行时,不在容器中保存任何数据,而将数据统一保存在容器外部,比如数据库中。

因为有状态的容器异常重启就会造成数据丢失,也无法多副本部署,无法实现负载均衡。

比如PHP的Session数据默认存储在磁盘上,比如 /tmp 目录,而多副本负载均衡时,多个PHP容器的目录是彼此隔离的。比如存在两个副本A和B,用户第一次请求时候,流量被转发到A,并生成了SESSION,而第二次请求时,流量可能被负载均衡器转发到B上,而B是没有SESSION数据的,所以就会造成会话超时等BUG。

如果采用主机卷的方式,多个容器挂载同一个主机目录,就可以共享SESSION数据,但是如果多主机负载均衡场景,就需要将SESSION存储于外部数据库或Redis中了。

除了文件,还有内存数据,比如Node.js项目中使用了全局变量暂存数据,那么这个容器也是有状态的,也会出现类似BUG,所以要使用无状态容器。

为什么要避免使用latest

docker镜像的tag部分可以省略,默认为latest,比如: docker pull ubuntu

这当然非常方便,但是请不要这样操作。在部署镜像时或Dockerfile的FROM中,请不要省略Tag,也不要使用latest作为Tag。

首先,这样非常不直观,ubuntu:16.04 要比 ubuntu:latest 更加明确,使用 latest 作为标签时,我们经常需要进行思考甚至查阅仓库文档才能确定具体的版本号。

更重要的,latest 引用是经常变化的,随着时间的推移,此时的latest可能和下个月的latest是完全不同的版本,比如 ubuntu:latest 刚刚从 16.04 升级为 18.04,使用 latest 会给未来增加非常多的不确定性隐患,此时能部署成功,下个月也许就会出现各种问题。

所以,请一定避免使用latest标签,而使用稳定的、明确的、具体的版本号来标明你的依赖项。

RUN cd

WORKDIR 指定工作目录(或称当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会自行创建。 WORKDIR 在 Dockerfile可以多次使用

WARNING

Dockerfile 不能等同于 Shell 脚本来书写,下面是错误写法:

RUN cd /app
RUN echo "hello" > world.txt

此 Dockerfile 构建镜像,会发现找不到 /app/world.txt 文件,或者其内容不是 hello 。

在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;

而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。

每一个 RUN 都会启动一个容器、执行命令、然后提交存储层文件变更。

第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。

第二层启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。

因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。