当主机操作系统是 Ubuntu 16.04 或 RHEL 7.x 时,以下命令有效,帮助我们在 docker 容器内于主机上运行 systemctl 命令:
# nsenter --mount=/hostroot/proc/1/ns/mnt -- systemctl start dummy.service
但是在较新的主机操作系统 Ubuntu 20.04 和 RHEL 8.x 中,这不起作用,我们收到以下错误:
# nsenter --mount=/hostroot/proc/1/ns/mnt -- systemctl start dummy.service
Failed to connect to bus: No data available
我附加了一个简单示例和命令来运行和重现该问题:
我想从容器在主机上启动的示例服务:
# cat /etc/systemd/system/dummy.service
[Unit]
Description=dummy service
[Service]
ExecStart=/usr/bin/sleep infinity
我的容器的 Dockerfile:
# cat Dockerfile
FROM ubuntu:20.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt update -y --fix-missing
RUN apt install -y util-linux
STOPSIGNAL SIGRTMIN+3
CMD [ "/bin/bash" ]
构建图像:
# docker build -t trial .
删除所有过时的容器:
# docker rm -f trial
运行图像:
# docker run -it -d --net=host --privileged -v /:/hostroot -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name trial trial
重现问题:
# docker exec -it trial bash
# nsenter --mount=/hostroot/proc/1/ns/mnt -- systemctl start dummy.service
Failed to connect to bus: No data available
我想知道是否有任何其他选项或任何需要改变的 docker run 命令才能使其正常工作。
答案1
我研究了好几个小时,似乎这个方法sd_bus_start已更改为包含其他检查。我无法缩小它所寻找的其他内容的范围,但是我能够想出一个更优雅的解决方案,使用远程 systemctl 命令而不是从主机挂载所有目录来完成相同的任务。
远程 systemctl
systemctl
通过标志支持远程命令--host / -H
。它使用 ssh 连接到远程主机,因此需要 ssh 密钥对。由于我们控制着我们所在的主机,因此设置起来非常简单。
Docker 命令(或 Kubernetes 参数)
这是可以使用的完整命令,我将在下面分解每个部分。容器的假设是它已systemctl
安装ssh
,容器正在主机网络上运行,并且root
帐户的主目录已挂载(如果需要,您可以使用其他用途)。
(ls ~/.ssh/id_rsa || ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N "")
&& (grep -qxF $(cat ~/.ssh/id_rsa.pub) ~/.ssh/authorized_keys || echo $(cat ~/.ssh/id_rsa.pub) > ~/.ssh/authorized_keys)
&& (grep -qxF "StrictHostKeyChecking no" ~/.ssh/config || echo "StrictHostKeyChecking no" >> ~/.ssh/config)
&& (grep -qxF "UserKnownHostsFile /dev/null" ~/.ssh/config || echo "UserKnownHostsFile /dev/null" >> ~/.ssh/config)
&& systemctl -H [email protected] start nfs-server.service
此命令查看~/.ssh/id_rsa
文件是否存在,否则创建一个。
(ls ~/.ssh/id_rsa || ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N "")
现在,如果文件中尚不存在公钥,我们将公钥添加到授权密钥中。
(grep -qxF "$(cat ~/.ssh/id_rsa.pub)" ~/.ssh/authorized_keys || echo "$(cat ~/.ssh/id_rsa.pub)" > ~/.ssh/authorized_keys)
这可能可以通过将其放在 ssh 配置的一部分中来提高安全性127.0.0.1
,但我们需要
(grep -qxF "StrictHostKeyChecking no" ~/.ssh/config || echo "StrictHostKeyChecking no" >> ~/.ssh/config)
&& (grep -qxF "UserKnownHostsFile /dev/null" ~/.ssh/config || echo "UserKnownHostsFile /dev/null" >> ~/.ssh/config)
最后我们有了实际的systemctl
命令。注意。-H [email protected]
systemctl -H [email protected] start nfs-server.service
为了实现最大程度的安全,最好先在容器外部设置密钥和用户(通过 Ansible 或类似方式),并且只允许systemctl -H
在容器内执行命令。