本文发布于Cylon的收藏册,转载请著名原文链接~


什么是容器中的多进程管理

在容器中的主进程 (main running process) 是指 Dockerfile中 ENTRYPOINTCMD 指定运行的命令,通常情况下一个进程(服务)为一个容器;也存在一种场景,就是主进程会fork多个子进程,例如nginx,不过这种多进程通常为nginx主进程进行管理。而一些场景下,我们的业务本身就需要多个启用独立的多个进程。

在Docker官方提到了在容器中运行多个服务的方式,官方提出,应该避免这种情况

but to get the most benefit out of Docker, avoid one container being responsible for multiple aspects of your overall application.

但也给出了如何管理多进程的一种思路,

  • Use a wrapper script
  • Use Bash job controls
  • Use a process manager

下面就通过官方给出的这三种方式阐述容器中的多进程管理

Use a wrapper script

对于使用脚本来管理多进程来说,本质上是可以实现多进程的启动,但是你没法去监控(管理)多个进程的运行时,例如 Nginx + PHP 模式, PHP或nginx全部挂掉,只要脚本还在运行,那么这个容器的生命周期还是处于Running

Use Bash job controls

这种模式是利用了Bash的后台模式进行短暂的切换进程,但有些镜像不提供Bash这时应该怎么办

Use a process manager

进程管理器,通常情况下大家想到的就是顶顶大名的 supervisor 和 systemd,但这两个程序运行的环境十分苛刻,例如 supervisor 是Python开发的程序,运行需要依赖 Python;而 systemd 的运行条件更为苛刻,例如需要额外运行dbus-damon进行注册到dbus总线之上,这种进程管理器可能运行的进程比我们要管理的进程都要多。在这种场景下,有一个部署简单,配置简单,无依赖的轻量级容器多进程管理器 s6-overlay

s6-overlay

s6-overlay 一组脚本,只需要简单解压就可以使现有的 Docker 镜像通过将 s6 用作容器的 pid 1 和服务的来管理多个进程。

s6-overlay 包含两个组件,s6-overlay-noarch.tar.xzs6-overlay-x86_64.tar.xz

  • noarch 包含了一些脚本,是s6运行的所必须有的一个组件,他包含了 /init 作为 pid 为1 的进程
  • x86 是作为 x86系统下运行 s6 所需要的 所有二进制文件

编写服务启动脚本

需要在 /etc/s6-overlay/s6-rc.d/ 与 /etc/services.d/ 中配置你要启动的app,例如

/etc/services.d/nginx/run

run则代表启动的命令

#!/command/execlineb -P
nginx -g "daemon off;"

除上述提到的内容外,还需一个 type 来指明 启动的模式

  • longrun 运行为daemon模式被s6进行管理
  • oneshot 类似一个脚本,但通过s6-rc进行管理,类似于初始化任务

所以你需要在 /etc/s6-overlay/s6-rc.d/myapp/type 中定义其 type 文件,这个文件内填写这两种类型的文字即可

到这里完成了一个基本的进程的配置,例如还有 finish 脚本,当在失败时执行的

S6 init 的阶段

s6官方对init阶段省略了用户不需要关心的一个阶段后,为 3 个阶段

  • 初始化阶段 (initialization),这里是内核启动的第一个用户态进程,该阶段作为init唯一的持久进程
  • 巡航阶段 (cruising),这个阶段init负责启动与维护其他进程,比如运行s6系列,init 的职责是清除孤儿进程并监督进程,同时允许管理员添加或删除服务,例如上面的 longrunoneshot 类的服务,都是在这个阶段被启动
  • 关闭阶段 (shutdown),在此阶段结束时,所有进程都将被终止
    • 发送 TERM 信号 到遗留的 longrun 服务,如果需要将等待结束后退出
    • 有序的关闭用户 s6-rc
    • 运行 finalization 脚本
    • 向进程发送 TERM signal,最终不会留下任何的进程
    • sleep一阵,允许驻留的进程退出完
    • 发送 KILL 信号,退出所有进程,这时容器退出

S6的安装

S6的安装很简单,步骤只需要如下几步:

  • 只需要下载对应的两个tar包
  • init 作为pid为1的进程
  • 准备 installiation阶段 和 finalization 阶段的脚本 复制到对应路径内就可以正常启动了

finalization 通常使用场景为:当你的程序在退出时存在一些特定的结束命令的场景,官方给出的通常是用于进程结束后的清理动作

