Systemd

如何等待 systemd-nspawn 容器啟動?

  • March 27, 2022

我有一個包含以下內容的腳本:

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) 中添加:

[Exec]
NotifyReady=yes

然後sudo machinectl start yourcontainer將等到容器完成啟動後再退出。你的腳本的第二行現在可以工作了,因為容器已經準備好了(除非你的容器無法啟動,這會使你的輪詢陷入無限循環)。

在後台,主機systemd-nspawn正在容器中設置一個 Unix 域套接字/run/host/notify。當容器的 systemd 準備就緒時(換句話說,當它到達目標時),它會向該套接字multi-user.target發送通知。READY=1主機的systemd-nspawn服務等待接收此消息。

這種方法的缺點是您不能再非同步啟動容器(除非使用&符號),如果您正在調試並且啟動時間很長,這會很煩人。

其他一些方法,按複雜性排序:

在 chroot 中執行命令

假設容器沒有執行,

sudo chroot /var/lib/machines/yourcontainer /bin/bash -c "$command"

如果您剛剛創建了一個容器並以程式方式對其進行了多次初始化,這將非常有用。顯然,它並沒有從沙盒功能中受益。如果您以前使用 執行相同的容器,它也將不起作用PrivateUsers=yes,因為這些文件將chown使用高 UID 進行編輯。如果容器已經在執行,它可能會給出未定義的結果。

直接使用 systemd-nspawn

這種方法不需要上面NotifyReady=yes|no解釋。

systemd-nspawn -M yourcontainer -P /bin/bash -c "$command"

這將在所有沙盒打開的情況下在容器內執行命令,但該命令將作為唯一程序(並且帶有PID=1)執行 - 您的 init 服務將不會執行。因此,例如網路將不可用(除非您仍然使用主機網路)。

如果容器已在執行,則此命令將不起作用。

套接字啟動

如果您正在等待容器中的伺服器準備就緒,則只需使用套接字啟動(假設您的伺服器兼容)。這在別處有更好的解釋,總之 systemd 將等待連接到您的套接字(例如 TCP 埠 80)。當客戶端確實連接時,systemd 將啟動您的容器,然後將流量轉發給它。古時候inetd也做過同樣的事情。

這將需要文件Service=systemd-nspawn@yourcontainer.service中的一行.socket

引用自:https://unix.stackexchange.com/questions/693204