背景
在最开始使用Docker容器的时候,一直把Docker当作一个Linux虚拟机,后来用久了发现Docker只会起来一个特殊的进程Pid=1,Dockerfile里有两个关键字可以指定指令,ENTRYPOINT
和 CMD
。当主进程退出的时候容器所拥有的PID命名空间就会被销毁,容器生命周期也会结束。
但是有时候需要在Docker容器中运行多个进程,基本做法用shell或者第三方守护进程(dumb-init、tini、Monit、Supervisor、skaware、s6-overlay、runit、Systemd)作为主进程运行其他程序。
S6介绍
S6使用
这里主要介绍s6-overlay使用
- s6-overlay生命周期:
- step1:容器初始化会启动s6-svscan作为pid1
- step2:s6-svscan扫描服务目录并由s6-supervise执行相应脚本和服务
- 1、使用修复所有权和权限/etc/fix-attrs.d
- 2、执行中包含的初始化脚本/etc/cont-init.d
- 3、将服务(/etc/services.d)复制到s6文件夹下,并向s6-supervise发出信号,并交由s6-supervise接管。
- step3:收到
docker stop
,转送s6-supervise进程,停止服务并执行终结脚本/etc/cont-finish.d
,确保内置服务不会成为僵尸进程。
依照文档描述,系统启动会扫描几个文件夹
/etc/fix-attrs.d
: 权限脚本文件夹
/etc/cont-init.d
:初始化脚本文件夹
/etc/services.d
: 服务启动执行脚本文件夹
/etc/cont-finish.d
: 服务结束执行脚本文件夹
脚本例子
权限脚本
linux下执行程序需要很多权限授权,例如文件挂载后需要对挂载文件进行赋予权限等,需要在容器启动后操作。s6-overlay提供了初始化脚本文件夹/etc/fix-attrs.d
。匹配格式如下:
1
| path(路径) recurse(是否嵌套) account(用户名) fmode(文件模式) dmode(目录模式)
|
path
: 文件或路径
recurse
: 如果时文件夹,是否嵌套所有子文件
account
: 用户名。如果用户没找到就按照默认UID和GID
fmode
: 文件模式。如:0644
dmode
: 目录模式。如:0755
举例:
1
| /var/lib/mysql true mysql 0600 0700
|
初始化脚本
执行完权限脚本(/etc/fix-attrs.d/
)后,在启动服务(/etc/services.d/
)前可以做一些准备工作,如环境变量设置、文件夹创建等。
举例:
/etc/cont-init.d/02-confd-onetime
:
1 2 3 4 5 6 7 8 9 10 11 12
| #!/usr/bin/execlineb -P
with-contenv s6-envuidgid nginx multisubstitute { import -u -D0 UID import -u -D0 GID import -u CONFD_PREFIX define CONFD_CHECK_CMD "/usr/sbin/nginx -t -c {{ .src }}" } confd --onetime --prefix="${CONFD_PREFIX}" --tmpl-uid="${UID}" --tmpl-gid="${GID}" --tmpl-src="/etc/nginx/nginx.conf.tmpl" --tmpl-dest="/etc/nginx/nginx.conf" --tmpl-check-cmd="${CONFD_CHECK_CMD}" etcd
|
服务启动脚本
/etc/services.d/myapp/run
:
1 2 3
| #!/usr/bin/execlineb -P
nginx -g "daemon off;"
|
守护进程默认是自动重启服务,如果想要不自动重启可以在finish脚本里写终止
/etc/services.d/myapp/finish
:
1 2 3
| #!/usr/bin/execlineb -S0
s6-svscanctl -t /var/run/s6/services
|
/etc/services.d/myapp/finish
:
1 2 3 4 5
| #!/usr/bin/execlineb -S1 if { s6-test ${1} -ne 0 } if { s6-test ${1} -ne 256 }
s6-svscanctl -t /var/run/s6/services
|
日志输出
s6-overlay采用s6已经提供了开箱即用的s6-log,在这基础上提供了进一步封装logutil-service
,主要提供以下几个功能
- 在s6-log中执行环境变量
S6_LOGGING_SCRIPT
中的脚本
- 移除权限,任何人都可以写文件,不需要再通过
s6-setuidgid
来启动
- 清除所有环境变量
- 初始化s6-log日志程序
在初始化时候创建日志文件夹和任意操作权限
/etc/cont-init.d/myapp-logfolder
:
1 2 3
| #!/bin/sh mkdir -p /var/log/myapp chown nobody:nogroup /var/log/myapp
|
输出stdin所有日志
/etc/services.d/myapp/log/run
:
1 2
| #!/bin/sh exec logutil-service /var/log/myapp
|
Docker基础镜像
Dockerfile文件参考smebberson/docker-alpine,我增加了镜像时区设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| FROM alipine:3.9 LABEL [email protected]
ENV S6_OVERLAY_VERSION=v1.22.1.0 \ GO_DNSMASQ_VERSION=1.0.7 \ TIME_ZONE=Asia/Shanghai
RUN apk add --update --no-cache bind-tools curl libcap && \ curl -sSL https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-amd64.tar.gz \ | tar xfz - -C / && \ curl -sSL https://github.com/janeczku/go-dnsmasq/releases/download/${GO_DNSMASQ_VERSION}/go-dnsmasq-min_linux-amd64 -o /bin/go-dnsmasq && \ chmod +x /bin/go-dnsmasq && \ apk del curl && \ addgroup go-dnsmasq && \ adduser -D -g "" -s /bin/sh -G go-dnsmasq go-dnsmasq && \ setcap CAP_NET_BIND_SERVICE=+eip /bin/go-dnsmasq
RUN ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime && \ echo "${TIME_ZONE}" > /etc/timezone
COPY root /
ENTRYPOINT ["/init"] CMD []
|
root
文件下目录结构
1 2 3 4 5 6 7 8 9 10 11
| ├─ configroot │ ├─etc │ │ ├─fix.attrs.d │ │ │ ├─01-resolver-resolv │ │ ├─cont.init.d │ │ │ ├─30-resolver │ │ │ ├─40-resolver │ │ ├─services.d │ │ │ ├─resolver │ │ │ │ ├─run │ │ │ │ ├─finish
|
构建S6程序
Dockerfile
这里拿Hexo作为守护服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| FROM dobor/alpine-base:latest LABEL [email protected]
ENV HEXO_MODE=server
RUN addgroup hexo && \ adduser -D -g "" -s /bin/sh -G hexo hexo
WORKDIR /home/hexo
RUN apk --update --no-progress --no-cache add git nodejs npm openssh && \ npm config set registry https://registry.npm.taobao.org && \ npm install -g hexo-cli && \ hexo init . && \ npm install hexo-deployer-git && \ npm install hexo-tag-aplayer && \ npm install [email protected] && \ npm install hexo-renderer-pug && \ npm install hexo-renderer-stylus && \ npm install hexo-wordcount && \ npm install hexo-abbrlink && \ rm -rf /var/cache/apk/*
ADD root /
VOLUME /home/hexo/source /home/hexo/themes /home/hexo/.ssh
RUN chown -R hexo .
EXPOSE 4000
|
目录结构
新增/root
文件结构
1 2 3 4 5 6 7 8 9 10
| ├─ configroot │ ├─etc │ │ ├─fix.attrs.d │ │ │ ├─02-hexo │ │ ├─cont.init.d │ │ │ ├─10-hexo │ │ │ ├─20-hexo │ │ ├─services.d │ │ │ ├─hexo │ │ │ │ ├─run
|
权限
给git
配置文件.config
赋予访问权限
/etc/fix-attrs.d/02-hexo
:
1
| /root/.config true root 0666 0666
|
初始化
设置执行命令的HEXO_RUNAS
环境变量
/etc/cont-init.d/10-hexo
:
1 2 3 4 5 6
| #!/usr/bin/with-contenv sh
if [ -z "$HEXO_RUNAS" ]; then printf "hexo" > /var/run/s6/container_environment/HEXO_RUNAS fi
|
生成博客静态文件和启用插件
/etc/cont-init.d/20-hexo
:
1 2 3 4 5 6 7
| #!/usr/bin/with-contenv sh
exec s6-setuidgid $HEXO_RUNAS hexo g -f --cwd /home/hexo
exec s6-setuidgid $HEXO_RUNAS hexo douban --cwd /home/hexo
|
服务启动
通过环境变量HEXO_MODE来作为web站点服务还是发布静态文件到GitHub上。
/etc/services.d/run
:
1 2 3 4 5 6 7 8 9 10 11
| #!/usr/bin/with-contenv sh
if [ $HEXO_MODE = 's' ] || [ $HEXO_MODE = 'server' ]; then exec s6-setuidgid $HEXO_RUNAS hexo server -p 4000 --cwd /home/hexo fi
if [ $HEXO_MODE = 'd' ] || [ $HEXO_MODE = 'deploy' ]; then exec s6-setuidgid $HEXO_RUNAS hexo deploy --cwd /home/hexo fi
|
总结
通过把s6整合进hexo镜像内部,使得hexo容器更新一个稳定的微型linux服务器,既保留容器占用资源低,启动快等特点,又使得容器内部可以执行多进程,可以整合更复杂的功能于一个容器内。