我有一个包含以下内容的脚本:
sudo machinectl start "$machinename"
sudo systemd-run -PM root@"$machinename" "$command"
Failed to connect to bus: No such file or directory
Failed to start transient service unit: Transport endpoint is not connected
这失败了,因为第一行只开始启动容器;第二行在容器完成启动之前运行。目前,我有一个解决方案,可以不断轮询容器的状态并阻塞,直到它准备好:
while [ "$(sudo systemctl show "systemd-nspawn@$machinename" -P StatusText)" != "Container running: Ready." ]
do
true
done
如何等待容器完成启动,而不是不断轮询容器的状态?
取决于你想要做什么。我将直接回答您的问题,然后回答一些替代方案。我假设您使用 systemd 作为容器中的初始化系统,如果您的容器操作系统基于 Debian / Arch / Ubuntu 或类似系统,这将是正确的。
启动 nspawn 容器后执行命令
在
.nspawn
文件 (/etc/systemd/nspawn/yourcontainer.nspawn
) 中添加:然后
sudo machinectl start yourcontainer
将等到容器完成启动后再退出。您的脚本的第二行现在可以工作了,因为容器已准备就绪(除非您的容器无法启动,否则会使您的轮询陷入无限循环)。在后台,主机
systemd-nspawn
正在容器中设置一个 Unix 域套接字/run/host/notify
。当容器的 systemd 准备就绪时(换句话说,当它到达目标时),它会向该套接字multi-user.target
发送通知。READY=1
主机的systemd-nspawn
服务等待接收此消息。这种方法的缺点是您不能再异步启动容器(除非使用
&
符号),如果您正在调试并且启动时间很长,这会很烦人。其他一些方法,按复杂性排序:
在 chroot 中运行命令
假设容器没有运行,
如果您刚刚创建了一个容器并以编程方式对其进行了多次初始化,这将非常有用。显然,它并没有从沙盒功能中受益。如果您以前使用 运行相同的容器,它也将不起作用
PrivateUsers=yes
,因为这些文件将chown
使用高 UID 进行编辑。如果容器已经在运行,它可能会给出未定义的结果。直接使用 systemd-nspawn
这种方法不需要上面的
NotifyReady=yes|no
解释。这将在所有沙盒打开的情况下在容器内运行命令,但该命令将作为唯一进程(并且带有
PID=1
)运行 - 您的 init 服务将不会运行。因此,例如网络将不可用(除非您仍然使用主机网络)。如果容器已在运行,则此命令将不起作用。
套接字激活
如果您正在等待容器中的服务器准备就绪,则只需使用套接字激活(假设您的服务器兼容)。这在别处有更好的解释,总之 systemd 将等待连接到您的套接字(例如 TCP 端口 80)。当客户端确实连接时,systemd 将启动您的容器,然后将流量转发给它。古时候
inetd
也做过同样的事情。这将需要文件
[email protected]
中的一行.socket
。