在容器中运行systemd

我们长期以来一直在容器中使用systemd这一主题。 早在2014年,我们的安全工程师Daniel Walsh撰写了一篇名为 在Docker容器内 运行Runningd》的文章,几年后,另一篇名为《在非特权容器中运行Runningd》的文章指出,情况并非如此。大大改善了。 他特别写道:“不幸的是,两年后,如果您搜索Docker系统,弹出的第一件事就是他的同一篇旧文章。 所以是时候改变一些东西了。” 此外,我们已经讨论了Docker开发人员与systemd之间的冲突



在本文中,我们将说明过去发生的变化以及Podman如何在此问题上为我们提供帮助。

在容器内运行systemd的原因很多,例如:

  1. 多服务容器 -许多人希望从虚拟机中获取其多服务应用程序并在容器中运行它们。 当然,将此类应用程序分解为微服务会更好,但是并不是每个人都可以做到,或者根本没有时间。 因此,以systemd从单位文件启动的服务形式启动此类应用程序是很有意义的。
  2. 系统化单元文件 -容器中运行的大多数应用程序都是从以前在虚拟机或物理机上运行的代码编译而来的。 这些应用程序具有为这些应用程序编写的单位文件,并且了解如何运行它们。 因此,最好使用受支持的方法来启动服务,而不是破解您自己的初始化服务。
  3. Systemd是一个流程管理器。 它比其他任何工具都更好地管理服务(关闭,重新启动服务或爬网僵尸进程)。

没有在容器中运行systemd的原因很多。 主要的一点是systemd / journald控制容器的输出,而诸如KubernetesOpenShift之类的工具则希望容器将日志直接写入stdout和stderr。 因此,如果您打算通过上述编排工具来管理容器,则需要认真考虑使用基于systemd的容器。 此外,Docker和Moby的开发人员通常强烈反对在容器中使用systemd。

Podman的到来


我们很高兴地宣布,局势终于动荡了。 Red Hat负责启动容器的团队决定开发自己的容器引擎 。 他的名字叫Podman,并提供与Docker相同的命令行界面(CLI)。 几乎所有Docker命令都可以在Podman中以相同的方式使用。 我们经常举办研讨会,现在称为将Docker更改为Podman ,而第一张幻灯片鼓励您注册:alias docker = podman。

许多这样做。

我和我的Podman绝不反对基于systemd的容器。 毕竟,Systemd最常被用作Linux的init子系统,并且不让它在容器中正常工作就意味着忽略了成千上万的人习惯于运行容器的方式。

Podman知道如何使systemd在容器中正常工作。 她需要在/ run和/ tmp上安装tmpfs之类的东西。 她喜欢启用“容器”环境,并且正在等待对cgroup目录的一部分以及对/ var / log / journalized文件夹的写权限。

当启动第一个命令为init或systemd的容器时,Podman会自动配置tmpfs和Cgroups,以便systemd正常启动。 要阻止此自动启动模式,请使用--systemd = false选项。 请注意,只有当Podman认为有必要执行systemd或init命令时,才使用systemd模式。

以下是手册摘录:

男子波德曼奔跑
...

–Systemd = true | false

在系统模式下运行容器。 默认启用。

如果在容器内部执行了systemd或init命令,则Podman将在以下目录中配置tmpfs挂载点:

/运行/运行/锁定/ tmp / sys / fs / cgroup / systemd / var / lib / journal

另外,默认情况下,SIGRTMIN + 3将用作停止信号。

所有这些使systemd无需任何修改即可在密闭容器中工作。

注意:systemd正在尝试写入cgroup文件系统。 但是,SELinux默认情况下会阻止容器执行此操作。 要启用写入,请启用批处理参数container_manage_cgroup:

setsebool -P container_manage_cgroup是

现在查看使用Podman时在容器中运行systemd的Dockerfile的外观:

# cat Dockerfile FROM fedora RUN dnf -y install httpd; dnf clean all; systemctl enable httpd EXPOSE 80 CMD [ "/sbin/init" ] 

仅此而已。

