LXC で NVIDIA GPU が使える非特権コンテナを作る


異なるバージョンのCUDAを一つの計算機で共存したいときにこうすると便利である。ホスト側のLinuxで nvidia-smi を実行したときと同じようにコンテナ内の nvidia-smi が動作するようにする。LXC でコンテナを作成し実行することはすでに出来ているとする。(非特権コンテナとUbuntu 20.04に合わせて記事を少し更新した)。一般ユーザー(非rootユーザー)でコンテナを作りたい場合 ここ を参照

ホスト側でnvidia-smiを実行すると以下のように見える

# nvidia-smi
Tue Feb  4 10:52:19 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.48.02    Driver Version: 440.48.02    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 208...  Off  | 00000000:08:00.0 Off |                  N/A |
| 30%   31C    P8    25W / 250W |     18MiB / 11016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce RTX 208...  Off  | 00000000:09:00.0 Off |                  N/A |
| 29%   32C    P8    20W / 250W |      1MiB / 11019MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1262      G   /usr/lib/xorg/Xorg                            16MiB |
+-----------------------------------------------------------------------------+

ホスト側での作業

まず lxc-create -n コンテナ名 -t download -- -d ubuntu -r focal -a amd64 でUbuntuのコンテナを作る。もし /var/lib/lxc がBTRFSにあるなら -B btrfs を付けておくと後で lxc-copy -n 古いコンテナ -N 新しいコンテナ でコンテナを複製するときに掛かる時間が劇的に短くなる。

次に lxc-execute -n コンテナ名 -- /bin/passwd でrootに適切なパスワードを設定する。

以下の設定を追加する

/var/lib/lxc/コンテナ名/configに追加
lxc.mount.entry = /dev/nvidiactl dev/nvidiactl none bind,rw,create=file 0 0
lxc.mount.entry = /dev/nvidia-modeset dev/nvidia-modeset none bind,rw,create=file,optional 0 0
lxc.mount.entry = /dev/nvidia-uvm dev/nvidia-uvm none bind,rw,create=file 0 0
lxc.mount.entry = /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,rw,create=file 0 0
lxc.mount.entry = /dev/nvidia0 dev/nvidia0 none bind,rw,create=file 0 0
lxc.mount.entry = /dev/nvidia1 dev/nvidia1 none bind,rw,create=file,optional 0 0
# 以下は非特権コンテナなら不要、またホストLinuxがFedora 31などCGroup2を用いるディストロの場合
# cgroupをcgroup2に置き換える
lxc.cgroup.devices.allow = c 195:* rwm
lxc.cgroup.devices.allow = c 235:* rwm

最後に lxc-start -F -n コンテナ名 でコンテナを起動する。ログインプロンプトが表示されたらrootでログインする。非特権コンテナからNVIDIA GPUにアクセスするためには上記の '/dev' 以下のファイルの所有者またはグループをコンテナ内のroot(たいていの場合100000)にchownchgrp で予めホスト側で変更しておく必要がある

コンテナ内での作業

apt-get --no-install-recommends install software-properties-common
add-apt-repository ppa:graphics-drivers/ppa
# 上記2行はUbuntu Focalでは不要
apt-get --no-install-recommends install nvidia-utils-440

最後のnvidia-utils-440 はホスト側LinuxのNVIDIAドライバのバージョンと完全に同一でないとエラーになりnvidia-smiが動作しないので注意。ここまで行いコンテナ内でnvidia-smiを実行すると

# nvidia-smi
Tue Feb  4 02:01:33 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.48.02    Driver Version: 440.48.02    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 208...  Off  | 00000000:08:00.0 Off |                  N/A |
| 30%   31C    P8    26W / 250W |     18MiB / 11016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce RTX 208...  Off  | 00000000:09:00.0 Off |                  N/A |
| 29%   32C    P8    20W / 250W |      1MiB / 11019MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

となる。

ホストの/homeをコンテナ内で見えるようにするには

/var/lib/lxc/コンテナ名/configに追加
lxc.mount.entry = /home home none bind,rw 0 0

非特権コンテナの場合上記の方法では /home 以下のすべてのファイルの所有者が nobody になってしまし、まともにアクセスできない。その不都合を解消するためには、読み書きしたいファイルのUIDが1000であるとして、コンテナの設定ファイルの中のUID/GIDの変換方法を指定している lxc.idmap の行を例えば以下のように書き換えるとよい。

$HOME/.local/share/lxc/コンテナ名/configを変更
lxc.idmap = u 0 100000 1000
lxc.idmap = g 0 100000 1000
lxc.idmap = u 1000 1000 1
lxc.idmap = g 1000 1000 1
lxc.idmap = u 1001 101001 64535
lxc.idmap = g 1001 101001 64535

上記のように書き換えるとホスト側ほホームディレクトリのUID 1000, GID 1000 がコンテナ内部でもUID 1000, GID 1000に割り当てられるため、UID 1000のユーザーがそのホームディレクトリにあるファイルを操作できるようになる。

他のホストからネットワーク的に見えるようにするには

設定ファイルに以下の内容を追加する。以下の設定はlxc-startをrootから起動しないとエラーになりできない(コンテナは非特権でも構わない)。

/var/lib/lxc/コンテナ名/configに追加
lxc.net.0.type = macvlan
lxc.net.0.link = enp6s0 # この名前は "ip l" で表示されるEthernetインターフェースの名前
lxc.net.0.flags = up
lxc.net.0.name = eth0