systemd-nspawn

提供: ArchWiki
ナビゲーションに移動 検索に移動

関連記事

systemd-nspawnchroot コマンドに似ていますが、chroot を強化したものです。

systemd-nspawn を使えば軽量な名前空間コンテナでコマンドや OS を実行することができます。ファイルシステム階層だけでなく、プロセスツリーや様々な IPC サブシステム、ホスト・ドメイン名も完全に仮想化するため chroot よりも強力です。

systemd-nspawn/sys, /proc/sys, /sys/fs/selinux などのコンテナの様々なカーネルインターフェイスへのアクセスを読み取り専用に制限します。コンテナの中からネットワークインターフェイスやシステムクロックを変更することは出来ません。デバイスノードが作成はできません。コンテナの中からホスト環境を再起動することはできず、カーネルモジュールのロードもできません。

systemd-nspawnLXCLibvirt よりも設定が簡単なツールです。

目次

インストール

systemd-nspawn は systemd の一部であり、systemd に含まれています。

サンプル

コンテナに最小限の Arch Linux を作成して起動

まず arch-install-scripts パッケージをインストールしてください。

次に、コンテナを置くためのディレクトリを作成してください。この例では、~/MyContainer を使用します。

そして、pacstrap を使って最小限の arch システムをコンテナにインストールします。最低限でも base パッケージはインストールする必要があります。

# pacstrap -K -c ~/MyContainer base [additional pkgs/groups]
ヒント: base パッケージは、linux カーネルパッケージに依存せず、コンテナにも対応しています。
ノート: pacstrap が利用できない異なるオペレーティングシステムから作成する場合、bootstrap tarball をコンテナイメージとして使用することができます。pacman キーリングはコンテナ内で初期化する必要があります。詳細は 既存の Linux からインストール#pacman キーリングの初期化 を参照してください。

インストールが完了したら、コンテナに chroot し、root パスワードを設定します。

# systemd-nspawn -D ~/MyContainer
# passwd
# logout
ヒント: Setting root password is optional. You can get a root shell in a booted container directly without having to log in by running machinectl shell root@MyContainer. See #machinectl.

最後に、コンテナを起動します。

# systemd-nspawn -b -D ~/MyContainer

-b オプションはシェルを実行する代わりにコンテナを起動します (つまり PID=1 として systemd を実行)。-D にはコンテナのルートディレクトリにするディレクトリを指定します。

コンテナが開始したら、設定したパスワードを使って "root" でログインしてください。

ノート: "Login incorrect" でログインが失敗する場合、問題は、securetty TTY デバイスのホワイトリストである可能性があります。#root ログインが失敗する をご確認ください。

コンテナの電源を切りたいときはコンテナの中から poweroff を実行することで出来ます。ホストからは、machinectl ツールでコンテナを制御できます。

ノート: コンテナの中からセッションを終了するには Ctrl を押しながら ] を素早く3回押してください。US キーボード以外の場合は ] の代わりに % を使用します。

Debian や Ubuntu 環境の作成

debootstrapdebian-archive-keyringubuntu-keyring のどちらか (インストールしたい方のディストリのキーリング) をインストールしてください。

ノート: systemd-nspawn はコンテナ内のオペレーティングシステムが systemd init を使用し (PID 1 として動作する)、systemd-nspawn がコンテナにインストールされている必要があります。この要件は、コンテナ環境に systemd-container パッケージをインストールすることで満たされます。systemd-container パッケージは dbus に依存しますが、dbus または dbus-broker パッケージがコンテナシステムにインストールされていることを確認してください。

後は簡単に 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
ノート: 異なる Fedora リリースをインストールしたい場合は、異なるリリースが 独自のパッケージ要件 を持つことを考慮してください。

btrfs ファイルシステムを使用している場合は、ディレクトリを作成する代わりにサブボリュームを作成してください。

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/ のサブディレクトリはコンテナ名に対応しています。

ノート: なんらかの理由でコンテナを /var/lib/machines/ に移動できない場合、シンボリックリンクが使えます。machinectl(1) § FILES AND DIRECTORIES を参照してください。

systemd-nspawn オプションのデフォルト値

machinectlsystemd-nspawn@.service 経由で起動されたコンテナは systemd-nspawn コマンドで起動されたコンテナとはオプションの既定値が異なることを理解することが重要です。サービスが使用する追加オプションは以下の通りです。

  • -b/--boot – マネージドコンテナは自動的に init プログラムを検索し、PID 1 として起動します。
  • --network-veth つまり --private-network – マネージドコンテナは仮想ネットワークインターフェースを取得し、ホストネットワークから切り離されます。詳細は、#ネットワーキング を参照してください。
  • -U – カーネルがサポートしている場合、マネージドコンテナはデフォルトで user_namespaces(7) 機能を使用します。詳細は、#非特権コンテナ を参照してください。
  • --link-journal=try-guest

