LXD コンテナに GPU・USB・GUI・Audio を渡す
よるほろぐ編集部
lxc launch や lxc config device add に慣れてきたら、「ホストのリソースをコンテナに直接渡す」操作も選択肢になります。LXD のコンテナはホストとカーネル・ファイルシステム空間を共有するため、VM よりもデバイスやソケットを中に突っ込みやすいのが特長です。ただし「渡せる」ことと「安全・簡単」は別の話なので、公式ドキュメントが何を保証し、何を保証しないかを意識しながら進めます。
本記事で扱うのは Canonical LXD 公式ドキュメントに載っている device 型のみです。GUI や Audio については公式に「これをやれば GUI が使えます」という専用ページはないため、disk・unix-char・proxy の応用例として位置づけます。
本記事は LXD 実践ガイドの第3回です。前提として、LXD と LXC の違いと基本コマンドとプロファイル・ストレージ・スナップショットを使う日常運用を先に確認してください。
前提:ホストのディレクトリをマウントする disk device
後述の GUI や Audio でも使うので、まず disk device から確認しておきます。disk device はホストのパスを source に指定すると、コンテナでは bind-mount、VM では 9p または virtiofs で共有します。コンテナの場合は次の構文です。
lxc config device add <instance> <device> disk source=<host_path> path=<path_in_instance>
コンテナでよく使うオプションは path=(マウント先、必須)、shift=true(UID/GID をコンテナに合わせてシフト)、readonly=true、required=true|false です。propagation= はマウント伝播の挙動を制御します。
たとえばホームディレクトリを読み取り専用で渡す場合は次のようになります。
lxc config device add c1 host-home disk source=/home/example path=/mnt/home readonly=true shift=true
GPU を渡す gpu device
コンテナ内で機械学習やレンダリングを動かすなら、GPU を直通させたいところです。LXD の gpu device は gputype=physical がデフォルトで、コンテナ・VM 両方で使えます。コンテナでは複数 GPU を一度にマッチさせることも可能です。
まずホスト側でリソースを確認します。
lxc info --resources
PCI アドレスやベンダー情報を確認したら、特定の GPU を渡します。
lxc config device add c1 gpu0 gpu gputype=physical pci=0000:01:00.0
CDI 記法を使えばベンダー固有の GPU 番号で指定することもできます。これはコンテナのみ有効です。
# NVIDIA
lxc config device add c1 nvidia0 gpu gputype=physical id=nvidia.com/gpu=0
# AMD
lxc config device add c1 amd0 gpu gputype=physical id=amd.com/gpu=0
すべての GPU を一括で渡したいなら、識別子を絞らずに追加します。
lxc config device add c1 all-gpus gpu gputype=physical
コンテナでは uid=、gid=、mode= でデバイスの所有者と権限を調整できます。NVIDIA ドライバの場合、コンテナ内に対応するユーザーランドライブラリも入っている必要があるので、配布元の手順に沿ってセットアップしてください。LXD の gpu device はカーネルドライバ側のデバイスを渡すだけで、ライブラリや CUDA ランタイムまでは面倒を見ません。
USB デバイスを渡す usb device
USB デバイスは vendorid と productid で指定します。LXD の usb device はコンテナ・VM 両対応で、ホットプラグにも対応しています。
lxc config device add <instance> <device> usb vendorid=<VID> productid=<PID>
VID と PID は lsusb で確認します。
lsusb
公式ドキュメントは lsusb の使用を例示しています。たとえば次のような指定になります。
lxc config device add c1 myusb usb vendorid=046d productid=c52b
required=false(デフォルト)にすると、デバイスが接続されていなくてもインスタンスは起動でき、後から挿すと自動でコンテナに現れます。これは試行錯誤するうえで重宝します。
重要な注意点として、コンテナに渡されるのは /dev/bus/usb 上の libusb デバイスだけです。ユーザースペースドライバで動くデバイス向けです。カーネルドライバが必要なデバイスは unix-char または unix-hotplug を使う必要があります。公式ドキュメントはこの点を明記しています。
また公式は、スループットが高い・低レイテンシが必要なデバイスは避けるよう注意しています。USB パススルーは利便性が高い一方、性能保証は限定的です。
キャラクタデバイスを渡す unix-char device
/dev/input/event* や /dev/video*、/dev/snd/* のようなカーネルドライバが必要なデバイスは、usb device ではなく unix-char device を使います。VM には使えず、コンテナ専用です。
lxc config device add <instance> <device> unix-char source=<host_path> path=<instance_path>
source を省略して path だけ指定すると、ホストと同じパスでコンテナに現れます。required=false にして source を指定すれば、ホットプラグに対応します。デバイスが現れたら自動で渡り、消えたら除去されます。
オプションには uid=、gid=、mode=、major=、minor= があります。たとえば Webcam をコンテナ内で使いたい場合は次のようになります。
lxc config device add c1 cam0 unix-char source=/dev/video0 path=/dev/video0
Audio を /dev/snd/* 経由で渡したい場合も同じ考え方です。
lxc config device add c1 snd unix-char source=/dev/snd path=/dev/snd required=false
unix-char は低レベルな分、柔軟ですがセキュリティリスクも大きいです。不要なデバイスは追加しない、権限は最小にする、という意識で運用してください。
GUI を共有する
LXD 公式ドキュメントには「GUI 共有」の専用ページはありません。ただし disk device でホストのソケットファイルを bind-mount したり、proxy device で Unix socket を転送したりできるため、これらを応用して GUI アプリケーションを表示できます。ここから先は device 型の応用例です。
X11 の場合
X11 では /tmp/.X11-unix/X0 などのソケットをコンテナにマウントし、DISPLAY 環境変数を設定します。/tmp/.X11-unix/X0 は Linux でよく使われるソケットパスで、これは LXD 公式が指定しているわけではありません。
lxc config device add c1 x11 disk source=/tmp/.X11-unix path=/tmp/.X11-unix
lxc exec c1 -- bash -c 'export DISPLAY=:0; glxgears'
安全のため、コンテナを信頼できる場合だけこの共有を行い、X11 のアクセス制御(xhost や cookie 認証)も併用してください。
Wayland の場合
Wayland は /run/user/<uid>/wayland-0 などのソケットを使います。たとえば UID 1000 の場合は次のようになります。
lxc config device add c1 wayland disk source=/run/user/1000/wayland-0 path=/run/user/1000/wayland-0
lxc exec c1 -- bash -c 'export WAYLAND_DISPLAY=wayland-0; wayland-info'
Wayland は X11 より脆弱性の影響範囲が小さいと言われますが、それでもホストの compositor ソケットをコンテナに渡すのは強い権限委譲です。運用対象を絞って使うのが良いでしょう。
Audio を共有する
Audio も GUI と同様、公式の専用ページはありません。disk または proxy でホストのサウンドサーバソケットをコンテナに繋ぐのが一般的な応用例です。
PulseAudio の場合
PulseAudio は /run/user/<uid>/pulse/native ソケットを使います。UID 1000 を例にすると次のようになります。
lxc config device add c1 pulse disk source=/run/user/1000/pulse path=/run/user/1000/pulse
lxc exec c1 -- bash -c 'export PULSE_SERVER=unix:/run/user/1000/pulse/native; pactl info'
PipeWire の場合
PipeWire も pipewire-0 ソケットを /run/user/<uid>/ 以下に作成します。同じく disk でマウントし、対応する環境変数を設定します。
lxc config device add c1 pipewire disk source=/run/user/1000/pipewire-0 path=/run/user/1000/pipewire-0
必要に応じて /dev/snd/* を unix-char で渡すことも検討してください。ただしサウンドサーバを経由する方式の方が排他制御やルーティングが楽になることが多いです。
proxy device で Unix socket を転送する
proxy device は TCP/UDP だけでなく、Unix socket 間のトラフィック転送もサポートしています。公式ドキュメントは「forwarding a GUI or audio traffic from a container to the host system」に有用と明記しています。
サポートされる接続タイプには tcp<->tcp、udp<->udp、unix<->unix、tcp<->unix、unix<->tcp、tcp<->udp、unix<->udp があります。
lxc config device add <instance> <device> proxy listen=<type>:<addr>:<port> connect=<type>:<addr>:<port> bind=<host|instance>
非 NAT モードで bind=instance にすると、listen ソケットがインスタンス側(コンテナ側)に作られ、ホスト側からそこへ接続する形になります。応用例として、ホスト側の X11 ソケットをコンテナに転送する設定を示します。これは LXD 公式ドキュメントの例ではなく、proxy の「GUI/audio 転送に有用」という機能を応用したものです。
lxc config device add c1 x11 proxy bind=host listen=unix:/tmp/.X11-unix/X0 connect=unix:/tmp/.X11-unix/X0
NAT モード(nat=true)は TCP/UDP のみで、インスタンス NIC に static IP が必要です。Unix socket の転送には使えませんので、注意してください。
VM(—vm)ではどう違うか
VM は別カーネル・別ファイルシステム空間で動くため、disk device でホストのパスを 9p または virtiofs 経由で共有することはできても、Unix ソケットファイルは別カーネル空間へ渡らないため GUI や Audio のソケット渡しは実質できません。GPU については gputype=physical で PCI パススルーし、1 デバイスあたり 1 GPU を丸ごと渡すのが基本で、コンテナのようなレンダーノード単位の細かい共有は前提が異なります。
VM で GUI や Audio を使いたい場合は、LXD 組み込みというより QEMU 側で spice や VNC などの画面転送プロトコルを使う必要があります。本記事ではコンテナを前提にしているため、VM の GUI/Audio についてはこれ以上深入りしません。
まとめ
LXD では disk・gpu・usb・unix-char・proxy の各 device 型を組み合わせることで、GPU、USB、GUI、Audio などのリソースをコンテナに渡せます。コンテナはホストとカーネル空間を共有するため、VM では難しいソケット共有もシンプルに実現できます。ただし GUI/Audio については公式ドキュメントに専用ページはなく、disk・proxy・unix-char の応用例として位置づける必要があります。
セキュリティ面でも、デバイスをコンテナに渡すということはホストのリソースを直接露出させることに他なりません。必要なデバイスだけを、最小権限で、信頼できるコンテナにのみ追加する運用を心がけてください。
LXD にはこのほかにも、複数サーバーを束ねるクラスタリング(最大50台で統合管理)や、稼働中のインスタンスを別ホストへ移すライブマイグレーションという機能があります。本記事は単一ホストでのコンテナを前提にしたので扱いませんでしたが、スケールや冗長化を考えるなら公式ドキュメントの “Clustering” と “Migration” の節を起点に調べてみてください。
出典
- https://canonical.com/lxd/docs/latest/
- https://canonical.com/lxd/docs/latest/reference/devices_disk/
- https://canonical.com/lxd/docs/latest/reference/devices_gpu/
- https://canonical.com/lxd/docs/latest/reference/devices_usb/
- https://canonical.com/lxd/docs/latest/reference/devices_unix_char/
- https://canonical.com/lxd/docs/latest/reference/devices_proxy/