Docker 单进程模型理解

容器=应用+依赖的执行环境

容器不像虚拟机那样拥有独立的操作系统,容器只包括了操作系统的”躯壳”,并没有包括操作系统的内核。同一台机器上的所有容器,都会共享宿主机操作系统的内核

容器只是通过 Linux 的 NamespacesCgroups 实现了进程级别的隔离。虽然在容器里看不见宿主机上的其他进程,但归根结底它还只是一个运行在宿主机上的进程,所以容器就不具备操作系统的进程管理能力。

每个容器里只运行一个进程这个说法其实不太准确,因为像 Nginx 在启动后主进程会再开启若干个 Worker 进程负责请求的处理,Apache 更是会为每个请求创建一个进程。

容器的”单进程模型”,并不是指容器里只能运行”一个”进程,而是指容器没有管理多个进程的能力。这是因为容器里的主进程(PID=1的进程)就是应用本身,其他的进程都是这个主进程的子进程。所以,用户编写的应用,并不能够像正常操作系统里的 init 进程或者 systemd 那样拥有进程管理的功能。

比如,你的容器启动命令是执行一个 shell 脚本,脚本里依次启动容器里的 Nginx 和 Web 应用。比如下面这个 shell 脚本

1
sudo su -root -c "nginx -s start && /app/go_web_bin"

这个容器里主进程是 sh,Nginx 和 Web 应用是子进程。可是,当 Nginx 进程异常退出的时候,主进程 sh 是感知不到的,也就没法对 Nginx 进行重启。Docker 只能识别主进程的状态,如果主进程正常,Docker 的状态就是 Running,所以在容器里不推荐跑多个进程。

所以更确切的说法是每个容器应该只有一个关注点,只有一个单一的功能。将应用程序解耦到多个容器中,可以更轻松地水平缩扩和重复使用容器。例如,一个 Web 应用程序服务可能由三个单独的容器组成,每个容器都有自己的镜像,以松耦合的方式管理 Web 应用程序,数据库和 Redis 缓存。对于这些相互依赖的容器,则使用 Docker 容器网络来保持这些容器的通信。

  1. 遵循单一原则,一个容器只运行一个主进程。多个进程都部署在一个容器中,弊端很多。

    • 比如更新某个进程的镜像时,其他进程也会被迫重启。
    • 如果一个进程出问题导致容器挂了,所有进程都将无法访问。
    • 比如一个服务挂了,docker 侦测不到,你必须自己做健康状态检查。一个服务一个容器不存在这个问题,docker 能感知到服务运行状态,死了会自己重启。
    • 出现故障时开发人员能方便地对该故障容器进行问题排查,而不必对整个系统的各个部分进行排查,这也使得其更具有可移植性和可预测性。
  2. 水平伸缩将变得十分容易。例如对于美团,我们拿他的外卖和订酒店两个来说。业务分时特点非常明显,订外卖中午晚上,业务流量明显会高出很多倍。而订酒店其实更多的是靠近节假日前期。如果把各类业务揉杂在一个服务里,就会出现:明明是订餐高峰,为什么要扩容订酒店的服务?

  3. 每个容器中只运行一个应用程序,升级程序时能够将影响范围控制再更小的粒度,极大增加应用程序生命周期管理的灵活性,避免在升级某个服务时中断相同容器中的其他进程。

  4. 每个容器中只运行一个应用程序,从安全性和隔离性角度来看,能够提供更安全的服务和应用程序间的隔离,以保持强大的安全状态或遵守PCI之类的规定。

话说回来,容器本身的设计,就是希望容器和服务/应用能够具备相同的生命周期。即:一个容器对应一个进程。这样,才能够最好地应用容器编排来管理好容器和服务。


Docker 单进程模型理解
https://flepeng.github.io/042-云原生-01-Docker-00-简介-Docker-单进程模型理解/
作者
Lepeng
发布于
2023年3月1日
许可协议