この動作は、コンテナごとの設定ファイルでオーバーライドすることができます。 詳細は、#設定 を参照してください。

machinectl

ノート: machinectl ツールを使うには systemddbus がコンテナにインストールされている必要があります。詳しくは [1] を参照。

コンテナはコマンドで管理できます。例えば、コンテナを起動するには、次のようにします。

$ machinectl start container-name
ノート: machinectl は、コンテナ名が有効なホスト名であるように、ASCII 文字、数字、ハイフンのみで構成されていることを要求します。例えば、コンテナ名にアンダースコアが含まれている場合、それは単に認識されず、 machinectl start container_name を実行すると、エラー Invalid machine name container_name となる。詳しくは[2][3]を参照してください。

同様に、poweroff, reboot, status, show などのサブコマンドがあります。詳細な説明については、machinectl(1) § Machine Commands を参照してください。

ヒント: 電源オフと再起動の操作は、poweroffreboot コマンドを使用してコンテナ内から実行することができます。

その他の一般的なコマンドは以下の通りです:

  • 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) を参照してください。

ノート:
  • .nspawn ファイルは、machinectl remove を実行した時に、/etc/systemd/nspawn/ から予期せずに削除される場合があります。
  • systemd-nspawn@.service ファイルで指定されている、--settings=override がある場合、.nspawn ファイルで指定されているネットワークオプションとコマンドラインオプションの相互作用で正しく動作しません。回避策としてサービスが --network-veth を指定している場合でも、VirtualEthernet=on オプションを含める必要があります。

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時間合計に対して、コンテナが最大で取得する時間を制限することで、同様の結果が得られます。

ヒント: これらの変更を一時的なものにしたい場合は、--runtime オプションを渡すことができます。その結果は、systemd-cgtop で確認できます。

ネットワーキング

systemd-nspawn コンテナは、ホストネットワーク または プライベートネットワークのいずれかを使用できます。

  • ホストネットワークモードでは、コンテナはホストネットワークへのフルアクセスが可能です。これは、コンテナがホスト上のすべてのネットワークサービスにアクセスできるようになり、コンテナからのパケットがホストのパケットとして外部ネットワークに表示される事を意味します(つまり、同じIPアドレスを共有します)。
  • プライベートネットワークモードでは、コンテナはホストのネットワークから切断されています。これにより、ループバックデバイスとコンテナに明示的に割り当てられたものを除いて、すべてのネットワークインターフェイスがコンテナを使用できなくなります。コンテナのネットワークインターフェイスを設定するには、いくつかの方法があります。
    • 既存のインターフェイスをコンテナに割り当てることができます(たとえば、複数のイーサネットデバイスがある場合)。
    • 既存のインターフェース(つまり、VLANインターフェース)に関連付けられた仮想ネットワークインターフェースを作成して、コンテナーに割り当てることができます。
    • ホストとコンテナの間に仮想イーサネットリンクを作成できます。
後者の場合、コンテナのネットワークは、(外部ネットワークや他のコンテナから) 完全に分離されており、ホストとコンテナ間のネットワークを構成するのは管理者の責任です。これには通常、複数の(物理または仮想)インターフェイスを接続するための ネットワークブリッジ の作成、または複数のインターフェイス間の ネットワークアドレス変換 の設定が含まれます。

ホストネットワーキングモードは、コンテナに割り当てられたインターフェースを構成するネットワーキングソフトウェアを実行しない アプリケーションコンテナ に適しています。ホストネットワーキングは、シェルから systemd-nspawn を実行するときのデフォルトのモードです。

一方、プライベート・ネットワーキング・モードは、ホスト・システムから隔離されている必要がある システムコンテナ に適しています。仮想イーサネットリンクの作成は、複雑な仮想ネットワークの作成を可能にする非常に柔軟なツールです。これは machinectlsystemd-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 を意味することに注意してください。

ノート:
  • コンテナ名が長すぎる場合、インターフェイス名は、15文字制限 に収まるように短縮されます(例: ve-long-container-name の代わりに ve-long-conKQGh)。フルネームはインターフェイスの altname プロパティとして設定され(ip-link(8)を参照)、インターフェイスの参照に使用できます。
  • ip link でインターフェイスを調べる場合、インターフェイス名は、ve-container-name@if2host0@if9 のようにサフィックスを付けて表示されます。@ifN は実際にはインターフェイス名の一部ではありません。その代わりに、ip link はこの情報を追加して、仮想イーサネットケーブルが相手側のどの slot に接続しているかを示します
例えば、ve-foo@if2 とし表示されているホスト仮想イーサネットインターフェイスはコンテナ foo に接続され、コンテナ内で ip link を実行しているときに、index 2 で示されている 2 番目のネットワークインタフェースに接続されています。同様に、コンテナ内の host0@if9 という名前のインターフェイスは、ホスト上の 9 番目のネットワークインターフェイス接続されています。

