快轉到主要內容

使用systemd-nspawn取代chroot,建立輕量Debian容器

· 民國114年乙巳年
·
切換繁體/簡體
分類   資訊科技 虛擬機與容器技術
標籤   Systemd Chroot Docker Linux
目錄

什麼都要管的systemd居然還可以生成容器!systemd-nspawn是Linux系統內建的一個功能,透過這個工具,能夠生成多個Linux容器,在隔離的環境中執行程式。

使用範例:在Ubuntu系統跑Debian容器

1. systemd-nspawn特色
#

systemd-nspawn意思名為systemd namespace spawn。

講到容器化,Linux系統最古早的技術就是使用chroot指令進入一個隔離的環境了。從設計上來說,systemd-nspawn比chroot更好,透過隔離namespace與IPC提供更高安全性。所以systemd-nspawn可以說是chroot的威力加強版(Arch Wiki說systemd-nspawn是「打了類固醇的chroot」XD),它讓容器能夠作為一個系統服務控制,隨時被使用者啟動與停止。

在容器內部,依然能夠使用systemctl指令控制服務,這是Docker難以做到的技術。這表示一旦將容器設定為跟著Linux啟動,它就會跟著Systemd的規則執行。

既然提到容器,就會需要rootfs。雖然systemd-nspawn相容OCI標準,但是它不能直接拿Docker映像檔部署系統,且幾乎沒有人在維護systemd-nspawn映像檔,只能靠debootstrap這類工具產生。

systemd-nspawn沒有Docker生態系的工具鏈完整,很難用來分發應用程式。它的功能就侷限於產生一個暫時性的容器而已。

論將容器與Systemd服務整合,Podman Quadlets的方案也比systemd-nspawn靈活吧。

還有systemd-nspawn最大缺點就是依賴Systemd,沒有chroot的移植性好。chroot能在傳統Sysvinit的Linux發行版正常使用,甚至能搭配BusyBox在root過的Android執行。

因此systemd-nspawn可以看作是Docker以外的輕量替代品。與systemd-nspawn定位最類似的技術應該是LXC。

2. 前置條件
#

  1. 雖然systemd-nspawn是Systemd內建功能,不過有些條件需要滿足。儘量使用搭載最新版Systemd的發行版,我在Ubuntu 24.04與Fedora 42測試過,都能正常使用,惟部分細節行為不太一樣。

  2. 要使用systemd-nspawn功能,部份發行版可能需要額外安裝套件

sudo apt install systemd-container
  1. 接著,確保宿主機已經啟用systemd-networkdsystemd-resolved服務,爾後容器才能連上網。
sudo systemctl enable --now systemd-networkd

sudo systemctl enable --now systemd-resolved

3. 準備rootfs,以Debian容器為例
#

  1. 要使用Linux發行版容器,就需要用工具產生rootfs,例如Debian使用debootstrap,Arch Linux配pacbootstrap,Fedora配DNF等等。還有人分享過用docker export解開映像檔當作rootfs的玩法,暫且不提。

  2. 假設我們要建立一個Debian的容器,就得先在宿主機安裝debootstrap:

sudo apt install debootstrap
  1. 建立Debian Stable的rootfs,選取目前的Stable分支(撰文當下是Debian 13),容器建議放在/var/lib/machines/
sudo debootstrap --include=systemd,dbus stable /var/lib/machines/debian

4. 以systemd-nspawn指令進入容器
#

  1. 第一次登入容器內部,以root身分登入。systemd-nspawn預設是以privileged容器執行的。
sudo systemd-nspawn -D /var/lib/machines/debian -U --machine debian
  1. 必須先修改Root密碼
passwd root
  1. 確保容器內有網路
systemctl enable --now systemd-networkd.service
  1. 然後登出。
exit
  1. 進入容器後快速按三下Ctrl + ]退出登入介面。

5. 使用machinectl控制容器
#

幾種啟動systemd-nspawn容器方式:

  • sudo systemctl start systemd-nspawn@debian
  • sudo machinectl start debian
  • sudo systemd-nspawn –boot -U -D /var/lib/machines/debian
  • 撰寫.nspawn設定檔,再透過machinectl啟動容器

systemd-nspawn可看作是一次性登入的指令。使用--boot參數非必要,但只有使用這個參數的時候,讓Systemd成為PID 1,才能夠在容器裡面使用systemctl指令。

其中machinectl是在容器開機之後才能使用的指令,根據官方文件,推薦使用這個來控制容器。

可以用machinectlsystemctl停止容器

systemctl enable設定開機自動啟動容器。

容器啟動之後要用machinectl login才能重新進入終端機

