<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/ DTD/wml_1.1.xml">

<wml>
<head>
<meta http-equiv="cache-control" content="max-age=180,private" />
</head>
<card title="Docker中应用s6-overlay">
<p>
作者:<a href="index.php?action=showuser&amp;userid=1&amp;hash=">admin</a><br />时间:2021-12-14 14:57<br />分类:<a href="index.php?action=list&amp;cid=3&amp;hash=">电脑技术</a><br />内容:
Docker中应用s6-overlay

背景

在最开始使用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 是skarnet（Laurent Bercot） 开发的一款轻量级守护进程套件

s6-overlay

s6-overlay 是容器内部初始化s6的工具包，我在搜索基础镜像的时候在github上发现，看到readme描述我就被吸引了。

文档

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。匹配格式如下：

path(路径) recurse(是否嵌套) account(用户名) fmode(文件模式) dmode(目录模式)

path: 文件或路径

recurse: 如果时文件夹，是否嵌套所有子文件

account: 用户名。如果用户没找到就按照默认UID和GID

fmode: 文件模式。如：0644

dmode: 目录模式。如：0755

举例：



XML/HTML代码


    /var/lib/mysql true mysql 0600 0700




初始化脚本

执行完权限脚本(/etc/fix-attrs.d/)后，在启动服务(/etc/services.d/)前可以做一些准备工作，如环境变量设置、文件夹创建等。

举例：

/etc/cont-init.d/02-confd-onetime:



XML/HTML代码


    #!/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 &quot;/usr/sbin/nginx -t -c {{ .src }}&quot;  

    }  

    confd --onetime --prefix=&quot;${CONFD_PREFIX}&quot; --tmpl-uid=&quot;${UID}&quot; --tmpl-gid=&quot;${GID}&quot; --tmpl-src=&quot;/etc/nginx/nginx.conf.tmpl&quot; --tmpl-dest=&quot;/etc/nginx/nginx.conf&quot; --tmpl-check-cmd=&quot;${CONFD_CHECK_CMD}&quot; etcd  






服务启动脚本

自定义启动脚本

/etc/services.d/myapp/run:



XML/HTML代码


    #!/usr/bin/execlineb -P

    nginx -g &quot;daemon off;&quot;






自定义重启策略

守护进程默认是自动重启服务，如果想要不自动重启可以在finish脚本里写终止

/etc/services.d/myapp/finish:



XML/HTML代码


    #!/usr/bin/execlineb -S0  

    s6-svscanctl -t /var/run/s6/services  






可以实现更高级做法，当服务崩溃，就不再重启

/etc/services.d/myapp/finish:



XML/HTML代码


    #!/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:



XML/HTML代码


    #!/bin/sh  

    mkdir -p /var/log/myapp  

    chown nobody:nogroup /var/log/myapp






输出stdin所有日志

/etc/services.d/myapp/log/run:



XML/HTML代码


    #!/bin/sh  

    exec logutil-service /var/log/myapp  






Docker基础镜像

Dockerfile文件参考smebberson/docker-alpine，我增加了镜像时区设置

DOCKERFILE



XML/HTML代码


    FROM alipine:3.9  

    LABEL MAINTAINER=chobon@aliyun.com  

    # Add s6-overlay  

    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 &amp;&amp; \  

        curl -sSL https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-amd64.tar.gz \  

        | tar xfz - -C / &amp;&amp; \  

        curl -sSL https://github.com/janeczku/go-dnsmasq/releases/download/${GO_DNSMASQ_VERSION}/go-dnsmasq-min_linux-amd64 -o /bin/go-dnsmasq &amp;&amp; \  

        chmod +x /bin/go-dnsmasq &amp;&amp; \  

        apk del curl &amp;&amp; \  

        # create user and give binary permissions to bind to lower port  

        addgroup go-dnsmasq &amp;&amp; \  

        adduser -D -g &quot;&quot; -s /bin/sh -G go-dnsmasq go-dnsmasq &amp;&amp; \  

        setcap CAP_NET_BIND_SERVICE=+eip /bin/go-dnsmasq  

    RUN ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime &amp;&amp; \  

        echo &quot;${TIME_ZONE}&quot; &gt; /etc/timezone  

    COPY root /  

    ENTRYPOINT [&quot;/init&quot;]  

    CMD []  






root文件下目录结构



XML/HTML代码


    ├─ configroot  

    │  ├─etc  

    │  │  ├─fix.attrs.d  

    │  │  │  ├─01-resolver-resolv  

    │  │  ├─cont.init.d  

    │  │  │  ├─30-resolver  

    │  │  │  ├─40-resolver  

    │  │  ├─services.d  

    │  │  │  ├─resolver  

    │  │  │  │  ├─run  

    │  │  │  │  ├─finish  