现在收集容器:

 # podman build -t systemd . 

我们告诉SELinux允许systemd修改Cgroup的配置:

 # setsebool -P container_manage_cgroup true 

顺便说一下,许多人忘记了这一步。 幸运的是,只需执行一次就足够了,并且在系统重新引导后将保存设置。

现在运行容器:

 # podman run -ti -p 80:80 systemd systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid) Detected virtualization container-other. Detected architecture x86-64. Welcome to Fedora 29 (Container Image)! Set hostname to <1b51b684bc99>. Failed to install release agent, ignoring: Read-only file system File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling. Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.) [ OK ] Listening on initctl Compatibility Named Pipe. [ OK ] Listening on Journal Socket (/dev/log). [ OK ] Started Forward Password Requests to Wall Directory Watch. [ OK ] Started Dispatch Password Requests to Console Directory Watch. [ OK ] Reached target Slices. … [ OK ] Started The Apache HTTP Server. 

一切,服务启动并正常工作:

 $ curl localhost <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> … </html> 

注意:请勿尝试在Docker上重复此操作! 在那里,仍然需要用铃鼓跳舞以通过恶魔发射这种容器。 (这需要其他字段和包才能在Docker中无缝运行,或者需要在特权容器中运行。有关详细信息,请参阅本文 。)

关于Podman和systemd的一些更酷的事情


Podman在systemd单位文件中比Docker更好地工作


如果需要在系统引导时启动容器,则只需将相应的Podman命令插入systemd单元文件中,即可启动服务并对其进行监视。 Podman使用标准的fork-exec模型。 换句话说,容器进程与Podman进程相关联,因此systemd可以轻松地监视它们。

Docker使用客户端-服务器模型,并且Docker CLI命令也可以直接放置在单元文件中。 但是,在Docker客户端连接到Docker守护程序之后,它(客户端)成为另一个处理stdin和stdout的进程。 反过来,systemd对Docker客户端与运行Docker守护程序的容器之间的连接一无所知,因此,在此模型下,systemd无法从根本上监视服务。

通过套接字进行系统激活


Podman通过套接字正确完成激活。 由于Podman使用fork-exec模型,因此可以将套接字转发到其子容器进程。 Docker不知道如何使用,因为它使用客户端-服务器模型。

Podman用于与带有容器的远程客户端进行交互的varlink服务实际上是通过套接字激活的。 用Node.js编写的cockpit-podman软件包,是cockpit项目的一部分,它使人们可以通过Web界面与Podman容器进行交互。 运行cockpit-podman的Web守护程序会将消息发送到systemd正在侦听的varlink套接字。 之后,systemd激活Podman程序以接收消息并开始管理容器。 通过套接字激活systemd可以使您在实现远程API时无需持续工作的守护进程。

另外,我们正在为Podman开发另一个客户端,称为podman-remote,该客户端实现相同的Podman CLI,但是调用varlink来启动容器。 Podman-remote可以在SSH会话之上运行,这使您可以安全地与不同计算机上的容器进行交互。 随着时间的推移,我们计划使用podman-remote与Linux一起支持MacOS和Windows,以便这些平台上的开发人员可以在Podman varlink运行的情况下运行Linux虚拟机,并完全感觉到容器在本地计算机上运行。

SD_NOTIFY


Systemd允许您延迟启动辅助服务,直到所需的容器化服务启动为止。 Podman可以将SD_NOTIFY套接字转发到容器化服务,以便该服务将其准备工作通知systemd。 同样,使用客户端-服务器模型的Docker不知道如何操作。

在计划中


我们计划添加podman generate systemd CONTAINERID命令,该命令将生成systemd单位文件来管理特定的容器。 对于非特权容器,这应该在root模式和rootless模式下都可以工作。 我们甚至看到了创建兼容OCI的systemd-nspawn运行时的请求。

结论


在容器中运行systemd是可以理解的需求。 多亏了Podman,我们终于有了一个容器启动器环境,该环境对systemd并不有害,但是使它易于使用。

Source: https://habr.com/ru/post/zh-CN468931/


All Articles