S6 在 LAIN 集群中的应用实践

时间:2021-05-08 14:14:34

S6 在 LAIN 集群中的应用实践


近几年,容器技术迅猛发展,并在各大企业得到了广泛应用。它标准化了应用程序的运行环 境,从而简化了程序的部署流程,同时也促进了 PaaS(Platform as a Service)系统的发展,LAIN 即为其中之一。作为一种革命性的新技术,Docker 具有种种优点,比如一处编译、处处运行,易于编排和轻量等等;但是,由于发展时间较短, 在生产环境中使用时,Docker 在一些环节出现了性能问题,另外,Docker 容器与虚拟机之 间也有一些微妙的区别,在生产环境中使用时需要给予特别的注意。下面分享一下 LAIN 生 产集群在运行过程中遇到的一些问题和解决方案。


不断累积的僵尸进程

S6 在 LAIN 集群中的应用实践


为了方便管理,我们把 Jenkins 搬进了容器。但是,Jenkins 容器经常停止响应, 需要定期重启。为什么在虚拟机上可以正常运行的程序到了容器里就出现问题了呢? 通过 ps aux 可以发现,Jenkins 容器里有大量的僵尸进程,即图 1 中的 [git-remote-http] 进程。


S6 在 LAIN 集群中的应用实践


图 1:zombie processes


进程表是有限的系统资源,当僵尸进程占用了大量的进程表空间时,就会导致无法启动新的 进程。那为什么容器里会产生大量的僵尸进程呢?因为在类 UNIX 系统中,PID 为 1 的 进程是特殊的:它负责收割僵尸进程。在虚拟机中,PID 为 1 的进程通常是 Systemd、 Sys Vinit 或者 upstart,它们都能自动收割僵尸进程;在容器里,情况有很大的不同: PID 为 1的进程常常是用户自己写的程序,很可能不会收割僵尸进程,比如上面的 jenkins-master,这时候就会造成僵尸进程的累积,最终导致容器内无法再启动新的进程。


偶尔卡死的 Docker Daemon

S6 在 LAIN 集群中的应用实践


Docker Daemon 可以收集容器的标准输出,然后使用 syslog 或 json-file 等 log-driver 处理。但是,这个架构是中心化的,Docker Daemon 会成为日志收集的瓶颈。 表 1 是我们测得的 Docker 收集日志的速度。


log-driver 日志收集速度
syslog 14.9 MB/s
json-file 37.9 MB/s
表 1:Docker 的日志收集速度


可以看到,这个速度并不理想,当容器的标准输出较多时,Docker Daemon 不能及时处理, 就会影响这个容器的正常运行;同时,Docker Daemon 对标准输出的处理会阻塞其他操作, 比如 docker ps 和 docker stop 等命令也会卡死。这时,我们只能重启 Docker Daemon,同时禁止容器的标准输出。


一站式解决方案 —— S6

S6 在 LAIN 集群中的应用实践


上述 2 个问题严重地影响了应用的正常运行,是否有办法可以解决呢?即,我们希望找到 一个工具,既可以收割僵尸进程,又可以把标准输出重定向到文件并自动 rotate。 表 2 是我们找到的一些工具。


进程管理工具 僵尸进程 重定向标准输出并自动 rotate
S6 收割 支持
tini 收割 支持
systemd 收割 支持,但为二进制
phusion-baseimages 收割 不支持
daemontools 不收割 支持
supervisor 不收割 支持

表 2:进程管理工具的比较


从上表可以看出,S6 是最满足我们需求的解决方案。而且,它体积很小,只有 904 KB,启 动时间不超过 100 ms,运行时占用的 CPU 和内存可以忽略不计。


S6[1] 包含 s6-svscan、s6-supervise 和 s6-log 等组件。这些组件遵循 UNIX 设计哲学,相互独立,功能正交,通过适当组合可 以实现强大的功能。那具体怎样组合呢?S6 的作者把类 UNIX 系统的运行时可以分为 3 个 阶段(Bercot,n.d.),如图 2 所示。 而 s6-overlay[2] 实现了此方案,下面参考 s6-overlay 说明如何在容器中使用 S6 管理进程。


