如何在一个Docker中同时运行多个程序进程?
我们都知道Docker容器的哲学是一个Docker容器只运行一个进程,但是有时候我们就是需要在一个Docker容器中运行多个进程
那么基本思路是在Dockerfile 的CMD 或者 ENTRYPOINT 运行一个”东西”,然后再让这个”东西”运行多个其他进程 简单说来是用Bash Shell脚本或者三方进程守护 (Monit,Skaware S6,Supervisor),其他没讲到的三方进程守护工具同理
Bash Shell脚本
入口文件运行一个Bash Shell 脚本, 然后在这个脚本内去拉起多个进程 注意最后要增加一个死循环不要让这个脚本退出,否则拉起的进程也退出了 run.sh
#!/bin/bash
# start 1
start1 > /var/log/start1.log 2>&1 &
# start 2
start2 > /var/log/start2.log 2>&1 &
# just keep this script running
while [[ true ]]; do
sleep 1
done
在Dockerfile的入口中运行run.sh
ENTRYPOINT ["run.sh"]
用Bash Shell 的方式,任意发行版的linux都支持,缺点是不能拉起crash的进程,也就是只能拉起运行不能”守护” 如果不关心进程crash问题那么可以用这种方式
三方容器进程初始化之-dumb-init
dumb-init官方 A minimal init system for Linux containers 一个最小化的Linux容器初始化系统 dumb-init是一个简单的进程监控器和init系统,设计为在最小容器环境(如Docker)中作为PID 1运行。它被部署为一个用C编写的小型静态链接二进制文件。 Dockerfile 参考
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD ["/my/script", "--with", "--args"]
ServiceMesh的数据平面Envoy Proxy的容器镜像就是使用的dumb-init
三方容器进程初始化之-tini
tini官方
A tiny but valid init
for containers
容器的一个小而有效的init
三方进程守护之-Monit
Monit和Supervisor还是有很大区别的,Supervisor管理的都是前台执行的进程,Monit既可以管理前台进程也可以管理后台进程,简单的说,在CentOS中使用service xxx start 启动的程序,使用Monit可以直接管理,这就解决了很多没有前台方式启动的程序不能用Supervisor来管理的问题。 Dockerfile 参考
ENTRYPOINT ["/usr/bin/monit","-I"]
三方进程守护之-Supervisor
大名鼎鼎的 Supervisor 如果有多种版本的linux要用那么可以用Supervisor做统一进程守护管理,网上资料一大堆 注意要以前台程序运行,配置文件中要有,如果是后台的方式docker会退出
[supervisord]
nodaemon=true
Dockerfile 参考
ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/youconf.conf"]
三方进程守护之-Skaware S6
Supervisor是常见的进程守护程序,不过程序文件太大,想要容器镜像尽量小,在特别是用Alpine作为基础镜像的时候推荐使用Skaware S6 参考这个微服务基础镜像 https://github.com/nicholasjackson/microservice-basebox 他就是用 Skaware 作为进程守护程序运行多个进程的 如果基础容器镜像是本身就是Alpine,那就再合适不过了 Dockerfile 参考
# skaware s6 daemon runner
RUN mkdir /s6
&& wget --no-check-certificate https://github.com/just-containers/skaware/releases/download/v1.21.2/s6-2.6.1.1-linux-amd64-bin.tar.gz
&& tar -xvzf s6-2.6.1.1-linux-amd64-bin.tar.gz --directory /s6 --strip-components=1
&& mv /s6/bin/* /usr/bin
&& rm -rf s6-2.6.1.1-linux-amd64-bin.tar.gz
&& rm -rf /s6
&& cd /usr/bin/
&& chmod -R 755 s6-accessrules-cdb-from-fs s6-accessrules-fs-from-cdb
s6-applyuidgid s6-cleanfifodir s6-connlimit s6-envdir s6-envuidgid
s6-fdholder-daemon s6-fdholder-delete s6-fdholder-deletec s6-fdholder-getdump
s6-fdholder-getdumpc s6-fdholder-list s6-fdholder-listc s6-fdholder-retrieve
s6-fdholder-retrievec s6-fdholder-setdump s6-fdholder-setdumpc s6-fdholder-store
s6-fdholder-storec s6-fdholder-transferdump s6-fdholder-transferdumpc s6-fdholderd
s6-fghack s6-ftrig-listen s6-ftrig-listen1 s6-ftrig-notify s6-ftrig-wait s6-ftrigrd
s6-ioconnect s6-ipcclient s6-ipcserver s6-ipcserver-access s6-ipcserver-socketbinder
s6-ipcserverd s6-log s6-mkfifodir s6-notifyoncheck s6-setlock s6-setsid s6-setuidgid
s6-softlimit s6-sudo s6-sudoc s6-sudod s6-supervise s6-svc s6-svlisten s6-svlisten1
s6-svok s6-svscan s6-svscanctl s6-svstat s6-svwait s6-tai64n s6-tai64nlocal s6lockd ucspilogd
&& cd -
# 预处理s6配置文件
RUN mkdir -p /etc/s6/.s6-svscan
&& ln -s /bin/true /etc/s6/.s6-svscan/finish
&& mkdir -p /etc/s6/cron
&& mkdir -p /etc/s6/app
&& ln -s /bin/true /etc/s6/cron/finish
&& ln -s /bin/true /etc/s6/app/finish
# corotab 文件内容
ADD cronfile /var/spool/cron/root
# 运行Bash 脚本
ADD cron.run /etc/s6/cron/run
ADD app.run /etc/s6/app/run
ENTRYPOINT ["/usr/bin/s6-svscan","/etc/s6"]
cron.run example
#!/usr/bin/env bash
exec crond -n
app.run example
#!/usr/bin/env bash
exec app
三方进程守护之-s6-overlay
s6-overlay 是基于 Skaware S6适用于容器的进程守护工具 s6-overlay 官网 https://github.com/just-containers/s6-overlay Dockerfile 参考
# Install s6-overlay 进程守护管理.
ENV S6_VERSION v1.21.4.0
RUN curl -L "https://github.com/just-containers/s6-overlay/releases/download/$S6_VERSION/s6-overlay-amd64.tar.gz" > /tmp/s6-overlay-amd64.tar.gz
RUN tar xzf /tmp/s6-overlay-amd64.tar.gz -C / --exclude="./bin" &&
tar xzf /tmp/s6-overlay-amd64.tar.gz -C /usr ./bin
&& rm /tmp/s6-overlay-amd64.tar.gz
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 1
# Set up a standard volume for logs.
VOLUME ["/var/log/services"]
# 设置入口为 s6-based init.
ENTRYPOINT ["/init"]
三方进程守护之-runit
runit官网http://smarden.org/runit/ 具体的使用方法见官网 在Docker生态圈, phusion/baseimage-docker, gitlab 在使用runit作为进程管理工具
下面以要运行cron 和 ssh 为例 /etc/service/ 为配置文件目录
/etc/service/sshd 为要运行的程序目录 /etc/service/sshd/run 为需要运行的程序入口脚本文件 cat run
#!/bin/sh
set -e
exec /usr/sbin/sshd -D
/etc/service/cron 为要运行的程序目录 /etc/service/cron/run 为需要运行的程序入口脚本文件 cat run
#!/bin/sh
exec /usr/sbin/cron -f
Dockerfile 参考
ENTRYPOINT ["/usr/bin/runsvdir","-P","/etc/service"]
三方进程守护之-Systemd
在 docker 中使用 Systemd 需要在 docker run 的时候开启特权模式 –privileged ,所以不推荐 这个直接放弃了 Dockerfile 参考
ENTRYPOINT ["/usr/sbin/init"]
参考资料
Alpine里的go应用,你猜他能有多小? http://blog.csdn.net/sisiy2015/article/details/50350261 如何运行多进程Docker容器? http://dockone.io/article/951 在Docker Container中启动定时任务 http://dockone.io/article/1070 Docker容器内多进程管理(一)-Supervisor http://www.linuxprobe.com/docker-process-management1.html Docker容器内多进程管理(二)-Monit http://www.linuxprobe.com/docker-process-management2.html 关于S6和Runit的论坛讨论 S6 or Runit, not systemd https://www.linuxquestions.org/questions/slackware-14/s6-or-runit-not-systemd-4175465428/ [译] runit 快速入门 https://segmentfault.com/a/1190000006644578
- 关于一般的并查集求根操作的一组对照研究
- vue计算属性详解——小白速会
- 【技巧】Java工程中的Debug信息分级输出接口及部署模式
- 1934: [Shoi2007]Vote 善意的投票
- 算法模板——线段树8 (字符串回文变换)
- 算法模板——哈希单模板字符串匹配
- javascript 原型及原型链详解
- 算法模板——并查集 2(支持快速即时查询本连通块内容,纯原创!)
- 1707: [Usaco2007 Nov]tanning分配防晒霜
- JavaScript 图片的上传前预览(兼容所有浏览器)
- 算法模板——sap网络最大流 3(递归+邻接表)
- BZOJ4819: [Sdoi2017]新生舞会(01分数规划)
- 3401: [Usaco2009 Mar]Look Up 仰望
- javascript 基本概念
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- springmvc之异常处理ResponseStatusExceptionResolver
- Java矩阵快速幂实现
- 走近STL -- 你好,List
- 我能看懂的MakeFile(自命名,多文件,多目标)
- Posix信号量与cond条件变量,到底该选谁?
- 信号量--System V信号量 与 Posix信号量
- 文件空间映射mmap()函数(是什么,为什么,怎么用)
- C++下shm共享内存模块
- 基于TypeScript封装Axios笔记(九)
- springmvc之SessionAttributes注解所引发的异常
- 【tensorflow2.0】处理文本数据-imdb数据
- springmvc之异常处理DefaultHandlerExceptionResolver
- springmvc之返回json类型的数据给前端
- springmvc之mvc:view-controller标签设置可以直接访问的视图
- exec族