本文发布于Cylon的收藏册,转载请著名原文链接~
什么是容器中的多进程管理
在容器中的主进程 (main running process) 是指 Dockerfile中 ENTRYPOINT
或 CMD
指定运行的命令,通常情况下一个进程(服务)为一个容器;也存在一种场景,就是主进程会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.xz
与 s6-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 的职责是清除孤儿进程并监督进程,同时允许管理员添加或删除服务,例如上面的
longrun
与oneshot
类的服务,都是在这个阶段被启动 - 关闭阶段 (shutdown),在此阶段结束时,所有进程都将被终止
- 发送 TERM 信号 到遗留的
longrun
服务,如果需要将等待结束后退出 - 有序的关闭用户 s6-rc
- 运行 finalization 脚本
- 向进程发送
TERM
signal,最终不会留下任何的进程 - sleep一阵,允许驻留的进程退出完
- 发送 KILL 信号,退出所有进程,这时容器退出
- 发送 TERM 信号 到遗留的
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
本文发布于Cylon的收藏册,转载请著名原文链接~
链接:https://www.oomkill.com/2023/05/multi-process-management/
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。