systemd-nspawn
systemd-nspawn は chroot コマンドに似ていますが、chroot を強化したものです。
systemd-nspawn を使えば軽量な名前空間コンテナでコマンドや OS を実行することができます。ファイルシステム階層だけでなく、プロセスツリーや様々な IPC サブシステム、ホスト・ドメイン名も完全に仮想化するため chroot よりも強力です。
systemd-nspawn は /sys
, /proc/sys
, /sys/fs/selinux
などのコンテナの様々なカーネルインターフェイスへのアクセスを読み取り専用に制限します。コンテナの中からネットワークインターフェイスやシステムクロックを変更することは出来ません。デバイスノードが作成はできません。コンテナの中からホスト環境を再起動することはできず、カーネルモジュールのロードもできません。
systemd-nspawn は LXC や Libvirt よりも設定が簡単なツールです。
目次
インストール
systemd-nspawn は systemd の一部であり、systemd に含まれています。
サンプル
コンテナに最小限の Arch Linux を作成して起動
まず arch-install-scripts パッケージをインストールしてください。
次に、コンテナを置くためのディレクトリを作成してください。この例では、~/MyContainer
を使用します。
そして、pacstrap を使って最小限の arch システムをコンテナにインストールします。最低限でも base パッケージはインストールする必要があります。
# pacstrap -K -c ~/MyContainer base [additional pkgs/groups]
インストールが完了したら、コンテナに chroot し、root パスワードを設定します。
# systemd-nspawn -D ~/MyContainer # passwd # logout
最後に、コンテナを起動します。
# systemd-nspawn -b -D ~/MyContainer
-b
オプションはシェルを実行する代わりにコンテナを起動します (つまり PID=1 として systemd
を実行)。-D
にはコンテナのルートディレクトリにするディレクトリを指定します。
コンテナが開始したら、設定したパスワードを使って "root" でログインしてください。
コンテナの電源を切りたいときはコンテナの中から poweroff
を実行することで出来ます。ホストからは、machinectl ツールでコンテナを制御できます。
Debian や Ubuntu 環境の作成
debootstrap と debian-archive-keyring か ubuntu-keyring のどちらか (インストールしたい方のディストリのキーリング) をインストールしてください。
後は簡単に Debian や Ubuntu 環境をセットアップできます:
# cd /var/lib/machines # debootstrap --include=dbus-broker,systemd-container --components=main,universe codename container-name repository-url
Debian の場合、コードネームとして指定するのは "stable" や "testing" などのローリングの名前か "stretch" や "sid" などのリリース名になります。Ubuntu の場合、"xenial" や "zesty" などのコードネームを使ってください。コードネームの完全なリストは /usr/share/debootstrap/scripts
にあります。Debian イメージの場合は "repository-url" には https://deb.debian.org/debian/ などを指定します。Ubuntu のイメージの場合は "repository-url" は http://archive.ubuntu.com/ubuntu/ などとなります。
Arch と同様に、Debian や Ubuntu ではパスワードなしでログインすることはできません。root のパスワードを設定するために、'-b' オプションを付けずにログインしてからパスワードを設定してください:
# cd /var/lib/machines # systemd-nspawn -D myContainer # passwd # logout
Fedora または AlmaLinux 環境の作成
dnf をインストールし、必要な Fedora リポジトリを追加するために /etc/dnf/dnf.conf
ファイルを編集します。
/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
fedora.gpg ファイルには最新の Fedora リリース用の gpg キーが含まれています https://getfedora.org/security/ 。Fedora 37 の最小コンテナを設定するには:
# cd /var/lib/machines # mkdir 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
AlmaLinux のようなエンタープライズ Linux 派生物は、デフォルトで三つのリポジトリが有効になっています。BaseOS は、すべてのインストールの基盤となるコアセットを含み、AppStream は追加アプリケーション、言語パッケージなどを含み、Extras は RHEL に含まれないパッケージを含んでいます。したがって、最小限のコンテナには /etc/dnf/dnf.conf
に BaseOS リポジトリのみを追加する必要があります。
/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
AlmaLinux 9 の最小コンテナを作成するには:
# 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
これにより、AlmaLinux 9 の最新マイナーバージョンがインストールされますが、特定のポイントリリースをインストールすることもできますが、その場合は gpgpkey エントリを手動で RPM-GPG-KEY-AlmaLinux-9 を指すように変更する必要があります。
Arch、Fedora、AlmaLinux のように、root パスワードを設定しない限り、root としてログインすることは許可されません。root パスワードを設定するには、-b
オプションなしで systemd-nspawn を実行してください:
# systemd-nspawn -D /var/lib/machines/container-name passwd
パッケージのビルドおよびテスト
使用例については、他のディストリビューションのパッケージの作成 を参照してください。
管理
/var/lib/machines/
にあるコンテナは、machinectl コマンドによって制御することができます。内部的には systemd-nspawn@.service
ユニットのインスタンスを制御しています。/var/lib/machines/
のサブディレクトリはコンテナ名に対応しています。
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 start container-name
同様に、poweroff
, reboot
, status
, show
などのサブコマンドがあります。詳細な説明については、machinectl(1) § Machine Commands を参照してください。
その他の一般的なコマンドは以下の通りです:
machinectl list
– 現在実行中のコンテナの一覧を表示するmachinectl login container-name
- 実行中のコンテナに新しいシェルを起動machinectl shell [username@]container-name
– コンテナで対話的なシェルセッションを開きます(コンテナ内のログインプロセスを経ずにユーザープロセスが即座に呼びだす)。machinectl enable container-name
またはmachinectl disable container-name
- コンテナを有効または無効にして、起動時に開始します。詳細については、#PC起動時にコンテナを自動で開始する を参照してください。
machinectl にはコンテナ(または仮想マシン)イメージとイメージ転送を管理するためのサブコマンドもあります。詳細については、machinectl(1) § Image Commands および、machinectl(1) § Image Transfer Commands を参照してください。2023Q1 時点で、machinectl(1) § EXAMPLES にある最初の 3 つの例はイメージ転送コマンドを示しています。machinectl(1) § FILES AND DIRECTORIES では、適切なイメージをどこで見つけるかについて説明しています。
systemd ツールチェイン
systemd のコアツールチェインは多くがコンテナでも使えるようにアップデートされています。コンテナの名前を引数とする -M, --machine=
オプションをツールに付けるだけです。
例:
- 特定のマシンの journal ログを表示:
$ journalctl -M MyContainer
- control group の中身を表示:
$ systemd-cgls -M MyContainer
- コンテナの起動時間を表示:
$ systemd-analyze -M MyContainer
- リソース利用状況を表示:
$ systemd-cgtop
設定
コンテナ毎の設定
グローバル設定のオーバーライドではなく、コンテナ毎の設定を指定するには、.nspawn ファイルを使用できます。詳細については、 systemd.nspawn(5) を参照してください。
PC起動時にコンテナを自動で開始する
コンテナを頻繁に使用する場合は、PC起動時に開始することをおすすめします。
まず、machines.target
が有効になっている事を確認します。
machinectl で検出可能なコンテナは、有効または無効にできます:
$ machinectl enable container-name
リソース制御
systemctl set-property
でコンテナの制限やリソース管理を実装するために、コントロールグループを利用することができます。systemd.resource-control(5) を参照してください。例えば、メモリ容量やCPU使用率を制限できます。コンテナのメモリ消費を2GiBに制限するには:
# systemctl set-property systemd-nspawn@container-name.service MemoryMax=2G
または、CPU時間の使用量をだいたい2コア分に制限したい場合:
# systemctl set-property systemd-nspawn@container-name.service CPUQuota=200%
これにより以下の永続ファイルが作成されます。
/etc/systemd/system.control/systemd-nspawn@container-name.service.d/
.
ドキュメントによると、MemoryHigh
はメモリ消費をチェックするための推奨される方法ですが、MemoryMax
のように厳密に制限されることはありません。MemoryMax
を最終防衛戦として残して、両方のオプションを使用できます。また、コンテナが認識できるCPUの数を制限しないことも考慮に入れてください。ただし、CPU時間合計に対して、コンテナが最大で取得する時間を制限することで、同様の結果が得られます。
ネットワーキング
systemd-nspawn コンテナは、ホストネットワーク または プライベートネットワークのいずれかを使用できます。
- ホストネットワークモードでは、コンテナはホストネットワークへのフルアクセスが可能です。これは、コンテナがホスト上のすべてのネットワークサービスにアクセスできるようになり、コンテナからのパケットがホストのパケットとして外部ネットワークに表示される事を意味します(つまり、同じIPアドレスを共有します)。
- プライベートネットワークモードでは、コンテナはホストのネットワークから切断されています。これにより、ループバックデバイスとコンテナに明示的に割り当てられたものを除いて、すべてのネットワークインターフェイスがコンテナを使用できなくなります。コンテナのネットワークインターフェイスを設定するには、いくつかの方法があります。
- 既存のインターフェイスをコンテナに割り当てることができます(たとえば、複数のイーサネットデバイスがある場合)。
- 既存のインターフェース(つまり、VLANインターフェース)に関連付けられた仮想ネットワークインターフェースを作成して、コンテナーに割り当てることができます。
- ホストとコンテナの間に仮想イーサネットリンクを作成できます。
- 後者の場合、コンテナのネットワークは、(外部ネットワークや他のコンテナから) 完全に分離されており、ホストとコンテナ間のネットワークを構成するのは管理者の責任です。これには通常、複数の(物理または仮想)インターフェイスを接続するための ネットワークブリッジ の作成、または複数のインターフェイス間の ネットワークアドレス変換 の設定が含まれます。
ホストネットワーキングモードは、コンテナに割り当てられたインターフェースを構成するネットワーキングソフトウェアを実行しない アプリケーションコンテナ に適しています。ホストネットワーキングは、シェルから 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-container-name
という名前のネットワークインターフェイスとして利用可能になります。リンクのコンテナ側は、hosts0
という名前になります。このオプションは、--private-network
を意味することに注意してください。
コンテナを起動する際には、ホストとコンテナの両方のインターフェイスにIPアドレスを割り当てなければなりません。ホストとコンテナの両方で systemd-networkd を使用している場合、初期状態で実行されます:
- ホスト上の
/usr/lib/systemd/network/80-container-ve.network
ファイルはve-container-name
インターフェイスと一致し、DHCP サーバーを起動します。DHCP サーバーは、IP アドレスをホストインターフェイスとコンテナーに割り当てます。 /usr/lib/systemd/network/80-container-host0.network
コンテナ内のファイルはhost0
インターフェイスと一致し、ホストから IP アドレスを受信する DHCP クライアントを起動します。
systemd-networkd を使用しない場合は、静的IPアドレスを設定するか、ホストインターフェイスで、DHCP サーバを起動し、コンテナで DHCP クライアントを起動できます。詳細については、ネットワーク設定 を参照してください。
コンテナに外部ネットワークへのアクセスを許可するには、インターネット共有#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
テーブルは、インターネット共有#NAT の有効化のように手動で設定する必要があります。ワイルドカードを使用して、ve-
で始まるすべてのインターフェイスに一致させることができます:
# iptables -A FORWARD -i ve-+ -o internet0 -j ACCEPT
さらに、DHCP サーバー(systemd-networkd によって運用される)への着信接続用に ve-+
インターフェースの UDP ポート 67 を開く必要があります:
# iptables -A INPUT -i ve-+ -p udp -m udp --dport 67 -j ACCEPT
ネットワークブリッジを使用する
ホストシステムにネットワークブリッジを構成している場合は、コンテナの仮想イーサネットリンクを作成し、そのホスト側をネットワークブリッジに追加できます。 これは、--network-bridge=bridge-name
オプションを使用して実行されます。--network-bridge
は --network-veth
を意味することに注意してください。つまり、仮想イーサネットリンクは自動的に作成されます。 ただし、リンクのホスト側は ve-
ではなく vb-
プリフィックスを使用するため、DHCP サーバーと IP マスカレードを起動するための systemd-networkd オプションは適用されません。
ブリッジの管理は管理者に任されています。例えば、ブリッジは物理インターフェースと仮想インターフェースを接続したり、複数のコンテナの仮想インターフェースのみを接続したりすることができます。systemd-networkd を使用した設定例については、systemd-networkd#Network bridge with DHCP と systemd-networkd#Network bridge with static IP addresses を参照してください。
また、--network-zone=zone-name
オプションは --network-bridge
と似ていますが、ネットワークブリッジは systemd-nspawn と systemd-networkd によって自動的に管理されます。vz-zone-name
という名前のブリッジインターフェースは、--network-zone=zone-name
を設定した最初のコンテナが起動したときに自動的に作成され、--network-zone=zone-name
を設定した最後のコンテナが終了したときに自動的に削除されます。したがって、このオプションを使用すると、複数の関連するコンテナを共通の仮想ネットワーク上に簡単に配置することができます。vz-*
インターフェースは、/usr/lib/systemd/network/80-container-vz.network
ファイルのオプションを使って、ve-*
インターフェースと同じように systemd-networkd によって管理されることに注意してください。
「macvlan」または「ipvlan」インターフェースを使用する
仮想イーサネットリンク(ホスト側がブリッジに追加される場合とされない場合があります)を作成する代わりに、既存の物理インターフェイス(つまり、VLAN インターフェイス)上に仮想インターフェイスを作成し、それをコンテナに追加できます。仮想インターフェイスは、基盤となるホストインターフェイスとブリッジされるため、コンテナは外部ネットワークに公開されます。これにより、ホストが接続されているのと同じ LAN から DHCP を介して個別の IP アドレスを取得できます。
systemd-nspawn には2つのオプションがあります:
--network-macvlan=interface
– 仮想インターフェイスは、基盤となる物理インターフェイスとは異なるMACアドレスを持ち、mv-interface
という名前が付けられます。
--network-ipvlan=interface
– 仮想インターフェイスは、基礎となる物理インターフェイスと同じMACアドレスを持ち、iv-interface
と名付けられます。
どちらのオプションも --private-network
を意味します。
既存のインターフェイスを使用する
ホストシステムに複数の物理ネットワークインターフェイスがある場合は、 --network-interface=interface
を使用してコンテナにインターフェイスを割り当てることができます(コンテナが起動している間はホストからは利用できないようにします)。--network-interface
は--private-network
を意味することに注意してください。
ポートマッピング
プライベートネットワークが有効になっている場合、ホストの個々のポートは、-p
/--port
オプションを使用するか、または .nspawn ファイルの Port
設定を使用してコンテナのポートにマッピングすることができます。たとえば、ホストの TCP ポート 8000 をコンテナの TCP ポート 80 にマッピングするには:
/etc/systemd/nspawn/container-name.nspawn
[Network] Port=tcp:8000:80
これは、nat
テーブルに iptables ルールを発行することで機能しますが、filter
テーブルの FORWARD
チェインは、#仮想イーサネットリンクを使用するに示されているように手動で設定する必要があります。さらに、シンプルなステートフルファイアウォールに従った場合、ホストの wan_interface
で転送されたポートへの新しい接続を許可するには、次のコマンドを実行してください:
# iptables -A FORWARD -i wan_interface -o ve-+ -p tcp --syn --dport 8000 -m conntrack --ctstate NEW -j ACCEPT
ドメイン名前解決
コンテナ内の ドメイン名前解決 は systemd-nspawn の --resolv-conf
オプションか、.nspawn ファイルの ResolvConf=
オプションで設定できます。systemd-nspawn(1) § 統合オプション に多くの値が記述されています。
デフォルト値は auto
で以下の事を意味します:
--private-network
が有効になっている場合、/etc/resolv.conf
はコンテナ内のまま残ります。- あるいは、ホストで systemd-resolved が実行されている場合、そのスタブ
resolv.conf
ファイルがコンテナにコピーまたはバインドマウントされます。 - それ以外の場合、
/etc/resolv.conf
ファイルはホストからコンテナにコピーされるか、バインドマウントされます。
最後の2つのケースでは、コンテナルートが書き込み可能な場合はファイルがコピーされ、読み取り専用の場合はバインドマウントされます。
ホスト上で systemd-resolved が実行される 2 番目のケースでは、コンテナがホストからのスタブ symlink ファイル /etc/resolv.conf
を使用できるように systemd-nspawn がコンテナ内でも実行されることを期待します。そうでない場合、デフォルト値の auto
はもはや機能しませんので、replace-*
オプションのいずれかを使ってシンボリックリンクを置き換える必要があります。
ヒントとテクニック
シェル/init 以外のコマンドを実行する
systemd-nspawn(1) § Execution Options より。
- " [オプション] --as-pid2 [呼び出し] シェルまたは指定されたプログラムを、PID 1(init)ではなくプロセス ID(PID)2 として使用します。[...] PID 1として正しく実行されるように変更されていない限り、コンテナ内で任意のコマンドを呼び出すにはこのモードを使用することが推奨されます。言い換えれば、このスイッチは、コマンドが init やシェルの実装を参照している場合を除き、ほとんどすべてのコマンドに使用すべきです。[...] このオプションは
--boot
と組み合わせることはできません。
非特権コンテナ
systemd-nspawn は非特権コンテナをサポートしますが、コンテナは root として起動する必要があります。
これを行う最も簡単な方法は、-U
オプションを使用して systemd-nspawn が自動的に未使用の UIDs/GIDs の範囲を選択させることです:
# systemd-nspawn -bUD ~/MyContainer
カーネルがユーザー名前空間をサポートしている場合、-U
オプションは --private-users=pick --private-users-chown
と同等です。これはコンテナの開始時にコンテナ内のファイルとディレクトリが選択された範囲のプライベート UIDs/GIDs に変更される事を意味します。詳細は、
systemd-nspawn(1) § User Namespacing Options を参照してください。
プライベート UID/GID の範囲を持つコンテナを起動したら、パーミッションエラーを避けるために、そのコンテナを使い続ける必要があります。あるいは、--private-users-chown
(または -U
) のファイルシステムへの影響を元に戻すには、0で始まるIDの範囲を指定します:
# systemd-nspawn -D ~/MyContainer --private-users=0 --private-users-chown
X 環境
新しいコンテナで X アプリケーションを動かす必要がある場合は Xhost を見て下さい。
外部の X サーバーにコンテナのセッションを接続するには DISPLAY
環境変数を設定する必要があります。
X は必要なファイルを /tmp
ディレクトリに保存します。コンテナから全てを表示させるには、/tmp
ディレクトリのファイルにアクセスできるようにしなくてはなりません。コンテナを起動するときに --bind=/tmp/.X11-unix:/tmp/.X11-unix
オプションを追加してください。
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
上記の2行目では、接続ファミリーを ""FamilyWild""(値65535
) に設定しているため、エントリはすべての表示に一致します。詳細はXsecurity(7) を参照。
X nesting/Xephyr を使用
X アプリケーションを実行し、共有 X デスクトップのリスクを回避するもう1つの簡単な方法は、X nesting を使用することです。 ここでの利点は、コンテナ内アプリケーションと非コンテナアプリケーションの間の相互作用を完全に回避し、異なる デスクトップ環境 または ウィンドウマネージャ を実行できることです。 欠点はパフォーマンスの低下と 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 \ firefox
あるいは、コンテナを起動して、例えば、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
を実行すると、含まれている全てのファイルを確認することができます。全てをコピーする必要はありません。以下の systemd override ファイルは、コンテナを machinectl start container-name
で実行する際に必要なファイルを全てバインドします。
/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
ホストのファイルシステムにアクセス
例えばホストとコンテナの両方が Arch Linux で、pacman のキャッシュを共有するには:
# systemd-nspawn --bind=/var/cache/pacman/pkg
詳しくは systemd-nspawn(1) の --bind
と --bind-ro
を参照してください。
ファイルを使ってコンテナごとにバインドを設定することもできます:
/etc/systemd/nspawn/my-container.nspawn
[Files] Bind=/var/cache/pacman/pkg
#コンテナごとに設定を指定するを参照。
systemd を使っていない環境で動作させる
Init#systemd-nspawn を見て下さい。
Btrfs のサブボリュームをコンテナのルートとして使う
Btrfs サブボリュームをコンテナのルートのテンプレートとして使うには、--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 サブボリュームはすぐに削除されます。
system-nspawn で docker を実行
Docker 20.10 以降、cgroups v2 を有効にした非特権 systemd-nspawn コンテナ内で Docker コンテナを実行することが可能になりました(Arch Linux ではデフォルト)。これにより、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
[7] と /usr/share/factory/etc/securetty
を削除するか、コンテナファイイルシステム上の /etc/securetty
に必要な pty 端末デバイス(pts/0
のような)を追加します。変更は、次の起動時に上書きされるため、/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
環境変数の端末タイプが正しくないことが原因である可能性があります。環境変数はホストのシェルから継承されませんが、明示的に設定されていない限り、systemd (vt220
) で固定されたデフォルトに戻ります。設定するには、コンテナ内の container-getty@.service
サービス用のオーバーレイを作成して、machinectl login
の login getty を起動し、ログインしているホスト端末と一致する値を TERM
に設定してください。
/etc/systemd/system/container-getty@.service.d/term.conf
[Service] Environment=TERM=xterm-256color
もしくは、machinectl shell
を使用してください。端末から TERM
環境変数を適切に継承します。
コンテナ内へのNFS共有のマウント
現時点(2019年6月)では利用できません。