6. 撰寫.nspawn容器設定檔
#

針對個別容器,能夠以.nspawn作結尾的檔案定義其行為。這個檔案語法與Systemd Service Unit一樣。

例如新增/etc/systemd/nspawn/debian.nspawn檔案。在這個檔案裡面能夠定義Debian容器掛載的目錄以及網路設定,就不需要打落落長的systemd-nspawn指令登入了。

在寫好設定檔之後,重新載入設定:sudo systemctl daemon-reload

可以直接用sudo machinectl start debian啟動容器,並用sudo machinectl login debian登入。

7. 容器對外網路設定
#

systemd-nspawn容器預設應該是使用host mode,會吃到宿主機的防火牆規則,能看到宿主機所有網路界面。

/etc/resolv.conf會直接沿用,所以宿主機有開啟systemd-resolved的話,容器內的這個檔案就會被覆蓋。

我們可以在設定檔/etc/systemd/nspawn/debian.nspawn裡面停用此選項,確保容器能直接使用宿主機網路:

[Network]
VirtualEthernet=no

如果要隔離容器網路,需要設定Private Network Mode。這個模式下,容器內部的網路會被隔離,/etc/resolv.conf將不受宿主機影響。要存取容器內部的服務,需要做通訊埠映射。

執行參數是--network-veth,也就是隔離網路模式--private-network。它會在容器啟動之後建立一個ve開頭的虛擬網路介面。

在設定檔/etc/systemd/nspawn/debian.nspawn裡面啟用此選項,確保容器會建立虛擬網路介面:

[Network]
VirtualEthernet=yes

註解:正常來說systemd-nspawn應該是host mode優先才對?我在Fedora 42建立容器能夠直接連上網路。但我在Ubuntu 24.04測試,發現它預設會在容器啟動後建立一個虛擬網路介面。

systemd-nspawn尚支援使用橋接網路或macvlan。

8. 執行圖形程式
#

讓容器內的X11程式能顯示在宿主機桌面上。至於純Wayland程式…?啊哈哈不清楚。

  1. 先用Xephyr生成一個巢狀X11桌面的視窗,DISPLAY環境變數設定為:10
sudo apt install xserver-xephyr

Xephyr -br -ac -noreset -screen 1280x720 :10
  1. 宿主機需要使用xhost指令允許其他X客戶端在目前的桌面開啟視窗。即使宿主機是Wayland桌面,也能用XWayland相容。
xhost +local:
  1. 編輯設定檔/etc/systemd/nspawn/debian.nspawn,設定啟動容器的時候指定DISPLAY的環境變數,並且掛載/dev/dri到容器內部(僅限Intel與AMD顯示卡,Nvidia需要掛載更多)啟用圖形加速。再掛載PulseAudio,讓它能夠發出音效。
[Exec]
Boot=yes
Environment=DISPLAY=:10

[Files]
BindReadOnly=/tmp/.X11-unix/
BindReadOnly=/home/宿主機使用者名稱/.Xauthority
Bind=/dev/dri
Bind=/run/user/1000/pulse:/run/user/1000/pulse
  1. 執行圖形程式前需要在Debian容器內部新增一個使用者,不能使用root。
useradd -m -g users -G wheel,audio,video,storage -s /bin/bash user

passwd user

visudo

su user
  1. 在容器裡面啟動程式
export DISPLAY=:10

export PULSE_SERVER=unix:/run/user/1000/pulse/

firefox-esr

參考資料
#

相關文章

Podlet:將docker-compose轉成Podman Quadlets,以Systemd管理容器
分類   資訊科技 虛擬機與容器技術
標籤   Systemd Podman Docker
FreeBSD跑Linux程式的方法:Linuxulator + Jail
分類   資訊科技 虛擬機與容器技術
標籤   FreeBSD Linux Debootstrap Chroot Rocky Linux Ubuntu
Waydroid使用技巧:解決沒網路、觸控沒反應、註冊Play商店、安裝ARM轉譯器
分類   資訊科技 虛擬機與容器技術
標籤   Waydroid Linux Android

此處提供二種留言板。點選按鈕,選擇您覺得方便的留言板。

(留言板載入中)這是Giscus留言板,需要Github帳號才能留言。支援Markdown語法,若要上傳圖片請善用外部圖床。您的留言會在Github Discussions向所有人公開。

Click here to edit your comments.

(留言板載入中)這是Disqus留言板,您可能會看到Disqus強制投放的廣告。為防止垃圾內容,有時留言可能會被系統判定需審核,導致延遲顯示,請見諒。若要上傳圖片請善用外部圖床網站。