Note that in general, finish scripts should only be used for local cleanups after a daemon dies. If a service is so important that the container needs to stop when it dies, we really recommend running it as the CMD.

下面是一个完整的使用了 s6 的多进程容器的 Dockerfile

FROM nginx:1.20 AS runner
WORKDIR /uranus
ARG S6_OVERLAY_VERSION=3.1.5.0
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp

RUN apt update && apt install xz-utils procps iproute2 -y && \
    tar -Jxpf /tmp/s6-overlay-x86_64.tar.xz -C / && \
    tar -Jxpf /tmp/s6-overlay-noarch.tar.xz -C / && \
    rm -f /tmp/s6-overlay-x86_64.tar.xz && \
    rm -f /tmp/s6-overlay-noarch.tar.xz
ENTRYPOINT ["/init"]
RUN mkdir /etc/services.d/
COPY --from=builder /uranus/_output/firewalld-gateway ./bin/
COPY --from=builder /uranus/firewalld-gateway.toml .
COPY --from=builder /uranus/dist /var/run/nginx/
COPY --from=builder /uranus/uranus.nginx.conf /etc/nginx/conf.d/
COPY --from=builder /uranus/s6/ /etc/s6-overlay/s6-rc.d/
COPY --from=builder /uranus/s6/ /etc/services.d/
ENV PATH "$PATH:/uranus/bin"
RUN  firewalld-gateway --sql-driver=sqlite --migration && \
     rm -f /etc/nginx/conf.d/default.conf && \
     echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type && \
     echo "longrun" > /etc/s6-overlay/s6-rc.d/uranus/type && \
     mkdir -pv /etc/s6-overlay/s6-rc.d/uranus/contents.d && \
     mkdir -pv /etc/s6-overlay/s6-rc.d/nginx/contents.d

#CMD [ " /command/s6-svscan", "/etc/services.d" ]
VOLUME ["/uranus" ]
EXPOSE 2953/tcp

在容器中进程内可以看出对应进程图 s6init 作为所有进程的父进程管理着supervise,之后管理者你需要管理的进程;如果进程异常,他会不断地拉起对应的进程,当然,如果是启动参数错误问题,那么永远不会被拉起,当然容器是出于 Running,这时就需要自行做服务检测

$ pstree
s6-svscan-+-s6-supervise---s6-linux-init-s
          |-s6-supervise---s6-ipcserverd
          |-3*[s6-supervise]
          |-s6-supervise---firewalld-gatew---5*[{firewalld-gatew}]
          `-s6-supervise---nginx---4*[nginx]


$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 May18 ?        00:00:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service
root        16     1  0 May18 ?        00:00:00 s6-supervise s6-linux-init-shutdownd
root        18    16  0 May18 ?        00:00:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -c /run/s6/basedir -g 3000 -C -B
root        25     1  0 May18 ?        00:00:00 s6-supervise s6rc-oneshot-runner
root        26     1  0 May18 ?        00:00:00 s6-supervise s6rc-fdholder
root        27     1  0 May18 ?        00:00:00 s6-supervise uranus
root        28     1  0 May18 ?        00:00:00 s6-supervise nginx
root        34    25  0 May18 ?        00:00:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcserver-access -v0 -E -l0 -i data/rules -- /package/admin/s6/command/s6-sudod -t 30000 -- /package/admin/s6-rc/command/s6-rc-oneshot-run -l .
root        69     1  0 May18 ?        00:00:00 s6-supervise uranus
root        70     1  0 May18 ?        00:00:00 s6-supervise nginx
root        71    69  1 May18 ?        00:36:40 /uranus/bin/firewalld-gateway -v 5 --sql-driver=sqlite --config=/uranus/firewalld-gateway.toml
root        72    70  0 May18 ?        00:00:00 nginx: master process nginx -g daemon off;
nginx       74    72  0 May18 ?        00:00:00 nginx: worker process
nginx       75    72  0 May18 ?        00:00:00 nginx: worker process
nginx       76    72  0 May18 ?        00:00:00 nginx: worker process
nginx       77    72  0 May18 ?        00:00:00 nginx: worker process
root        83     0  1 04:17 pts/0    00:00:00 bash
root        90    83  0 04:18 pts/0    00:00:00 ps -ef

Reference

How to run s6-svscan as process 1

Usage

本文发布于Cylon的收藏册,转载请著名原文链接~

链接:https://www.oomkill.com/2023/05/multi-process-management/

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。