Gitlab CI 的 systemd 容器入口点

问题描述 投票:0回答:3

我正在构建一个用于运行 Gitlab CI 作业的 docker 映像。其中一个组件需要 systemd 启动并在容器内运行,这并不是一件小事,但网上有几个指南,所以我设法做到了。该过程的一部分需要在 Dockerfile 中定义此入口点:

ENTRYPOINT ["/usr/sbin/init"]

以便 systemd 根据需要在容器中作为 PID 1 运行。这似乎与 Gitlab CI 要求相冲突:据我了解,gitlab-runner 会覆盖 Dockerfile 的 CMD 以生成一个 shell,然后执行 CI 脚本。但是 /usr/sbin/init 入口点无法理解 Gitlab 的 CMD,因此不会生成 shell 并且执行会停止。

我不知道如何解决这个问题:

  • 执行启动 /usr/sbin/init 的入口点脚本,然后 shell 将无法工作,因为 systemd 不是 PID1;
  • 使用 shell 作为 ENTRYPOINT,然后使用 systemd 作为 CMD 将不起作用,因为 Gitlab CI 会覆盖 CMD。

我想不出任何其他可能的解决方案,因此非常感谢任何帮助。

docker gitlab-ci systemd
3个回答
1
投票

您始终可以分叉 bash shell 来完成工作,然后使用 pid 1 执行 systemd,如下所示:

ENTRYPOINT [ "/bin/bash", "-c", "nohup bash -c \"${@} ; /sbin/init 0\" & exec /sbin/init", "${@}" ]

0
投票

我终于能够提出一个可行的解决方案。 ENTRYPOINT 执行脚本:

ENTRYPOINT ["/entrypoint.sh"]

它检索 PID 1 的 stdin/stdout fd(即 Gitlab CI 将用于作业 I/O 的那些),通过将它们附加到长时间运行的进程来固定它们,然后将 systemd 生成为 PID 1:

#!/bin/bash

# Start a long-running process to keep the container pipes open
sleep infinity < /proc/1/fd/0 > /proc/1/fd/1 2>&1 &
# Wait a bit before retrieving the PID
sleep 1
# Save the long-running PID on file
echo $! > /container-pipes-pid
# Start systemd as PID 1
exec /usr/lib/systemd/systemd

需要固定 stdin/stdout,因为某些 systemd 版本会在启动时关闭 stdin/stdout,但之所以需要它们,是因为 CI 基础设施使用它们将 CI 命令发送到 shell 并接收控制台输出。因此,将它们附加到

sleep infinity
会使它们即使在
exec /usr/lib/systemd/systemd
之后仍然存在。

然后由 systemd 单元生成 shell(之前已在 Dockerfile 中启用):

[Unit]
Description=Start bash shell attached to container STDIN/STDOUT

[Service]
Type=simple
PassEnvironment=PATH LD_LIBRARY_PATH
ExecStart=/bin/bash -c "echo Attaching to pipes of PID `cat container-pipes-pid` && exec /bin/bash < /proc/`cat container-pipes-pid`/fd/0 > /proc/`cat container-pipes-pid`/fd/1 2>/proc/`cat container-pipes-pid`/fd/2"
ExecStopPost=/usr/bin/systemctl exit $EXIT_STATUS

[Install]
WantedBy=multi-user.target rescue.target

固定的 fd 附加到 shell(这不会产生冲突,因为 systemd 和 sleep 都不在这些 fd 上执行 I/O),因此 shell 正确接收 CI 命令并将控制台输出定向到 CI 日志。然后,在作业结束时,当 CI 基础设施关闭 stdin 时,shell 终止并且该单元关闭容器,返回作业执行结果,以便 CI 正确检索作业结果。

这是一个相当复杂的实现,肯定可以改进和改进,但对于我的目的来说效果很好。


0
投票

我已经复制了 Nicola Mori 上面发布的解决方案,对于 Debian Bookworm、Trixie 和 Ubuntu Jammy,它工作得很好,未经修改 - 干得好!

对于 Debian Bullseye,

systemd
的路径是
/lib/systemd/systemd
而不是
/usr/lib/systemd/systemd
,因此需要更改。

我将上面发布的 systemd 单元保存为

bash.service
,并在 Docker 文件中添加它和
entrypoint.sh
,如下所示:

COPY bash.service /etc/systemd/system/bash.service
RUN chown root:root /entrypoint.sh \
    && chmod 755 /entrypoint.sh \
    && chown root:root /etc/systemd/system/bash.service \
    && chmod 644 /etc/systemd/system/bash.service \
    && systemctl enable bash.service
ENTRYPOINT ["/entrypoint.sh"]

请注意,如果这些容器在非特权模式下运行,那么它们会失败并显示:

ERROR: Job failed: exit code 255

我不知道是否可以将检查容器是否在特权模式下运行添加到

entrypoint.sh
脚本中,或者检查是否可能会干扰PID分配?

我还在 GitLab Discourse 论坛上发布了相关内容。

© www.soinside.com 2019 - 2024. All rights reserved.