构建S6程序

这里拿Hexo作为守护服务

DOCKERFILE



XML/HTML代码


    FROM dobor/alpine-base:latest  

    LABEL MAINTAINER=chobon@aliyun.com  

      

    ENV HEXO_MODE=server  

      

    # change ALIYUN apk source  

    #RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories  

      

    RUN addgroup hexo &amp;&amp; \  

        adduser -D -g &quot;&quot; -s /bin/sh -G hexo hexo  

      

    WORKDIR /home/hexo  

      

    RUN apk --update --no-progress --no-cache add git nodejs npm openssh &amp;&amp; \  

        npm config set registry https://registry.npm.taobao.org &amp;&amp; \  

        npm install -g hexo-cli &amp;&amp; \  

        hexo init . &amp;&amp; \  

        npm install hexo-deployer-git &amp;&amp; \  

        #npm install hexo-generator-json-content &amp;&amp; \  

        npm install hexo-tag-aplayer &amp;&amp; \  

        npm install cheerio@0.22.0 &amp;&amp; \  

        npm install hexo-renderer-pug &amp;&amp; \  

        npm install hexo-renderer-stylus &amp;&amp; \  

        npm install hexo-wordcount &amp;&amp; \  

        npm install hexo-abbrlink &amp;&amp; \  

        rm -rf /var/cache/apk/*  

      

    # copy local files  

    ADD root /  

      

    VOLUME /home/hexo/source /home/hexo/themes /home/hexo/.ssh  

      

    RUN chown -R hexo .  

      

    EXPOSE 4000  






目录结构

新增/root文件结构



XML/HTML代码


    ├─ 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:



XML/HTML代码


    /root/.config true root 0666 0666  






初始化

设置执行命令的HEXO_RUNAS环境变量

/etc/cont-init.d/10-hexo:



XML/HTML代码


    #!/usr/bin/with-contenv sh  

    # Unless this has already been defined, set it.  

    if [ -z &quot;$HEXO_RUNAS&quot; ]; then  

        printf &quot;hexo&quot; &gt; /var/run/s6/container_environment/HEXO_RUNAS  

    fi  






生成博客静态文件和启用插件

/etc/cont-init.d/20-hexo:



XML/HTML代码


    #!/usr/bin/with-contenv sh  

    # Generate Blog  

    exec s6-setuidgid $HEXO_RUNAS hexo g -f --cwd /home/hexo  

    # Generate Douban Page  

    exec s6-setuidgid $HEXO_RUNAS hexo douban --cwd /home/hexo  






服务启动

通过环境变量HEXO_MODE来作为web站点服务还是发布静态文件到GitHub上。

/etc/services.d/run:



XML/HTML代码


    #!/usr/bin/with-contenv sh  

    if [ $HEXO_MODE = 's' ] || [ $HEXO_MODE = 'server' ]; then  

        # Start Hexo Server.  

        exec s6-setuidgid $HEXO_RUNAS hexo server -p 4000 --cwd /home/hexo  

    fi  

    if [ $HEXO_MODE = 'd' ] || [ $HEXO_MODE = 'deploy' ]; then  

        # Start Hexo Deploy.  

        exec s6-setuidgid $HEXO_RUNAS hexo deploy --cwd /home/hexo  

    fi  






总结：

通过把s6整合进hexo镜像内部，使得hexo容器更新一个稳定的微型linux服务器，既保留容器占用资源低，启动快等特点，又使得容器内部可以执行多进程，可以整合更复杂的功能于一个容器内。



参考 参考 Github Dockerhub：alpine-s6 debian-s6 ubuntu-s6 s6-overlay

简单来说，在容器中安装的某个程序不能自动启动，可以通过s6写一个启动命令进去，从而启动系统的某个服务或者程序。

例如ssh server安装后重启容器就不能启动了，可以创建开机启动脚本/etc/cont-init.d/001-ssh.sh从而实现开机启动ssh server服务



XML/HTML代码


    echo '#!/bin/bash' &gt; /etc/cont-init.d/001-ssh.sh

    echo '/etc/init.d/ssh start' &gt;&gt; /etc/cont-init.d/001-ssh.sh

    chmod +x /etc/cont-init.d/001-ssh.sh




想启动其他程序可以自己写入启动命令。
</p><p>
<a href="index.php?action=login&amp;hash=">立即登陆发表评论</a><br />
</p>
<p><a href="index.php?action=list&amp;hash=">返回日志列表</a><br /><a href="index.php?action=index&amp;hash=">返回主页</a></p>
</card>
</wml>
