systemd-nspawn
systemd-nspawn 跟 chroot 命令類似,是個終極版的 chroot。
systemd-nspawn 可以在輕量的命名空間容器中運行命令或系統。它比 chroot 強大的地方是,它完全虛擬了文件系統層次結構、進程樹、各種 IPC 子系統以及主機名。
systemd-nspawn 將容器中各種內核接口的訪問限制為只讀,像是 /sys
, /proc/sys
和 /sys/fs/selinux
。網絡接口和系統時鐘不能從容器內更改,不能創建設備節點。不能從容器中重啟宿主機,也不能加載內核模塊。
相比 LXC 或 Libvirt, systemd-nspawn 更容易配置。
安裝[編輯 | 編輯原始碼]
systemd-nspawn 是 systemd 的一部分並被打進 systemd包 軟體包中。
用例[編輯 | 編輯原始碼]
創建並啟動最小 Arch Linux 容器[編輯 | 編輯原始碼]
首先安裝 arch-install-scripts包。
然後,創建一個目錄來保存容器。在這個用例中我們將使用 ~/MyContainer
。
接下來,我們使用 pacstrap 在容器中安裝一個基本的Arch系統。其中至少需要安裝包組 base包。
# pacstrap -K -c ~/MyContainer base [additional packages/groups]
一旦安裝完成後,進入容器中並設置root密碼:
# systemd-nspawn -D ~/MyContainer # passwd # logout
machinectl shell root@MyContainer
直接獲取一個 root shell ,並不需要登錄。最後, 啟動容器:
# systemd-nspawn -b -D ~/MyContainer
參數 -b
將會啟動這個容器(比如:以 PID=1 運行 systemd
), 而不是僅僅啟動一個 shell, 而參數 -D
指定成為容器根目錄的目錄。
容器啟動後,輸入密碼以"root"身份登錄。
securetty
TTY 設備白名單導致的。 請查看#Root登錄失敗。可以在容器內運行 poweroff
來關閉容器。在主機端,容器可以通過 machinectl 工具進行控制。
Ctrl
並快速地按 ]
三下。創建 Debian 或 Ubuntu 環境[編輯 | 編輯原始碼]
安裝 debootstrap包 以及 debian-archive-keyring包 或 ubuntu-keyring包 中的一個或兩個(當然要安裝你想要的發行版的keyring)。
然後通過以下結構式調用 deboostrap:
# debootstrap codename container-name repository-url
- codename:對於 Debian,有效的代碼名稱要不然是穩定性別名如
stable
、testing
或unstable
,要不然就是版本名稱如bookworm
或sid
。你可以從這裡獲取名稱列表:[1]。對於 Ubuntu:只有像是jammy
或noble
這樣的名稱應該被使用而不是版本號。你可以從 [2] 或者 [3] 獲取一份代碼名稱與版本號的對應表格。
- container-name 是會包含作業系統文件樹的目錄,如果它不存在的話將會被創建。
- repository-url:下載作業系統文件樹的鏡像地址。目前 Debian 發布在任何有效鏡像站,比如說 CDN 支持的 https://deb.debian.org/debian 。 Ubuntu 的鏡像站在 [4],比如說 http://archive.ubuntu.com/ubuntu 。
- 如果你需要一個以歸檔的 Debian 版本(指的是任何在2024年8月,Debian 10/Buster 之前的版本),請使用一個特殊的 debian-archive 鏡像地址:https://archive.debian.org/debian/ 。Ubuntu 應該使用 https://old-releases.ubuntu.com/ubuntu/ 。
- systemd-nspawn 要求容器中的作業系統使用 systemd 作為其 init 程序(以 PID 1 運行)。從 Debian 8 (「jessie」) [5] 以及 Ubuntu 15.04 (「vivid」)[6] 之後,這是兩者的默認 init 程序。但請注意,對於不包含在上述密鑰包中的版本,會出現與 「未知簽名密鑰(unknown signing key)」相關的問題,比如任何早於 Debian 9 (「Stretch」)[7] 的發行版本。這使得後者成為成為無需額外操作即可安裝的最老版本。
與 Arch 相同,Debian 和 Ubuntu 不會讓您在首次登錄時無密碼登錄。為了設置root密碼登錄,要不使用"-b"參數並設置密碼:
# cd /var/lib/machines # systemd-nspawn -D ./container-name # passwd # logout
systemd-container
時使用 --include=
選項,這始終是 dbus 和 systemd 的依賴項:
# debootstrap --include=systemd-container codename /var/lib/machines/container-name repository-url
Create a Fedora or AlmaLinux environment[編輯 | 編輯原始碼]
Install dnf包, and edit the /etc/dnf/dnf.conf
file to add the required Fedora repositories.
/etc/dnf/dnf.conf
[fedora] name=Fedora $releasever - $basearch metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch gpgkey=https://getfedora.org/static/fedora.gpg [updates] name=Fedora $releasever - $basearch - Updates metalink=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f$releasever&arch=$basearch gpgkey=https://getfedora.org/static/fedora.gpg
The fedora.gpg file contain the gpg keys for the latest Fedora releases https://getfedora.org/security/. To set up a minimal Fedora 37 container:
# mkdir /var/lib/machines/container-name # dnf --releasever=37 --best --setopt=install_weak_deps=False --repo=fedora --repo=updates --installroot=/var/lib/machines/container-name install dhcp-client dnf fedora-release glibc glibc-langpack-en iputils less ncurses passwd systemd systemd-networkd systemd-resolved util-linux vim-default-editor
If you are using btrfs filesystem create a subvolume instead of creating a directory.
An Enterprise Linux derivative like AlmaLinux has three repositories enabled by default, BaseOS wich contains a core set that provides the basis for all installations, AppStream that includes additional applications, language packages, etc and Extras that contains packages not included in RHEL. So for a minimal container we only need to add the BaseOS repository to /etc/dnf/dnf.conf
/etc/dnf/dnf.conf
[baseos] name=AlmaLinux $releasever - BaseOS mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-$releasever
To create an AlmaLinux 9 minimal container:
# dnf --repo=baseos --releasever=9 --best --installroot=/var/lib/machines/container-name --setopt=install_weak_deps=False install almalinux-release dhcp-client dnf glibc-langpack-en iproute iputils less passwd systemd vim-minimal
This will install the latest minor version of AlmaLinux 9, you can choose to install a specific point release, but you will need to change the gpgpkey entry to manually point to RPM-GPG-KEY-AlmaLinux-9
Just like Arch, Fedora or AlmaLinux will not let you log in as root without a password. To set up the root password, run systemd-nspawn without the -b
option:
# systemd-nspawn -D /var/lib/machines/container-name passwd
編譯與測試包[編輯 | 編輯原始碼]
請參閱 Creating packages for other distributions 尋找更多用例.
管理[編輯 | 編輯原始碼]
位於 /var/lib/machines/
的容器可以被 machinectl 命令所控制,它可以內部控制 systemd-nspawn@.service
單元的實例。 /var/lib/machines/
下的子目錄對應著容器的名字, 比如 /var/lib/machines/container-name/
。
/var/lib/machines/
, 可以使用符號連結。 詳情看 machinectl(1) § FILES AND DIRECTORIES 。默認 systemd-nspawn 選項[編輯 | 編輯原始碼]
要明白非常重要的一點是通過 machinectl 與 systemd-nspawn@.service
啟動的容器所使用的默認選項與通過 systemd-nspawn 命令手動啟動的有所不同。 通過服務啟動所使用的額外選項有:
-b
/--boot
– 管理的容器會自動搜索一個init程序,並以PID 1的形式調用它。--network-veth
關聯於--private-network
– 管理的容器獲得一個虛擬網絡接口,並與主機網絡斷開連接。 詳情看 #網絡。-U
– 如果內核支持,管理的容器默認使用 user_namespaces(7) 特性。 解釋請看 #無特權容器。--link-journal=try-guest
這些行為可以在每個容器配置文件中被覆蓋, 詳情看 #配置。
machinectl[編輯 | 編輯原始碼]
容器可以被 machinectl subcommand container-name
命令管理, 比如說,啟動容器:
$ machinectl start container-name
machinectl start container_name
時將會報錯 Invalid machine name container_name
。詳情請看 [9] 與 [10] 。相似的, 還有其他的子命令 poweroff
, reboot
, status
和 show
. 詳細解釋請看 machinectl(1) § Machine Commands 。
poweroff
and reboot
命令執行。其他常見命令:
machinectl list
– 顯示現在正在運行的容器machinectl login container-name
– 在一個容器中打開一個交互式登錄會話machinectl shell [username@]container-name
– 在容器中打開一個交互式shell會話(這將立即調用一個用戶進程,而不需要通過容器中的登錄進程)machinectl enable container-name
與machinectl disable container-name
– 啟用或禁用容器的隨開機啟動,詳見 #啟用容器的隨開機啟動
machinectl 也有管理容器(或虛擬機)鏡像和鏡像傳輸的子命令。 詳情見 machinectl(1) § Image Commands 與 machinectl(1) § Image Transfer Commands 。
systemd 工具鏈[編輯 | 編輯原始碼]
大部分核心的 systemd 工具鏈已更新為對容器有效。工具們通常提供了 -M, --machine=
選項並把容器名稱作為參數。
用例:
查看特定容器的 journal 日誌:
# journalctl -M MyContainer
顯示控制組的內容:
$ systemd-cgls -M MyContainer
查看容器的啟動時間:
$ systemd-analyze -M MyContainer
查看資源使用情況的概況:
$ systemd-cgtop
配置[編輯 | 編輯原始碼]
容器前設置[編輯 | 編輯原始碼]
要指定容器前設置而不是全局覆蓋,可以使用.nspawn文件。 詳情見 systemd.nspawn(5) 。
啟用容器的隨開機啟動[編輯 | 編輯原始碼]
當頻繁使用一個容器時,你可能希望在系統啟動時啟動它。
首先確保 machines.target
已經 enabled。
容器被 machinectl 的可發現可以 enabled 或者 disabled。
$ machinectl enable container-name
- 這樣做的效果是啟用
systemd-nspawn@container-name.service
systemd單元。 - 如#默認 systemd-nspawn 選項中提到的,由machinectl啟動的容器會獲得一個虛擬的乙太網接口。要禁用私有網絡,請參見#使用主機網絡。
資源控制[編輯 | 編輯原始碼]
您可以使用控制組來使用 systemctl set-property
實現對容器的限制和資源管理,請參閱 systemd.resource-control(5)。例如,您可能希望限制內存量或 CPU 使用率。要將容器的內存消耗限制為 2 GiB:
# systemctl set-property systemd-nspawn@myContainer.service MemoryMax=2G
或者將 CPU 時間使用率限制為大約相當於 2 個內核:
# systemctl set-property systemd-nspawn@myContainer.service CPUQuota=200%
這將在 /etc/systemd/system.control/systemd-nspawn@myContainer.service.d/
中創建常駐文件}。
根據文檔,MemoryHigh
是控制內存消耗的首選方法,但也不會像 MemoryMax
那樣進行強制限制。您可以使用這兩個選項將 MemoryMax
作為您最後的一道防線。還要考慮到如果您不會限制容器可以「看到」的 CPU 數量,但通過限制容器相對於總 CPU 時間的最大時間,也可以獲得類似的結果。
--runtime
使更改成為臨時性的。您可以使用 systemd-cgtop
檢查其結果。網絡[編輯 | 編輯原始碼]
systemd-nspawn 容器可以使用主機網絡或者私有網絡:
- 在主機網絡模式下,容器可以完全訪問主機網絡。這意味著容器將能夠訪問主機上的所有網絡服務,來自容器的數據包將在外部網絡中顯示為來自主機(即共享同一IP位址)。
- 在私有網絡模式下,容器與主機的網絡斷開連接,這使得容器無法使用所有網絡接口,但環回設備和明確分配給容器的接口除外。為容器設置網絡接口有多種不同的方法:
- 可以將現有接口分配給容器(例如,如果您有多個乙太網設備)。
- 可以創建一個與現有接口(即VLAN接口)相關聯的虛擬網絡接口,並將其分配給容器。
- 可以創建主機和容器之間的虛擬乙太網連結。
- 在後一種情況下,容器的網絡是完全隔離的(與外部網絡以及其他容器),由管理員來配置主機和容器之間的網絡。這通常涉及創建一個網橋network bridge來連接多個(物理或虛擬)接口,或者在多個接口之間設置一個NATNetwork Address Translation。
主機網絡模式適用於應用程式容器,它不運行任何網絡軟體來配置分配給容器的接口。當你從shell運行systemd-nspawn時,主機聯網是默認模式。
另一方面,私有網絡模式適用於應與主機系統隔離的 "系統容器"。創建虛擬乙太網鏈路是一個非常靈活的工具,可以創建複雜的虛擬網絡。這是由machinectl或systemd-nspawn@.service
啟動的容器的默認模式。
下面的小節描述了常見的情況。關於可用的systemd-nspawn選項,請參見systemd-nspawn(1) § Networking Options。
使用主機網絡[編輯 | 編輯原始碼]
要禁用私有網絡和創建由machinectl啟動的容器使用的虛擬乙太網連結,請添加.nspawn文件,其中包含以下選項:
/etc/systemd/nspawn/container-name.nspawn
[Network] VirtualEthernet=no
這將覆蓋在systemd-nspawn@.service
中使用的-n
/--network-veth
選項,新啟動的容器將使用主機網絡模式。
使用虛擬乙太網連結[編輯 | 編輯原始碼]
如果使用-n
/--network-veth
選項啟動容器,systemd-nspawn將在主機和容器之間創建一個虛擬的乙太網連結。連結的主機側將作為名為ve-容器名稱
的網絡接口提供。鏈路的容器側將被命名為host0
。請注意,這個選項意味著 --private-network
。
- 如果容器名稱太長,接口名稱將被縮短(例如
ve-long-conKQGh
而不是ve-long-container-name
),以適應15個字符的限制。全名將被設置為接口的altname
屬性(見ip-link(8)),並且仍然可以用來引用接口。 - 當檢查帶有
ip link
的接口時,接口名會顯示一個後綴,例如ve-容器名@if2
和host0@if9
。@ifN
實際上並不是接口名稱的一部分,相反,ip link
附加了這個信息,以指示虛擬乙太網電纜連接到另一端的哪個 "槽"。
- 例如,顯示為
ve-foo@if2
的主機虛擬乙太網接口與容器foo
相連,在容器內部與第二個網絡接口--在容器內部運行ip link
時,顯示索引為2的接口。同理,容器內名為host0@if9
的接口連接到主機上的第9個網絡接口。
當您啟動容器時,必須為兩個接口(主機上和容器中)分配一個 IP 地址。如果您在主機上和容器中都使用systemd-networkd,這就是開箱即用:
- 主機上的
/usr/lib/systemd/network/80-container-ve.network
文件與ve-container-name
接口相匹配,並啟動一個DHCP伺服器,該伺服器為主機接口和容器分配IP位址, - 容器中的
/usr/lib/systemd/network/80-container-host0.network
文件與host0
接口相匹配,並啟動一個DHCP客戶端,該客戶端從主機接收一個IP位址。
如果不使用 systemd-networkd,可以配置靜態 IP 地址或在主機接口上啟動 DHCP 伺服器,在容器中啟動 DHCP 客戶端。詳情請參見網絡配置。
要讓容器訪問外部網絡,您可以按照 Internet sharing#Enable NAT 中的描述配置 NAT。如果您使用 systemd-networkd,這將通過 /usr/lib/systemd/network/80-container-ve.network
中的 IPMasquerade=yes
選項自動(部分)完成。然而,這只會發出一個iptables規則,比如說:
-t nat -A POSTROUTING -s 192.168.163.192/28 -j MASQUERADE
filter
表必須手動配置,如Internet sharing#Enable NAT所示。您可以使用通配符來匹配所有以ve-
開頭的接口:
# iptables -A FORWARD -i ve-+ -o internet0 -j ACCEPT
並且,為了接收DHCP伺服器的入站連接,你需要在 ve-+
接口上開放 UDP 67 埠(由 systemd-networkd 操控):
# iptables -A INPUT -i ve-+ -p udp -m udp --dport 67 -j ACCEPT
使用網絡橋接[編輯 | 編輯原始碼]
如果您已在主機系統上配置了網橋network bridge,則可以為容器創建一個虛擬乙太網鏈路,並將其主機側添加到網橋中。這可以通過--network-bridge=bridge-name
選項來完成。請注意,--network-bridge
意味著--network-veth
,即虛擬乙太網鏈路是自動創建的。然而,鏈路的主機端將使用vb-
前綴而不是ve-
,因此用於啟動DHCP伺服器和IP偽裝的systemd-networkd選項將不會被應用。
網橋的管理由管理員負責。例如,網橋可以用一個物理接口連接虛擬接口,也可以只連接幾個容器的虛擬接口。參見systemd-networkd#Network bridge with DHCP和systemd-networkd#Network bridge with static IP addresses,了解使用systemd-networkd進行配置的例子。
還有一個--network-zone=zone-name
選項,它與--network-bridge
類似,但網橋由systemd-nspawn和systemd-networkd自動管理。當第一個用vz-zone-name
配置的容器啟動時,會自動創建名為--network-zone=zone-name
的網橋接口,當最後一個用--network-zone=zone-name
配置的容器退出時,會自動刪除。因此,這個選項可以方便地將多個相關的容器放置在一個共同的虛擬網絡上。請注意,vz-*
接口由systemd-networkd管理的方式與ve-*
接口相同,使用/usr/lib/systemd/network/80-container-vz.network
文件中的選項。
使用 "macvlan" 或者 "ipvlan" 接口[編輯 | 編輯原始碼]
您可以在現有的物理接口(即VLAN接口)上創建一個虛擬接口,並將其添加到容器中,而不是創建一個虛擬的乙太網鏈路(其主機端可能被添加到橋接中,也可能沒有)。該虛擬接口將與底層主機接口進行橋接,從而使容器暴露在外部網絡中,從而使其能夠通過DHCP從與主機相連的同一區域網中獲得一個獨特的IP位址。
systemd-nspawn 提供兩個選項:
--network-macvlan=interface
– 虛擬接口的MAC地址將與底層物理interface
不同,並被命名為mv-interface
。--network-ipvlan=interface
– 虛擬接口的MAC地址將與底層物理interface
相同,並命名為iv-interface
。
所有選項都意味著 --private-network
.
使用現有接口[編輯 | 編輯原始碼]
如果主機系統有多個物理網絡接口,可以使用--network-interface=interface
將interface
分配給容器(並使它在容器啟動時對主機不可用)。請注意,--network-interface
意味著--private-network
。
埠映射[編輯 | 編輯原始碼]
當啟用私有網絡時,可以使用-p
/-port
選項或使用.nspawn文件中的Port
設置將主機上的各個埠映射到容器上的埠。這可以通過向nat
表發出iptables規則來完成,但需要手動配置forward
表中的filter
鏈,如#使用虛擬乙太網連結所示。
例如,將主機上的TCP埠8000映射到容器中的TCP埠80:
/etc/systemd/nspawn/container-name.nspawn
[Network] Port=tcp:8000:80
域名解析[編輯 | 編輯原始碼]
容器中的域名解析 Domain name resolution 可以以主機系統中相同的方式配置。另外,systemd-nspawn 提供了管理容器中 /etc/resolv.conf
文件的選項:
--resolv-conf
can be used on command-lineResolvConf=
can be used in .nspawn files
這些相應的選項有許多可能的值,在 systemd-nspawn(1) § Integration Options 的描述中會提到。 默認值為 auto
,這意味著:
- 如果啟用了
--private-network
,/etc/resolv.conf
就會保持容器中的原樣。 - 否則,如果systemd-resolved在主機上運行,它的存根文件
resolv.conf
將被複製或綁定到容器中。 - 否則,
/etc/resolv.conf
文件就會被從主機複製或綁定到容器上。
在後兩種情況下,如果容器根部是可寫的,則複製文件,如果是只讀的,則綁定掛載。
對於第二種 systemd-resolved 在宿主機上運行的情況, systemd-nspawn 希望它也能在容器中運行,所以容器將會從宿主中 /etc/resolv.conf
文件建立樁符號連結。如果不是這樣,那麼默認值 auto
將不會生效,你應該使用任意 replace-*
選項來替換這個連結。
提示和技巧[編輯 | 編輯原始碼]
運行 non-shell/init 命令[編輯 | 編輯原始碼]
來自 systemd-nspawn(1) § Execution_Options:
[選項]
--as-pid2
[調用] shell 或以進程ID (PID) 2 而不是 PID 1 (init) 來運行特定程序。 [...] 建議使用這種模式來調用容器中的任意命令,除非它們已經被修改為以PID 1的形式正確運行。或者換句話說:這個改變應該被用於幾乎所有的命令,除非該命令是指一個init或shell的實現。
無特權容器[編輯 | 編輯原始碼]
systemd-nspawn支持無特權的容器,不過容器需要以root身份啟動。
最簡單的方法是通過-U
選項讓systemd-nspawn自動選擇一個未使用的UID/GID範圍。
# systemd-nspawn -bUD ~/MyContainer
如果內核支持用戶命名空間,-U
選項相當於--private-users=pick --private-users-ownership=auto
。詳情請參見systemd-nspawn(1) § User Namespacing Options。
如果一個容器曾使用 --private-users-ownership=chown
選項(或者在一個 -U
需要 --private-users-ownership=chown
的文件系統中)在私有UID/GID範圍啟動,就需要一直這樣使用它以避免權限錯誤。另外,也可以在一個ID範圍從0開始的容器文件系統中撤銷--private-users-ownership=chown
的效果:
# systemd-nspawn -D ~/MyContainer --private-users=0 --private-users-ownership=chown
使用 X 環境[編輯 | 編輯原始碼]
詳情見 Xhost 和 Change root#Run graphical applications from chroot.
您需要設置您容器會話中 DISPLAY
以連接到外部 X 伺服器。
X 在 /tmp
文件夾中存儲一些必要的文件。為了使容器能顯示任何內容,它需要訪問這些文件。為此,當啟動容器時,請追加 --bind-ro=/tmp/.X11-unix
選項。
/tmp/.X11-unix
的內容 必須以只讀方式裝入,否則它們將從文件系統中消失。只讀掛載標誌不會阻止在套接字上使用 connect()
。如果你還綁定了/run/user/1000
那麼你有可能希望顯式綁定 /run/user/1000/bus
為只讀,以防止 dbus 套接字被刪除。避免 xhost[編輯 | 編輯原始碼]
xhost
僅提供對 X 伺服器相當粗糙的訪問權限。更細節的訪問可通過$XAUTHORITY
文件控制。遺憾的是, 僅使 $XAUTHORITY
文件在容器中可被訪問並無法正常的執行工作:
這是因為您的 $XAUTHORITY
文件只特定於您的主機,但是容器是另一台主機。
根據 stackoverflow 下面這個技巧可以讓你的X伺服器接受來自於你容器中的X應用的 $XAUTHORITY
文件:
$ XAUTH=/tmp/container_xauth $ xauth nextract - "$DISPLAY" | sed -e 's/^..../ffff/' | xauth -f "$XAUTH" nmerge - # systemd-nspawn -D myContainer --bind=/tmp/.X11-unix --bind="$XAUTH" -E DISPLAY="$DISPLAY" -E XAUTHORITY="$XAUTH" --as-pid2 /usr/bin/xeyes
上面第二行將連接的組設定為 "FamilyWild",GID為65535
,會使輸入匹配你的每一個顯示。 更多信息請參考 Xsecurity(7) 。
使用 X 嵌套/Xephyr[編輯 | 編輯原始碼]
另一個運行X應用程式並避免共享X桌面風險的簡單方法是使用X嵌套。 這裡的優點是完全避免了容器內應用程式和非容器內應用程式之間的交互,並且能夠運行不同的 desktop environment 或 window manager,缺點是使用 Xephyr 時性能較差,並且缺乏硬體加速。 在容器外啟動 Xephyr:
# Xephyr :1 -resizeable
然後在使用下面的參數啟動容器:
--setenv=DISPLAY=:1 --bind-ro=/tmp/.X11-unix/X1
不需要其他綁定。
在某些情況下,你可能仍然需要在容器中手動設置 DISPLAY=:1
(主要是在與 b
一起使用時)。
運行 Firefox[編輯 | 編輯原始碼]
以PID 1運行
# systemd-nspawn --setenv=DISPLAY=:0 \ --setenv=XAUTHORITY=~/.Xauthority \ --bind-ro=$HOME/.Xauthority:/root/.Xauthority \ --bind=/tmp/.X11-unix \ -D ~/containers/firefox \ --as-pid2 \ firefox
--user <username>
選項。或者你可以啟動容器,讓systemd-networkd等設置虛擬網絡接口:
# systemd-nspawn --bind-ro=$HOME/.Xauthority:/root/.Xauthority \ --bind=/tmp/.X11-unix \ -D ~/containers/firefox \ --network-veth -b
一旦你的容器被啟動,就像這樣運行Xorg二進位文件:
# systemd-run -M firefox --setenv=DISPLAY=:0 firefox
3D圖形加速[編輯 | 編輯原始碼]
為了啟用加速的3D圖形,可能有必要在 .nspawn 文件中加入以下一行,將 /dev/dri
掛載到容器中。
Bind=/dev/dri
上述技巧來自patrickskiba.com。這將明顯解決了以下問題
libGL error: MESA-LOADER: failed to retrieve device information libGL error: Version 4 or later of flush extension not found libGL error: failed to load driver: i915
你可以通過運行 glxinfo
或 glxgears
來確認它已經被啟用。
Nvidia GPUs[編輯 | 編輯原始碼]
如果你不能在容器上安裝與主機上相同的nvidia驅動版本,你可能需要同時綁定驅動庫文件。你可以在主機上運行 pacman -Ql nvidia-utils
來查看它包含的所有文件。你不需要把所有文件都複製過來。當容器通過 machinectl start container-name
運行時,下面的systemd覆蓋文件將把所有必要的文件綁定過來。
/etc/systemd/system/systemd-nspawn@.service.d/nvidia-gpu.conf
[Service] ExecStart= ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --machine=%i \ --bind=/dev/dri \ --bind=/dev/shm \ --bind=/dev/nvidia0 \ --bind=/dev/nvidiactl \ --bind=/dev/nvidia-modeset \ --bind=/usr/bin/nvidia-bug-report.sh:/usr/bin/nvidia-bug-report.sh \ --bind=/usr/bin/nvidia-cuda-mps-control:/usr/bin/nvidia-cuda-mps-control \ --bind=/usr/bin/nvidia-cuda-mps-server:/usr/bin/nvidia-cuda-mps-server \ --bind=/usr/bin/nvidia-debugdump:/usr/bin/nvidia-debugdump \ --bind=/usr/bin/nvidia-modprobe:/usr/bin/nvidia-modprobe \ --bind=/usr/bin/nvidia-ngx-updater:/usr/bin/nvidia-ngx-updater \ --bind=/usr/bin/nvidia-persistenced:/usr/bin/nvidia-persistenced \ --bind=/usr/bin/nvidia-powerd:/usr/bin/nvidia-powerd \ --bind=/usr/bin/nvidia-sleep.sh:/usr/bin/nvidia-sleep.sh \ --bind=/usr/bin/nvidia-smi:/usr/bin/nvidia-smi \ --bind=/usr/bin/nvidia-xconfig:/usr/bin/nvidia-xconfig \ --bind=/usr/lib/gbm/nvidia-drm_gbm.so:/usr/lib/x86_64-linux-gnu/gbm/nvidia-drm_gbm.so \ --bind=/usr/lib/libEGL_nvidia.so:/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so \ --bind=/usr/lib/libGLESv1_CM_nvidia.so:/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so \ --bind=/usr/lib/libGLESv2_nvidia.so:/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so \ --bind=/usr/lib/libGLX_nvidia.so:/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so \ --bind=/usr/lib/libcuda.so:/usr/lib/x86_64-linux-gnu/libcuda.so \ --bind=/usr/lib/libnvcuvid.so:/usr/lib/x86_64-linux-gnu/libnvcuvid.so \ --bind=/usr/lib/libnvidia-allocator.so:/usr/lib/x86_64-linux-gnu/libnvidia-allocator.so \ --bind=/usr/lib/libnvidia-cfg.so:/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so \ --bind=/usr/lib/libnvidia-egl-gbm.so:/usr/lib/x86_64-linux-gnu/libnvidia-egl-gbm.so \ --bind=/usr/lib/libnvidia-eglcore.so:/usr/lib/x86_64-linux-gnu/libnvidia-eglcore.so \ --bind=/usr/lib/libnvidia-encode.so:/usr/lib/x86_64-linux-gnu/libnvidia-encode.so \ --bind=/usr/lib/libnvidia-fbc.so:/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so \ --bind=/usr/lib/libnvidia-glcore.so:/usr/lib/x86_64-linux-gnu/libnvidia-glcore.so \ --bind=/usr/lib/libnvidia-glsi.so:/usr/lib/x86_64-linux-gnu/libnvidia-glsi.so \ --bind=/usr/lib/libnvidia-glvkspirv.so:/usr/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so \ --bind=/usr/lib/libnvidia-ml.so:/usr/lib/x86_64-linux-gnu/libnvidia-ml.so \ --bind=/usr/lib/libnvidia-ngx.so:/usr/lib/x86_64-linux-gnu/libnvidia-ngx.so \ --bind=/usr/lib/libnvidia-opticalflow.so:/usr/lib/x86_64-linux-gnu/libnvidia-opticalflow.so \ --bind=/usr/lib/libnvidia-ptxjitcompiler.so:/usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so \ --bind=/usr/lib/libnvidia-rtcore.so:/usr/lib/x86_64-linux-gnu/libnvidia-rtcore.so \ --bind=/usr/lib/libnvidia-tls.so:/usr/lib/x86_64-linux-gnu/libnvidia-tls.so \ --bind=/usr/lib/libnvidia-vulkan-producer.so:/usr/lib/x86_64-linux-gnu/libnvidia-vulkan-producer.so \ --bind=/usr/lib/libnvoptix.so:/usr/lib/x86_64-linux-gnu/libnvoptix.so \ --bind=/usr/lib/modprobe.d/nvidia-utils.conf:/usr/lib/x86_64-linux-gnu/modprobe.d/nvidia-utils.conf \ --bind=/usr/lib/nvidia/wine/_nvngx.dll:/usr/lib/x86_64-linux-gnu/nvidia/wine/_nvngx.dll \ --bind=/usr/lib/nvidia/wine/nvngx.dll:/usr/lib/x86_64-linux-gnu/nvidia/wine/nvngx.dll \ --bind=/usr/lib/nvidia/xorg/libglxserver_nvidia.so:/usr/lib/x86_64-linux-gnu/nvidia/xorg/libglxserver_nvidia.so \ --bind=/usr/lib/vdpau/libvdpau_nvidia.so:/usr/lib/x86_64-linux-gnu/vdpau/libvdpau_nvidia.so \ --bind=/usr/lib/xorg/modules/drivers/nvidia_drv.so:/usr/lib/x86_64-linux-gnu/xorg/modules/drivers/nvidia_drv.so \ --bind=/usr/share/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf:/usr/share/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf \ --bind=/usr/share/dbus-1/system.d/nvidia-dbus.conf:/usr/share/dbus-1/system.d/nvidia-dbus.conf \ --bind=/usr/share/egl/egl_external_platform.d/15_nvidia_gbm.json:/usr/share/egl/egl_external_platform.d/15_nvidia_gbm.json \ --bind=/usr/share/glvnd/egl_vendor.d/10_nvidia.json:/usr/share/glvnd/egl_vendor.d/10_nvidia.json \ --bind=/usr/share/licenses/nvidia-utils/LICENSE:/usr/share/licenses/nvidia-utils/LICENSE \ --bind=/usr/share/vulkan/icd.d/nvidia_icd.json:/usr/share/vulkan/icd.d/nvidia_icd.json \ --bind=/usr/share/vulkan/implicit_layer.d/nvidia_layers.json:/usr/share/vulkan/implicit_layer.d/nvidia_layers.json \ DeviceAllow=/dev/dri rw DeviceAllow=/dev/shm rw DeviceAllow=/dev/nvidia0 rw DeviceAllow=/dev/nvidiactl rw DeviceAllow=/dev/nvidia-modeset rw
ldconfig
以更新庫。訪問主機文件系統[編輯 | 編輯原始碼]
請見 --bind
和 --bind-ro
於 systemd-nspawn(1).
如果主機和容器都是 Arch Linux,則例如,可以共享 pacman 緩存:
# systemd-nspawn --bind=/var/cache/pacman/pkg
或許你還可以使用文件來進行指定的先於容器的綁定:
/etc/systemd/nspawn/my-container.nspawn
[Files] Bind=/var/cache/pacman/pkg
詳情見 #容器前設置.
要將該目錄綁定到容器內的不同路徑,請添加路徑,並用冒號分隔。例如:
# systemd-nspawn --bind=/path/to/host_dir:/path/to/container_dir
如果是 #無特權容器,產生的掛載點將由nobody用戶擁有。這可以通過 idmap
掛載選項來修改。
# systemd-nspawn --bind=/path/to/host_dir:/path/to/container_dir:idmap
在非systemd系統上運行[編輯 | 編輯原始碼]
詳情見 Init#systemd-nspawn。
使用Btrfs子卷作為容器的根[編輯 | 編輯原始碼]
要使用 Btrfs subvolume 作為容器根目錄的模板,請使用 --template
標誌。這將獲取子卷的快照,並以它填充容器的根目錄。
例如,要使用位於/.snapshots/403/snapshot
的快照:
# systemd-nspawn --template=/.snapshots/403/snapshots -b -D my-container
其中my-container
是將為容器創建的目錄的名稱。關機後,會保留新創建的子卷。
使用容器的臨時Btrfs快照[編輯 | 編輯原始碼]
可以使用--ephemeral
或-x
標誌來創建容器的臨時btrfs快照,並將其作為容器的根。在容器中啟動時所作的任何更改都將丟失。例如:
# systemd-nspawn -D my-container -xb
其中my-container是現有容器或系統的目錄。例如,如果/
是一個btrfs子卷,我們可以通過以下操作創建一個當前運行的主機系統的短暫容器:
# systemd-nspawn -D / -xb
關閉容器電源後,創建的btrfs子卷會立即被刪除。
在 systemd-nspawn 中運行 docker[編輯 | 編輯原始碼]
自 Docker 20.10以來,在啟用 cgroups v2 (Arch Linux默認)的情況下,可以在無特權的 systemd-nspawn 容器內運行Docker容器,而不會因為禁用cgroups和用戶名字空間而破壞安全措施。要做到這一點,請編輯或創建 /etc/systemd/nspawn/myContainer.nspawn
,並添加以下配置。
/etc/systemd/nspawn/myContainer.nspawn
[Exec] SystemCallFilter=add_key keyctl bpf
然後,Docker 應該在容器內按原樣工作。
由於 overlayfs 不能用於用戶命名空間,並且在 systemd-nspawn 中不可用,默認情況下,Docker 會退回到使用低效的 vfs 作為其存儲驅動,每次啟動容器都會創建一個鏡像的副本。這一點可以通過使用 fuse-overlayfs 作為其存儲驅動來解決。要做到這一點,我們需要首先將 fuse 暴露給容器。
/etc/systemd/nspawn/myContainer.nspawn
[Files] Bind=/dev/fuse
然後允許容器讀取和寫入設備節點。
# systemctl set-property systemd-nspawn@myContainer DeviceAllow='/dev/fuse rwm'
最後,在容器中安裝 fuse-overlayfs包 包。你需要重新啟動容器以使所有配置生效。
疑難解答[編輯 | 編輯原始碼]
Root登錄失敗[編輯 | 編輯原始碼]
如果您在嘗試登錄時(即使用machinectl login <name>
)得到以下錯誤:
arch-nspawn login: root Login incorrect
並且容器中journal
顯示:
pam_securetty(login:auth): access denied: tty 'pts/0' is not secure !
只刪除容器文件系統中的/etc/securetty
[16]和/usr/share/factory/etc/securetty
,或者也可以根據需要只簡單地向容器文件系統中 /etc/securetty
添加所需的pty終端設備(如 pts/0
)。
但是任何改變都會在下一次啟動時被覆蓋,因此有必要同時在容器文件系統上的 /usr/lib/tmpfiles.d/arch.conf
文件中刪除 /etc/securetty
條目,見FS#63236。如果你選擇刪除,也可以在 /etc/pacman.conf
中選擇將這些文件列入黑名單(NoExtract),以防止它們被重新安裝。詳見 FS#45903。
execv(...) failed: Permission denied[編輯 | 編輯原始碼]
當試圖通過systemd-nspawn -bD /path/to/container
來啟動容器時(或在容器中執行一些東西),出現以下錯誤:
execv(/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init) failed: Permission denied
即使有關文件的權限(即 /lib/systemd/systemd
)是正確的,這也可能是由於以非root用戶的身份掛載容器所在的文件系統造成的。例如,如果您在fstab中手動掛載了具有noauto,user,...
選項的磁碟,systemd-nspawn將不允許執行文件,即使它們是由root用戶擁有的。
TERM中的終端類型不正確(破損的顏色)[編輯 | 編輯原始碼]
當通過machinectl login
登錄容器時,容器內的終端中的顏色和按鍵可能會被破壞。這可能是由於TERM
環境變量中的終端類型不正確。除非明確配置,否則環境變量不會從主機上的 shell 繼承,而是回到 systemd 中固定的默認值 (vt220
)。要配置,在容器內為 container-getty@.service
systemd 服務創建一個配置覆蓋,啟動 machinectl login
的登錄 getty,並將 TERM
設置為與您登錄的主機終端匹配的值:
/etc/systemd/system/container-getty@.service.d/term.conf
[Service] Environment=TERM=xterm-256color
或者使用machinectl shell
。它正確地繼承了終端的TERM
環境變量。
在容器內掛載NFS共享[編輯 | 編輯原始碼]
目前(2019年6月)還不可能
曲線救國的方式是使用主機掛載NFS後通過目錄映射到容器目錄,NFS掛載方式請查看 NFS#Client。掛載後會遇到權限錯誤,這是因為容器的用戶ID是通過主機映射過去的,詳情請查看:[17], 解決這個問題的方法有很多種,如下面幾種:
- 在主機上使用管理員權限將NFS掛載目錄所屬用戶ID修改為容器所對應的實際用戶ID。
- 在容器啟動時使用
--private-users
參數指定使用用戶作為特權用戶啟動。 - 在掛載目錄時使用
--bind-user
參數指定NFS目錄所屬用戶映射到容器中。