コンテナを起動する際には、ホストとコンテナの両方のインターフェイスに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
ノート: systemd-networkd は、libiptc ライブラリを使用して、iptables と対話します。 nftables を使用する場合は、iptables-nft 変換レイヤーをインストールします。systemd issue 13307 も参照してください。

さらに、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 DHCPsystemd-networkd#Network bridge with static IP addresses を参照してください。

また、--network-zone=zone-name オプションは --network-bridge と似ていますが、ネットワークブリッジは systemd-nspawnsystemd-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 を意味します。

ノート: ホストがコンテナと通信できるようにするために、ホストに macvlan または ipvlan インターフェースを作成し、それをコンテナが使用している同じ物理インターフェースに接続し、その上でネットワーク接続を設定してください。仮想インターフェース名が systemd-nspawn によって作成された仮想インターフェースと衝突しないことを確認してください。systemd-networkd を使用した例については、systemd-networkd#MACVLAN bridge を参照してください。

既存のインターフェイスを使用する

ホストシステムに複数の物理ネットワークインターフェイスがある場合は、 --network-interface=interface を使用してコンテナにインターフェイスを割り当てることができます(コンテナが起動している間はホストからは利用できないようにします)。--network-interface--private-network を意味することに注意してください。

ノート: systemd-nspawn コンテナにワイヤレスネットワークインターフェイスを渡すことは現在サポートされていません。[4]

ポートマッピング

プライベートネットワークが有効になっている場合、ホストの個々のポートは、-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 は、ポートをマッピングする際に loopback インターフェイスを明示的に除外します。したがって、上記の例では、localhost:8000 はホストに接続し、コンテナには接続しません。他のインターフェイスへの接続のみがポートマッピングの対象となります。詳細は、[5] を参照してください。
  • ポートマッピングは IPv4 接続に対してのみ機能します。 [6]

ドメイン名前解決

コンテナ内の ドメイン名前解決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 として起動する必要があります。

ノート: この機能には user_namespaces(7) が必要です。詳細については、Linux Containers#非特権コンテナのサポートを有効化 (任意) を参照してください。

これを行う最も簡単な方法は、-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 の範囲を手動で指定することもできますが、これが役立つことはほとんどありません。

プライベート 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 オプションを追加してください。

ノート: systemd バージョン 235 には バグ が存在し、/tmp/.X11-unix がファイルシステムから消失することがあります。問題を回避するには /tmp/.X11-unix を読み取り専用でバインドしてください: --bind-ro=/tmp/.X11-unix/X0/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

上記の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
ノート: そのため、firefox は root ユーザーとして実行されますが、#非特権コンテナを使用していない場合には独自のリスクが伴います。その場合、まずコンテナ内でユーザーを追加し、その後 systemd-nspawn の呼び出しで --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 を実行すると、含まれている全てのファイルを確認することができます。全てをコピーする必要はありません。以下の systemd override ファイルは、コンテナを machinectl start container-name で実行する際に必要なファイルを全てバインドします。

この記事またはセクションの正確性には問題があります。
理由: No reason to bind from /usr/lib/ into /usr/lib/x86_64-linux-gnu/. (議論: トーク:Systemd-nspawn#)
/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
ノート: ホスト上で NVIDIA ドライバをアップグレードするたびに、コンテナを再起動する必要があり、ライブラリを更新するためにコンテナ内で ldconfig を実行する必要がある場合があります。

ホストのファイルシステムにアクセス

例えばホストとコンテナの両方が 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 はそのまま機能するはずです。

ノート: 上記の設定では、コンテナに add_keykeyctl、および bpf といった名前空間化されていないシステムコールを公開しています。これはユーザー名前空間を完全に無効にする以前の方法と比較するとセキュリティリスクは大幅に低いものの、依然としてセキュリティリスクになり得ます。

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 !
この記事またはセクションの正確性には問題があります。
理由: Files in /usr/lib should not be edited by users, the change in /usr/lib/tmpfiles.d/arch.conf will be lost when filesystem is upgraded. (議論: トーク:Systemd-nspawn#)

コンテナファイルシステム上の、/etc/securetty[7]/usr/share/factory/etc/securetty を削除するか、コンテナファイイルシステム上の /etc/securetty に必要な pty 端末デバイス(pts/0 のような)を追加します。変更は、次の起動時に上書きされるため、/etc/securetty のエントリを削除する必要があります。FS#63236 を参照。削除を選択した場合は、/etc/pacman.confNoExtract にそれらを追加し、再インストールされないようにします。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ユーザーとしてマウントした結果である可能性があります。例えば、fstabnoauto,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月)では利用できません。

参照

翻訳ステータス: このページは en:Systemd-nspawn の翻訳バージョンです。最後の翻訳日は 2024/4/18 です。もし英語版に 変更 があれば、翻訳の同期を手伝うことができます。