目录
CMD
s6-overlay 是一组易于安装(只需提取一两个 tarball!)的脚本和实用程序,允许您使用现有的 Docker 映像,同时使用 s6 作为容器的 pid 1 和服务的进程管理程序。
构建以下 Dockerfile 并尝试一下:
# Use your favorite image
FROM ubuntu
ARG S6_OVERLAY_VERSION=3.2.0.2
RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t demo .
docker-host $ docker run --name s6demo -d -p 80:80 demo
docker-host $ docker top s6demo acxf
PID TTY STAT TIME COMMAND
11735 ? Ss 0:00 _ s6-svscan
11772 ? S 0:00 _ s6-supervise
11773 ? Ss 0:00 | _ s6-linux-init-s
11771 ? Ss 0:00 _ rc.init
11812 ? S 0:00 | _ nginx
11814 ? S 0:00 | _ nginx
11816 ? S 0:00 | _ nginx
11813 ? S 0:00 | _ nginx
11815 ? S 0:00 | _ nginx
11779 ? S 0:00 _ s6-supervise
11785 ? Ss 0:00 | _ s6-ipcserverd
11778 ? S 0:00 _ s6-supervise
docker-host $ curl --head http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 17 Jan 2022 13:33:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 17 Jan 2022 13:32:11 GMT
Connection: keep-alive
ETag: "61e56fdb-264"
Accept-Ranges: bytes
如果您要从以前版本的 s6-overlay ( v2 ) 迁移到新版本 ( v3 ),您可能需要对您的服务或使用 s6-overlay 的方式进行一些更改,以便一切顺利进行。本文档试图准确说明 v3 的工作原理,但我们有一个单独的页面列出了主要差异以及您可能会注意到的事情。如果您遇到这种情况,请阅读本文!
该项目有以下目标:
cont-init.d
)、终结 ( cont-finish.d
) 等任务以及他们自己的服务以及它们之间的依赖关系PID 1
功能s6
和s6-portable-utils
中包含一整套实用程序。它们包括方便且可组合的实用程序,使我们的生活变得更加轻松。logutil-service
进行开箱即用的日志轮换,该服务在后台使用s6-log
。USER
指令的一些支持,以特定用户身份运行整个进程树。不兼容所有功能,详细信息请参见注释部分。 经常重复的 Docker 口头禅之一是“每个容器一个进程”,但我们不同意。在容器中运行多个进程本身并没有什么坏处。更抽象的“每个容器做一件事”是我们的政策——容器应该做一件事,例如“运行聊天服务”或“运行 gitlab”。这可能涉及多个进程,这很好。
镜像作者回避进程管理器的另一个原因是他们相信进程管理器必须重新启动失败的服务,这意味着 Docker 容器永远不会死。
这确实有效地破坏了 Docker 生态系统——大多数镜像运行一个进程,当出现错误时该进程就会退出。通过出错退出,您允许系统管理员按照他们喜欢的方式处理故障。如果您的图像永远不会退出,您现在需要一些错误恢复和失败通知的替代方法。
我们的政策是,如果“事物”失败,那么容器也应该失败。我们通过确定哪些进程可以重新启动以及哪些进程应该关闭容器来做到这一点。例如,如果cron
或syslog
失败,您的容器很可能会重新启动它而不会产生任何不良影响,但如果ejabberd
失败,容器应该退出,以便系统管理员可以采取行动。
我们对“Docker Way”的解释是:
我们的 init 系统就是为了做到这一点而设计的。您的映像将像其他 Docker 映像一样运行,并适合现有的映像生态系统。
有关停止“事物”的详细信息,请参阅“用法”部分下的“编写可选的完成脚本”。
我们的覆盖初始化是经过适当定制的,可以在容器化环境中正确运行。本节简要介绍了各个阶段的工作原理,但如果您想了解完整的 init 系统应如何工作,您可以阅读这篇文章:如何将 s6-svscan 作为进程 1 运行
/etc/cont-init.d
中包含的旧版 oneshot 用户脚本。/etc/s6-overlay/s6-rc.d
中声明的用户 s6-rc 服务,遵循依赖关系/etc/services.d
) 复制到临时目录并让 s6 启动(并监督)它们。/etc/cont-finish.d
中包含的任何终止脚本。TERM
信号。无论如何,不应该有任何剩余的进程。KILL
信号。然后容器退出。 s6-overlay 作为一组 tarball 提供,您可以将其提取到图像上。您需要的 tarball 是您使用的映像的函数;大多数人都需要前两个,其他的是您可以在方便时使用的额外功能。
s6-overlay-noarch.tar.xz
:此 tarball 包含实现覆盖的脚本。我们称之为“noarch”,因为它是独立于体系结构的:它只包含脚本和其他文本文件。每个想要运行 s6-overlay 的人都需要提取这个 tarball。s6-overlay-x86_64.tar.xz
:将x86_64
替换为您的系统架构。该 tarball 包含 s6 生态系统中所有必需的二进制文件,所有文件都是静态链接的,并且不影响图像的二进制文件。除非您确定您的映像已附带提供覆盖中使用的二进制文件的所有软件包,否则您需要提取此 tarball。s6-overlay-symlinks-noarch.tar.xz
:此 tarball 包含 s6-overlay 脚本的符号链接,因此可以通过/usr/bin
访问它们。通常不需要,所有脚本都可以通过 PATH 环境变量访问,但是如果您有包含 shebang 的旧用户脚本,例如#!/usr/bin/with-contenv
,安装这些符号链接将使它们正常工作。s6-overlay-symlinks-arch.tar.xz
:此 tarball 包含指向第二个 tarball 提供的 s6 生态系统中的二进制文件的符号链接,以便通过/usr/bin
访问它们。通常不需要,但如果您有包含 shebangs 的旧用户脚本,例如#!/usr/bin/execlineb
,安装这些符号链接将使它们正常工作。syslogd-overlay-noarch.tar.xz
:此 tarball 包含syslogd
服务的定义。如果您运行的守护进程无法登录到 stderr 以利用 s6 日志记录基础设施,但对旧syslog()
机制的使用进行了硬编码,则可以提取此 tarball,并且您的容器将运行syslogd
守护进程的轻量级模拟,因此您的系统日志将被捕获并存储到磁盘上。要安装这些 tarball,请在 Dockerfile 中添加与要安装的功能相对应的行。例如,大多数人会使用以下内容:
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
确保在提取时保留文件权限(即使用-p
选项来tar
。)
该项目作为一组标准 .tar.xz 文件分发,您可以将其提取到映像的根目录中。 (您需要tar
的 xz-utils 包才能理解.tar.xz
文件;它在每个发行版中都可用,但并不总是在默认容器映像中,因此您可能需要apt install xz-utils
或apk add xz
,或者等价,然后才能扩展档案。)
然后,将ENTRYPOINT
设置为/init
。
现在,我们建议使用 Docker 的ADD
指令,而不是在RUN
指令中运行wget
或curl
- 当您使用ADD
时,Docker 能够处理 https URL,而您的基础映像可能无法使用 https,或者甚至可能没有wget
或curl
已安装。
从那里,您有几个选择:
CMD
方式运行该程序。CMD
使用CMD
是利用叠加层的便捷方法。您的CMD
可以在构建时在 Dockerfile 中给出,也可以在运行时在命令行中给出,无论哪种方式都可以。它将在s6设置的环境中作为正常进程运行;当它失败或退出时,容器将干净地关闭并退出。您可以通过这种方式运行交互式程序:只有 CMD 会收到您的交互式命令,支持进程将不受影响。
例如:
FROM busybox
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t s6demo .
docker-host $ docker run -ti s6demo /bin/sh
/package/admin/s6-overlay/libexec/preinit: notice: /var/run is not a symlink to /run, fixing it
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
/ # ps
PID USER TIME COMMAND
1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service
17 root 0:00 {rc.init} /bin/sh -e /run/s6/basedir/scripts/rc.init top /bin/sh
18 root 0:00 s6-supervise s6-linux-init-shutdownd
20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -c /run/s6/basedir -g 3000 -C -B
24 root 0:00 s6-supervise s6rc-fdholder
25 root 0:00 s6-supervise s6rc-oneshot-runner
31 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcserver-access -v0 -E -l0 -i data/rules -- /packa
58 root 0:00 /bin/sh
66 root 0:00 ps
/ # exit
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
docker-host $
使用具有 s6-overlay 的容器的另一种方法是对您的服务进行监督。您可以监督任意数量的服务;通常它们只是作为 CMD 运行的主守护进程的支持服务,但如果这就是您想要的,那么没有什么可以阻止您拥有一个空的 CMD 并将主守护进程也作为受监督的服务运行。在这种情况下,守护进程每当退出时都会被 s6 重新启动;仅当您通过docker stop
命令或在容器内部使用/run/s6/basedir/bin/halt
命令告诉容器这样做时,容器才会停止。
有两种方式提供监督服务。仍然支持的旧方法是创建“纯 s6”服务目录。在/etc/services.d
中创建一个以您的服务名称命名的目录,并将可执行run
文件放入其中;这是您将在其中放置长期进程执行的文件。有关服务目录监管的详细信息,以及如何配置 s6 处理守护进程的方式,您可以查看 servicedir 文档。一个简单的例子如下:
/etc/services.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
新的方法是在/etc/s6-overlay/s6-rc.d
目录下创建一个 s6-rc源定义目录,并将该目录的名称添加到user
包中,即创建一个同名的空文件在/etc/s6-overlay/s6-rc.d/user/contents.d
目录中。本页描述了源定义目录的格式。请注意,您可以定义longruns ,即将由 s6 监督的守护进程,就像使用/etc/services.d
方法一样,但也可以定义oneshots ,即将运行一次并退出的程序。您的主要服务可能是长期运行的,而不是一次性的:您可能需要一个守护进程来保留。
这种新格式的优点是它允许您定义服务之间的依赖关系:如果B依赖于A ,那么A将首先启动,然后当A准备好时B将启动,当容器被告知退出时, B将停止首先,然后A 。如果您有一个复杂的架构,其中各种流程相互依赖,或者您必须以精确的顺序混合单次运行和长期运行,那么这可能适合您。
上面的例子可以这样重写:
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp
:空文件。 (这会将myapp
添加到 s6-rc 将在容器启动时启动的服务集中。)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
:空文件。 (这告诉 s6-rc 仅在所有基本服务准备就绪时才启动myapp
:它可以防止竞争条件。)
我们鼓励您切换到新格式,但如果您不需要它的好处,您可以坚持使用/etc/services.d
中的常规服务目录,它也能正常工作。
如果您将主服务作为 CMD 运行,则无需执行任何操作:当您的 CMD 退出时,或者当您运行docker stop
时,容器将自然退出,并使用与您的服务相同的退出代码。 (但是请注意,在docker stop
情况下,您的服务将收到 SIGTERM,在这种情况下,退出代码将完全取决于您的服务如何处理它 - 它可能会捕获它并退出 0,捕获它并退出其他内容,或者不捕获它并让 shell 退出它自己的代码 - 通常为 130。)
但是,如果您将主服务作为受监督服务运行,则情况有所不同,并且当您向容器发送docker stop
命令时,您需要告诉容器使用什么代码退出。为此,您需要编写一个finish
脚本:
/etc/services.d
中的旧服务,则需要一个可执行的/etc/services.d/myapp/finish
脚本。/etc/s6-overlay/s6-rc.d/myapp/finish
文件(该文件可能可执行,也可能不可执行)。该finish
脚本将在您的服务退出时运行,并将采用两个参数:
在finish
脚本中,您需要将所需的容器退出代码写入/run/s6-linux-init-container-results/exitcode
文件 - 就是这样。
例如,上面myapp
服务的finish
脚本可能如下所示:
#! /bin/sh
if test " $1 " -eq 256 ; then
e= $(( 128 + $2 ))
else
e= " $1 "
fi
echo " $e " > /run/s6-linux-init-container-results/exitcode
当您向容器发送docker stop
命令时, myapp
服务将被终止,并且该脚本将运行;它会将myapp
的退出代码(如果myapp
捕获 TERM 信号)或 130(如果myapp
没有捕获 TERM 信号)写入特殊的/run/s6-linux-init-container-results/exitcode
文件,该文件将在容器关闭过程结束时由 s6-overlay 读取,您的容器将以该值退出。
本节介绍 v3之前的s6-overlay 版本的功能。 fix-attrs 在 v3 中仍然受支持,但已被弃用,原因有几个:其中之一是,当可以静态完成时,动态更改所有权通常不是一个好的策略。另一个原因是它不适用于 USER 容器。我们现在建议您在运行容器之前处理脱机挂载的主机的所有权和权限,而不是修复属性。当您拥有所有需要的信息时,这应该在您的 Dockerfile 中完成。
也就是说,这是我们为以前的版本编写的内容,今天仍然适用(但请停止依赖它):
有时,在继续之前修复所有权和权限很有趣,因为例如,您已经在容器内安装/映射了主机文件夹。我们的覆盖提供了一种使用/etc/fix-attrs.d
中的文件来解决此问题的方法。这是修复属性文件后面的模式格式:
path recurse account fmode dmode
path
:文件或目录路径。recurse
:(设置为true
或false
)如果找到文件夹,则递归遍历其中包含的所有文件和文件夹。account
:目标帐户。如果找不到帐户,可以默认使用后备uid:gid
。例如, nobody,32768:32768
会首先尝试使用nobody
帐户,然后回退到uid 32768
。例如,如果daemon
帐户是UID=2
和GID=2
,则account
字段的可能值如下:daemon: UID=2 GID=2
daemon,3:4: UID=2 GID=2
2:2,3:4: UID=2 GID=2
daemon:11111,3:4: UID=2 GID=11111
11111:daemon,3:4: UID=11111 GID=2
daemon:daemon,3:4: UID=2 GID=2
daemon:unexisting,3:4: UID=2 GID=4
unexisting:daemon,3:4: UID=3 GID=2
11111:11111,3:4: UID=11111 GID=11111
fmode
:目标文件模式。例如, 0644
。dmode
:目标目录/文件夹模式。例如, 0755
。这里有一些工作示例:
/etc/fix-attrs.d/01-mysql-data-dir
:
/var/lib/mysql true mysql 0600 0700
/etc/fix-attrs.d/02-mysql-log-dirs
:
/var/log/mysql-error-logs true nobody,32768:32768 0644 2700
/var/log/mysql-general-logs true nobody,32768:32768 0644 2700
/var/log/mysql-slow-query-logs true nobody,32768:32768 0644 2700
这是旧的做法:
修复属性后(通过/etc/fix-attrs.d/
)和启动用户提供的服务之前(通过 s6-rc 或/etc/services.d
),我们的覆盖层将执行/etc/cont-init.d
,例如:
/etc/cont-init.d/02-confd-onetime
:
#!/command/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/s6-overlay/s6-rc.d
中添加服务定义目录,将一次性初始化和完成任务编写为 s6-rc 服务,使它们成为一部分user
捆绑包(因此它们实际上在容器启动时启动),并使它们依赖于base
捆绑包(因此它们仅在base
之后启动)。
有关 s6-rc 的所有信息都可以在这里找到。
容器启动时,按以下顺序执行操作:
/etc/fix-attrs.d
中的文件执行属性修复。/etc/cont-init.d
中的一次性初始化脚本按顺序运行。user
包中的服务由 s6-rc 按照依赖项定义的顺序启动。服务可以是一次性(初始化任务)或长期运行(将在容器的整个生命周期中运行的守护进程)。如果服务依赖于base
,则保证它们在此时启动,而不是更早;如果不这样做,它们可能会更早开始,这可能会导致竞争条件 - 因此建议始终使它们依赖于base
。/etc/services.d
中的长期服务。user2
捆绑包中具有正确依赖关系的服务将启动。 (大多数人不需要使用它;如果您不确定,请坚持user
包。)当容器停止时,无论是因为管理员发送停止命令还是因为 CMD 退出,操作都会以相反的顺序执行:
user2
捆绑包中具有正确依赖关系的服务将停止。/etc/services.d
中的长期运行服务已停止。down
脚本;这就是 s6-rc 执行终结任务的方式。/etc/cont-finish.d
中的一次性完成脚本按顺序运行。 user2
捆绑包的目的是允许其中声明的用户服务在/etc/services.d
服务之后启动;但为了做到这一点, user2
中的每个服务都需要声明对legacy-services
依赖。换句话说,要使foobar
服务延迟启动,您需要:
/etc/s6-overlay/s6-rc.d/foobar
中定义它。/etc/s6-overlay/s6-rc.d/foobar/dependencies.d/legacy-services
文件/etc/s6-overlay/s6-rc.d/user2/contents.d/foobar
文件。这将确保foobar
将在/etc/services.d
中的所有内容之后启动。
默认情况下,在/etc/services.d
中创建的服务将自动重新启动。如果某个服务应该关闭容器,您可能应该将其作为 CMD 运行;但如果您希望将其作为受监督的服务运行,那么您需要编写一个finish
脚本,该脚本将在服务关闭时运行;要使容器停止,必须调用/run/s6/basedir/bin/halt
命令。这是一个完成脚本示例:
/etc/services.d/myapp/finish
:
#!/command/execlineb -S0
foreground { redirfd -w 1 /run/s6-linux-init-container-results/exitcode echo 0 }
/run/s6/basedir/bin/halt
脚本的第一行将0
写入/run/s6-linux-init-container-results/exitcode
文件。第二行停止容器。当您通过从容器内部运行的/run/s6/basedir/bin/halt
命令停止容器时,将读取/run/s6-linux-init-container-results/exitcode
并将其内容用作退出代码启动容器的docker run
命令。如果文件不存在,或者容器因docker stop
或其他原因而停止,则退出代码默认为 0。
可以在完成脚本中执行更高级的操作。例如,下面的脚本仅在服务非零退出时关闭服务:
/etc/services.d/myapp/finish
:
#!/command/execlineb -S1
if { eltest ${1} -ne 0 -a ${1} -ne 256 }
/run/s6/basedir/bin/halt
请注意,一般来说,结束脚本只能用于守护进程终止后的本地清理。如果某个服务非常重要,以至于容器在死亡时需要停止,我们强烈建议将其作为 CMD 运行。
每个服务都可以有其专用的记录器。记录器是一个 s6 服务,它自动从服务的标准输出中读取数据,并将数据记录到您想要的位置的自动旋转文件中。请注意,守护进程通常会记录到 stderr,而不是 stdout,因此您应该在 shell 中使用exec 2>&1
或在 execline 中使用fdmove -c 2 1
启动服务的运行脚本,以便捕获stderr 。
s6-overlay 提供了一个名为logutil-service
实用程序,它是s6-log
程序的包装器。该助手执行以下操作:
S6_LOGGING_SCRIPT
中包含的日志记录脚本nobody
用户(如果不存在则默认为65534:65534
)然后 s6-log 将永远运行,从您的服务读取数据并将其写入您为logutil-service
指定的目录。
请注意:
s6-setuidgid
切换用户nobody
用户写入nobody
用户可写入父文件夹。您可以在cont-init.d
脚本中或作为 s6-rc oneshots 创建日志文件夹。下面是一个以旧方式实现的记录服务myapp
的示例:
/etc/cont-init.d/myapp-log-prepare
:
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
/etc/services.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/services.d/myapp/log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
这是在 s6-rc 中实现的相同服务 myapp。
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/dependencies.d/base
:空文件
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/type
:
oneshot
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
if { mkdir -p /var/log/myapp }
if { chown nobody:nogroup /var/log/myapp }
chmod 02755 /var/log/myapp
(详细部分开始。)
因此, up
和down
文件很特殊:它们不是 shell 脚本,而是由 execlineb 解释的单个命令行。您不必担心 execline;您应该只记住up
文件包含单个命令行。因此,如果您需要包含多个指令的脚本,请按以下步骤操作:
up
文件中调用该脚本。以下是通常为myapp-log-prepare
编写up
文件的方法:
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
/etc/s6-overlay/scripts/myapp-log-prepare
/etc/s6-overlay/scripts/myapp-log-prepare
:(需要可执行)
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
实际脚本的位置是任意的,它只需要与您在up
文件中写入的内容匹配即可。
但在这里,碰巧脚本足够简单,可以完全放入up
文件中,而不会使其变得太复杂或太难以理解。因此,我们选择将其作为示例,以表明如果您愿意的话,您可以对up
执行更多操作。您可以在此处阅读 execline 语言的完整文档。
(详细部分结束后,再次单击上面的三角形即可折叠。)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
:空文件
/etc/s6-overlay/s6-rc.d/myapp-log/dependencies.d/myapp-log-prepare
:空文件
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/s6-overlay/s6-rc.d/myapp/producer-for
for :
myapp-log
/etc/s6-overlay/s6-rc.d/myapp-log/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp-log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
/etc/s6-overlay/s6-rc.d/myapp-log/consumer-for
:
myapp
/etc/s6-overlay/s6-rc.d/myapp-log/pipeline-name
:
myapp-pipeline
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp-pipeline
:空文件
有很多文件!这一切的含义总结如下:
myapp | myapp-log
管道被赋予一个名称myapp-pipeline
,并且该名称被声明为user
包的一部分,因此它将在容器启动时启动。myapp-log-prepare
、 myapp-log
和myapp
都依赖于base
包,这意味着它们只有在系统实际准备好启动它们时才会启动。它确实完成了与/etc/cont-init.d
加/etc/services.d
方法相同的事情,但它的底层更干净,并且可以处理更复杂的依赖关系图,因此只要有机会,我们建议您您熟悉声明服务和记录器的 s6-rc 方式。服务定义目录的完整语法,包括声明您的服务是长期运行还是一次性服务、声明管道、添加特定于服务的超时(如果需要)等,可以在此处找到。
当执行服务时,无论是服务还是记录器,一个好的做法是在执行之前放弃权限。 s6
已经包含了可以执行这些操作的实用程序:
在execline
中:
#!/command/execlineb -P
s6-setuidgid daemon
myservice
在sh
中:
#! /bin/sh
exec s6-setuidgid daemon myservice
如果您想了解有关这些实用程序的更多信息,请查看: s6-setuidgid
、 s6-envuidgid
和s6-applyuidgid
。
如果您希望自定义脚本具有可用的容器环境:您可以使用with-contenv
帮助程序,它将所有这些都推送到您的执行环境中,例如:
/etc/cont-init.d/01-contenv-example
:
#! /command/with-contenv sh
env
该脚本将输出容器环境的内容。
Docker 的最新版本允许使用只读根文件系统运行容器。如果您的容器处于这种情况,您应该设置S6_READ_ONLY_ROOT=1
来通知 s6-overlay 它不应尝试写入某些区域 - 相反,它将复制到安装在/run
上的 tmpfs 中。
请注意,s6-overlay 假设:
/run
存在并且可写。如果不是,它将尝试在那里挂载 tmpfs。/var/run
是/run
的符号链接,用于与以前的版本兼容。如果不是,它就会变成这样。一般来说,您的默认 docker 设置应该已经在/run
中提供了合适的 tmpfs 。
通过向执行上下文提供一组已经预定义的环境变量,可以以某种方式调整 s6-overlay 的行为:
PATH
(默认 = /command:/usr/bin:/bin
):这是容器中所有服务(包括 CMD)的默认 PATH。如果您有很多服务依赖于存储在另一个目录(例如/usr/sbin
中的二进制文件,请设置此变量。请注意,如果/command
、 /usr/bin
和/bin
尚未位于您提供的路径中,则它们将始终添加到该路径中。S6_KEEP_ENV
(默认= 0):如果设置,则环境不会重置,整个监督树会看到原始的环境变量集。它将with-contenv
切换为 nop。S6_LOGGING
(默认 = 0):0
:将所有内容输出到 stdout/stderr。1
:使用内部catch-all
记录器并保留其上的所有内容,它位于/var/log/s6-uncaught-logs
中。作为CMD
运行的任何内容仍会输出到 stdout/stderr。2
:使用内部catch-all
记录器并保留其上的所有内容,包括CMD
的输出。绝对没有任何内容写入 stdout/stderr。S6_CATCHALL_USER
(默认 = root):如果设置,并且S6_LOGGING
为 1 或 2,则全能记录器将以该用户身份运行,该用户必须在映像的/etc/passwd
中定义。每一点特权分离都对安全性有所帮助。S6_BEHAVIOUR_IF_STAGE2_FAILS
(默认值 = 0):确定如果服务脚本之一失败,容器应执行的操作。这包括:fix-attrs
中出现任何问题/etc/cont-init.d
或新式 s6-rc oneshot 失败/etc/services.d
或新式 s6-rc longrun 被标记为等待就绪通知,并且未能在分配的时间内就绪(请参阅下面的S6_CMD_WAIT_FOR_SERVICES_MAXTIME
)。 S6_BEHAVIOUR_IF_STAGE2_FAILS
的有效值如下:0
:即使脚本失败,也以静默方式继续。1
:继续,但会发出恼人的错误消息警告。2
:停止容器。S6_KILL_FINISH_MAXTIME
(默认 = 5000):系统在关闭时应等待/etc/cont-finish.d
中的脚本自然完成的时间(以毫秒为单位)。在此持续时间之后,将向脚本发送 SIGKILL。请记住, /etc/cont.finish.d
中的脚本是按顺序运行的,并且关闭序列可能会为每个脚本等待S6_KILL_FINISH_MAXTIME
毫秒。S6_SERVICES_READYTIME
(默认值= 50):对于在/etc/services.d
中声明的服务,在服务启动的时刻和可以测试其就绪状态的时刻之间存在不可避免的竞争条件。为了避免这种竞争,我们在测试准备情况之前会睡一会儿,默认为 50 毫秒。如果您的计算机速度缓慢或非常繁忙,您可能会收到类似s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory
错误。在这种情况下,您应该通过在S6_SERVICES_READYTIME
变量中声明它(以毫秒为单位)来增加睡眠时间。请注意,它仅涉及/etc/services.d
; s6-rc 不受竞争条件的影响。S6_SERVICES_GRACETIME
(默认 = 3000):在关闭时, s6
应等待多长时间(以毫秒为单位),以便/etc/services.d
中声明的服务终止,然后再继续其余的关闭操作。S6_KILL_GRACETIME
(默认 = 3000):在关闭过程结束时,当所有进程都收到 TERM 信号时, s6
应等待多长时间(以毫秒为单位),以便它们在发送KILL
信号以确保它们已死亡之前死亡。S6_LOGGING_SCRIPT
(默认=“n20 s1000000 T”):此环境决定记录什么以及如何记录,默认情况下每行都会以 ISO8601 开头,当当前日志文件达到 1mb 时进行轮换,并最多归档 20 个文件。S6_CMD_ARG0
(默认 = 未设置):此环境变量的值将添加到 docker 传递的任何CMD
参数之前。如果您要将现有映像迁移到 s6-overlay 并希望将其作为直接替换,请使用它:将此变量设置为以前使用的 ENTRYPOINT 的值将帮助您进行转换。S6_CMD_USE_TERMINAL
(默认 = 0):如果您的 CMD 需要终端进行输出(通常当您使用docker run -it
运行容器时),并且您已将S6_LOGGING
设置为非零值,请将此值设置为1 。这个设置会让你的CMD实际输出到你的终端;缺点是它的输出不会被记录。默认情况下(当此变量为0或未设置时),当S6_LOGGING
非零时,会记录 CMD 的 stdout 和 stderr,这意味着即使您在交互式终端中运行它们,它们也会进入管道。S6_FIX_ATTRS_HIDDEN
(默认 = 0):控制fix-attrs.d
脚本处理文件和目录的方式。0
:排除隐藏文件和目录。1
:处理所有文件和目录。S6_CMD_WAIT_FOR_SERVICES
(默认 = 0):默认情况下,当容器启动时, /etc/services.d
中的服务将启动,并且执行将继续启动user2
捆绑包和 CMD(如果定义了其中任何一个)。但是,如果S6_CMD_WAIT_FOR_SERVICES
不为零,则容器启动序列将等待,直到/etc/services.d
中的服务准备就绪,然后再继续序列的其余部分。请注意,只有当/etc/services.d
中的服务向 s6 通知其准备就绪时,这才有意义。S6_CMD_WAIT_FOR_SERVICES_MAXTIME
(默认 = 0,即无限):在继续执行 CMD 之前服务可以启动的最长时间(以毫秒为单位)。如果您的服务可能会无限期地阻塞,并且您希望容器在给定时间后一切都未正常运行时发生故障,请将此变量设置为正值。请注意,此值还包括设置旧容器初始化 ( /etc/cont-init.d
) 和服务 ( /etc/services.d
) 的时间,因此在计算合适的值时请考虑到这一点。在s6五次播放的版本中,默认值为5000(五秒),但造成了比解决问题更多的容器故障,因此默认情况下没有超时:S6-架子将等待等待为了提出所有服务所必需的。S6_READ_ONLY_ROOT
(default = 0):当在一个root Filesystem的容器中运行时,将此ENV设置为1 ,以告知INIT阶段2,以便在/etc
to /run/s6/etc
中复制用户提供的初始化脚本它试图更改权限等。有关更多信息,请参见仅阅读的根文件系统。S6_SYNC_DISKS
(default = 0):将此env设置为1,以告知Init阶段3,它应该在停止容器之前尝试同步文件系统。注意:这可能会同步主机上的所有文件系统。S6_STAGE2_HOOK
(default = none):如果存在此变量,则其内容将被解释为在启动服务之前将在第2阶段运行的shell摘录。例如,可以使用此操作来在运行时在运行时动态修补服务数据库。错误的值可以防止您的容器运行或危害您的安全性,因此只有在确切知道自己在做什么时才使用此操作。如有疑问,请留下此变量未定义。S6_VERBOSITY
(默认值= 2):在容器启动和停止时间内控制S6-RC的详细性以及潜在的其他工具。默认值2通常是冗长:它将列出服务开始和停止操作。您可以通过减少此数字使容器更安静:1只会打印警告和错误,而0只会打印错误。您还可以通过将该数字提高到5个,使容器更详细,即打印跟踪和调试信息,但是输出将很快变得非常嘈杂,大多数人不需要这一点。S6_CMD_RECEIVE_SIGNALS
(默认值= 0):决定发送到容器的信号应发送到容器的PID 1或CMD。默认情况下,当您执行例如docker stop
时,将发送一个术语信号到容器的PID 1,这将触发完整的容器关闭序列 - 但是,如果存在CMD,则将是最后一个被杀死的过程之一,只有当其他所有内容都关闭并且容器即将退出时。如果此变量为1或以上,则将信号从PID 1转到CMD,这意味着docker stop
会将Sigterm发送到CMD,并且容器仅在CMD死亡时触发其关闭过程。请注意,只有Sigterm,sigquit,sigint,sigusr1,sigusr2,sigpwr和sigwinch被转移;其他信号要么被忽略或不能转移,因此必然会由PID 1处理。请注意,使用此选项可能会阻止Interactive CMD完全工作 - 换句话说,如果您在终端中运行了交互式CMD,请设置此变量;但这应该很好,因为在这种情况下,您已经有了停止CMD的交互方式。如果在容器中运行的软件需要Syslog,请提取syslogd-overlay-noarch.tar.xz
tarball:这将为您提供一个小的syslogd仿真。日志将在/var/log/syslogd
的各个子目录下找到,例如,消息将在/var/log/syslogd/messages/
directory中找到,最新日志可在/var/log/syslogd/messages/current
中找到。 /var/log/syslogd/messages/current