在单一容器中运行多个进程
之前我 Ubuntu镜像(采用tini替代systemd) 等实践中,都采用了 tini 来作为初始化进程管理器,通过脚本来启动多个进程。不过,这种方式需要自己手写启动脚本,相应带来了维护成本。
按照Docker官方文档 Run multiple processes in a container ,确实可以通过脚本包装方式来启动多个进程(先启动的进程通过 & 放到后台),但是脚本包装不如进程管理器,所以需要选择一个轻量级的进程管理器。按照官方文档,推荐的是 supervisor ,所以我现在改进维护,使用 supervisor 来替代早先选择的 tini ,带来如下优势:
supervisor是一个相对规范的启动管理器,通过配置文件来管理多个进程无需编写脚本
提供了稳定的进程管理功能,包括自动重启服务,管理继承祖以及使用
supervisorctl进行交互管理
如果不需要 supervisor 提供的全面的多进程管理,而遵循容器设计的单进程架构,多进程的调度完全采用 Docker Compose 或 Kubernetes ,那么就不需要使用 supervisor ,采用 tini 来解决进程管理更为轻量级且推荐。
supervisor
supervisor 是一个 client/server 系统,用于监控一系列进程,有点类似 launchd , daemontools 和 runit 。不过, supervisor 并不是仅仅作为 process id 1 启动的 init ,而是用于控制和一个项目或用户相关的进程,这意味着 supervisor 实际上可以在启动时类似其他程序一样启动(来管理一组进程)。
在Docker容器中,需要安装 supervisor 并复制配置文件到进行中,这样一旦启动了 supervisord 服务,就能够管理一组程序进程。
备注
我发现在 Alpine Linux 中安装 supervisor 需要依赖安装 25个 软件包,让我实在有点震撼。为了能够更轻量级,我后续在 Alpine Docker镜像 将继续使用 tini 。
仅在 Ubuntu Linux 等重量级容器中尝试实践 supervisor ,仅作为技术积累备用
修订 Debian镜像(tini进程管理器) 的 Dockerfile ,将tini替换成
supervisor
supervisorFROM debian:latest
ENV container=docker
RUN apt update -y
RUN apt upgrade -y
# 安装supervisor
RUN apt install -y supervisor
# Copy supervisord.conf
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN apt -y install sudo passwd openssh-client openssh-server curl
RUN apt -y install nginx
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# add account "admin" and give sudo privilege
RUN groupadd -g 501 admin
RUN useradd -g 501 -u 501 -d /home/admin admin
RUN adduser admin sudo
RUN echo "%sudo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# set TIMEZONE to Shanghai
RUN unlink /etc/localtime
RUN ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mkdir /run/sshd
RUN ssh-keygen -A
# run service when container started - sshd
EXPOSE 22:1122
# Run your program under supervisor
# CMD ["/your/program", "-and", "-its", "arguments"]
CMD ["/usr/bin/supervisord"]
配套提供
supervisord.conf:
supervisord.conf 同时启动nginx和sshd[supervisord]
nodaemon=true ; Run supervisord in the foreground
logfile=/var/log/supervisord.log
pidfile=/var/run/supervisord.pid
[program:sshd]
command=/usr/sbin/sshd -D ; Run sshd in the foreground
; 以下运行nginx参考 https://www.tothenew.com/blog/dockerizing-nginx-and-ssh-using-supervisord/
; 日志直接输出到控制台,以便标准化采集
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout ; Redirect Nginx logs to Docker's log collector
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; 以下段落参考 https://learn.arm.com/learning-paths/servers-and-cloud-computing/supervisord/example/
; 运行脚本以及apache
; [program:remoteit-agent]
; command=/usr/share/remoteit/refresh.sh
; [program:apache2]
; command=/usr/sbin/apache2ctl -DFOREGROUND
创建镜像:
docker build --rm -t debian-ssh-supervisor .
运行容器
docker run -dt --name debian-ssh-supervisor --hostname debian-ssh-supervisor \
-p 1122:22 \
-v /Users/admin/secrets:/home/admin/.ssh \
-v /Users/admin/docs:/home/admin/docs \
debian-ssh-supervisor:latest
# 修正 /home/admin 目录属主权限,因为Lima虚拟机的映射Host主机 /Users/admin 目录后属主是 lima
# 绑定目录到容器以后,如果账号id不能一致,就会导致 /home/admin 目录下映射的 .ssh 和 docs 目录无法读写
# 我原本想使用 docker exec debian-ssh-supervisor chown -R admin:admin /home/admin
# 但是容器内无法修改host主机目录属主
# 最终解决需要统一 Host, Lima, Container 中 admin 账号的 id
# 举例:
# - macOS 的 admin 账号id 是 uid=501(admin) gid=20(staff)
# - Lima 的 lima 账号id 也需要调整为 uid=501(lima) gid=20(dialout)
# - Container 的 admin 账号id 调整为 uid=501(admin) gid=20(dialout)