本文翻译自 https://docs.docker.com/config/containers/multi-service_container/
我们都知道,所谓的容器就是一个被隔离到不同环境的进程而已,那我们如果在一个容器内运行多个进程呢?
一个容器的主进程是 ENTRYPOINT
或者在 Dockerfile
最后的 CMD
,
容器的主进程负责管理它启动的全部进程。
在某些条件下,主进程由于出错而退出,容器退出时,主进程并不能优雅地处理停止的子进程。
如果你的进程会出现上述情况,你启动容器时可以使用 --init
标志,在容器内插入一个简易的初始化进程作为主进程,容器退出时INIT
处理这些停止的子进程 ---- 子进程作为孤儿进程会被托管到 INIT 进程
用上述方式管理容器的生命周期比使用笨重的 sysinit
, upstart
, 或者 systemd
更优雅
如果需要在一个容器内运行多个服务,你可以通过下面几种不同方式解决。
将全部的命令都封装到脚本中,并将脚本作为 CMD 执行。这是一个非常原生的做法。 比如
#!/bin/bash # Start the first process ./my_first_process -D status=$? if [ $status -ne 0 ]; then echo "Failed to start my_first_process: $status" exit $status fi # Start the second process ./my_second_process -D status=$? if [ $status -ne 0 ]; then echo "Failed to start my_second_process: $status" exit $status fi # Naive check runs checks once a minute to see if either of the processes exited. # This illustrates part of the heavy lifting you need to do if you want to run # more than one service in a container. The container exits with an error # if it detects that either of the processes has exited. # Otherwise it loops forever, waking up every 60 seconds while sleep 60; do ps aux |grep my_first_process |grep -q -v grep PROCESS_1_STATUS=$? ps aux |grep my_second_process |grep -q -v grep PROCESS_2_STATUS=$? # If the greps above find anything, they exit with 0 status # If they are not both 0, then something is wrong if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then echo "One of the processes has already exited." exit 1 fi done
Dockerfile
FROM ubuntu:latest COPY my_first_process my_first_process COPY my_second_process my_second_process COPY my_wrapper_script.sh my_wrapper_script.sh CMD ./my_wrapper_script.sh
如果你先启动主进程但暂时需要在运行其他的进程(比如与主进程交互),那你可以使用 bash 的任务控制解决。
#!/bin/bash # turn on bash's job control set -m # Start the primary process and put it in the background ./my_main_process & # Start the helper process ./my_helper_process # the my_helper_process might need to know how to wait on the # primary process to start before it does its work and returns
now we bring the primary process back into the foreground
and leave it there
fg %1
Dockerfile
FROM ubuntu:latest COPY my_main_process my_main_process COPY my_helper_process my_helper_process COPY my_wrapper_script.sh my_wrapper_script.sh CMD ./my_wrapper_script.sh
- 使用一个像 `supervisord` 一样的进程管理器。这是一个重量级的方法,需要将 `supervisord` 和它的配置文件打包到镜像(或者直接使用基于 `supervisord`的镜像),然后启动supervisor,它会根据配置文件代你管理进程,下面是一个使用 supervisord 的 Dockerfill,我们假定已经预先写好了 `supervisord.conf`,`my_first_process`, `my_second_process`,并放到与 `Dockerfile` 同级的目录
FROM ubuntu:latest RUN apt-get update && apt-get install -y supervisor RUN mkdir -p /var/log/supervisor COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY my_first_process my_first_process COPY my_second_process my_second_process CMD "/usr/bin/supervisord"