如何在 Docker 容器中使用 systemctl

我们有时在使用 docker 的时候,会发现在容器中使用 systemctl 或者 service 的时候,会遇到下面的错误:

Failed to connect to bus: No such file or directory

一、原因

That’s because “systemctl” talks to the systemd daemon by using the d-bus. In a container there is no systemd-daemon. Asking for a start will probably not quite do what you expect - the dev-mapping need to be a bit longer.

容器里面是没有 systemd 进程的,所以不能正常开启 systemctl 。为什么 docker 会这样呢:

This is by design. Docker should be running a process in the foreground in your container and it will be spawned as PID 1 within the container’s pid namespace. Docker is designed for process isolation, not for OS virtualization, so there are no other OS processes and daemons running inside the container (like systemd, cron, syslog, etc), only your entrypoint or command you run.

If they included systemd commands, you’d find a lot of things not working since your entrypoint replaces init. Systemd also makes use to cgroups which docker restricts inside of containers since the ability to change cgroups could allow a process to escape the container’s isolation. Without systemd running as init inside your container, there’s no daemon to process your start and stop commands.

docker只是提供了进程隔离,不是操作系统的虚拟。

二、解决方案

1、网上流传一种错误的解决方法

我们可以在启动容器的时候将在启动参数加上 /sbin/init 来让其生效。

以centos为例:

docker run -d -v /sys/fs/cgroup/:/sys/fs/cgroup:ro --cap-add SYS_ADMIN --name systemd_websrv centos /sbin/init

以上为网上流传的方法,我先为大家解释一下这条命令:

-d:表示以后台进程的形式运行 Docker 容器。
-v /sys/fs/cgroup/:/sys/fs/cgroup:ro:表示将主机的 /sys/fs/cgroup 目录挂载到容器中的 /sys/fs/cgroup 目录,并以只读的方式挂载。这个目录包含了 cgroups 系统的配置文件,用于管理系统资源的分配。(:ro表示以只读方式挂载,当然:rw表示以读写方式挂载)
--cap-add SYS_ADMIN:表示为容器添加 SYS_ADMIN 的能力,以便容器能够访问系统的一些管理功能。
--name systemd_websrv:表示将容器命名为 "systemd_websrv"。
centos /sbin/init:表示使用 CentOS 镜像来创建容器,并在容器中运行 /sbin/init 程序。/sbin/init 是系统启动时运行的第一个进程,它负责启动系统的其他进程。

我们尝试运行这条命令,然后使用如下命令查询 systemctl 在容器里是否正常工作。

docker exec -it systemd_websrv systemctl
Failed to connect to bus: No such file or directory

错误的原因可能是在启动 Docker 容器时没有正确挂载容器内的 /run/dbus 目录。我们使用如下命令将 /run/dbus 挂载上。

# 首先删除容器名为 systemd_websrv 的容器
docker stop systemd_websrv
docker rm systemd_websrv
# 修改命令挂载 /run/dbus/ 目录
docker run -d -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /run/dbus:/run/dbus -v /etc/machine-id:/etc/machine-id --cap-add SYS_ADMIN --name systemd_websrv centos /sbin/init

再次尝试运行这条命令,然后使用如下命令查询 systemctl 在容器里是否正常工作。

docker exec -it systemd_websrv systemctl
Failed to connect to bus: Permission denied

这次提示的是没有权限了,继续改命令。

# 删除容器名为 systemd_websrv 的容器
docker stop systemd_websrv
docker rm systemd_websrv
# 修改命令挂载 /run/dbus/system_bus_socket 目录
docker run -ti -v /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro -v /sys/fs/cgroup/:/sys/fs/cgroup:ro --cap-add SYS_ADMIN --name systemd_websrv centos /bin/bash

这次可以了,进入 Docker容器了,我们尝试查询 systemctl 能否正常使用。

systemctl status
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

PID 1 是什么东东?我们尝试使用如下命令查询一下。

ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.4  12036  2160 ?        Ss   08:16   0:00 /bin/bash
root        16  0.0  0.4  44652  1780 ?        R+   08:20   0:00 ps aux

由于 init 进程 PID 1 是 /bin/bash,而不是 systemd,即使挂载了 dbus 也无济于事。因此这条路是行不通的。

2、正确的解决方法:替换 systemctl

使用 docker-systemctl-replacement 替换容器中的 systemctl 。

以 ubuntu 镜像为例:

1). 安装python2

sudo apt install python

2). 替换 systemctl (注意路径,可以使用 where is systemctl 查看当前默认路径)

wget https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl.py -O /bin/systemctl

3). 给定权限

sudo chmod a+x /bin/systemctl

这样就可以使用非 systemd 的 systemctl ,但是因为是非官方的 systemctl 所以可能存在一些未知问题。

最好还是建议将 docker 作为进程隔离环境,single app single container, 但是遇到非常特殊的情况下,可以使用上述解决方案,如果有更好的方案,欢迎反馈。

三、参考

https://stackoverflow.com/questions/49285658/how-to-solve-docker-issue-failed-to-connect-to-bus-no-such-file-or-directory
https://stackoverflow.com/questions/39169403/systemd-and-systemctl-within-ubuntu-docker-images
https://super-unix.com/ubuntu/ubuntu-systemctl-failed-to-connect-to-bus-docker-ubuntu16-04-container/


本文出处:HiFeng'Blog
本文链接:
https://www.hicairo.com/post/56.html
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA许可协议。转载请注明出处!