如何等待 systemd-nspawn 容器啟動?
我有一個包含以下內容的腳本:
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
。