S6 在 LAIN 集群中的应用实践


图 2:类 UNIX 系统的运行时阶段8


阶段 1


在阶段 1,s6-overlay 准备环境变量和创建 s6 的工作目录 /var/run/s6/services 等, 然后启动 PID 为 1 的 s6-svscan 以管理 /var/run/s6/services下的服务:


s6-svscan -t0 /var/run/s6/services


为了适应 LAIN 的需求,我们还将 Dockerfile 里的 CMD 写入了 /etc/services.d/app/run。整个流程如图 3 所示。


S6 在 LAIN 集群中的应用实践


图 3:阶段 1


阶段 2


在阶段 2,s6-overlay 首先把 /etc/services.d 里的文件复制到 s6-svscan 的运行时 目录 /var/run/s6/services,然后通过 s6-svscanctl -a /var/run/s6/services 触 发 s6-svcan 对此目录的检索;s6-svcan 发现 /var/run/s6/services/app/run 和 /var/run/s6/services/app/log/run 后,会调用 s6-supervise 分别启动这个两个服 务,如图 4 所示。


S6 在 LAIN 集群中的应用实践


图 4:阶段 2


首先,容器运行过程中,s6-svcan 会收割僵尸进程,解决了我们的第一个问题。


其次,s6-log 将 CMD 的标准输出重定向到 /lain/logs/default/current,而不是发送到 Docker Daemon,这样就避免了 Docker Daemon 收集日志的性能瓶颈, 表 3 是我们的测试结果。可以看到,在 日志文件 test.log 体积较小的时候,s6-log 收集日志的速度可以达到 200 MB/s 左 右,几乎与直接写入文件的速度相当;当 test.log 的体积逐渐增大时,因为会触发越来 越频繁的日志轮转,所以 s6-log 的速度逐渐降低,逐渐趋近于 90 MB/s 左右。 s6-log 只有在日志文件达到 256 MB 时才会触发轮转,而这种情况在实际的生成环境中 并不会频繁发生,因此 s6-log 的实际日志收集速度应该在 100 MB/s 以上,足以满足我 们的需求,解决了我们的第二个问题。


test.log 体积 直接用文件收集日志的速度 日志收集速度
80 MB 94.5 MB/s 209 MB/s
160 MB 91.4 MB/s 212 MB/s
320 MB 90.5 MB/s 103 MB/s
640 MB 234 MB/s 99.6 MB/s
1280 MB 225 MB/s 95.7 MB/s
2560 MB 212 MB/s 91.8 MB/s

表 3:s6-log 的日志收集速度


阶段 3


s6-supervise 还会监听 SIGTERM 信号,当 s6-supervise 收到此信号后进入阶段 3: 执行 /var/run/s6/services/app/finish,也就是 s6-svscanctl -t /var/run/s6/services,从而让整个容器优雅地退出,这比 Docker 默认的等待 10 秒后 强制杀死进程的行为友好很多。


综上,在 Docker 中使用 S6 可以获得以下好处:


  • 自动收割僵尸进程

  • 在保留自动 rotate 功能的同时提高日志收集的速度

  • 适当处理 SIGTERM 信号,优化 docker stop 的用户体验


参考文献:


  1. Bercot, Laurent. n.d. “How to Run S6-Svscan as Process 1.” http://skarnet.org/software/s6/s6-svscan-1.html.


相关链接:


  1. http://www.skarnet.org/software/s6/

  2. https://github.com/just-containers/s6-overlay


基于Kubernetes的容器云平台实践培训

S6 在 LAIN 集群中的应用实践


本次培训包含:Kubernetes核心概念;Kubernetes集群的安装配置、运维管理、架构规划;Kubernetes组件、监控、网络;针对于Kubernetes API接口的二次开发;DevOps基本理念;Docker的企业级应用与运维等,点击识别下方二维码加微信好友了解具体培训内容


S6 在 LAIN 集群中的应用实践


点击阅读原文链接即可报名。