kWatanabe 記事一覧へ

kWatanabe の 技術帖

某企業でOSや仮想化の研究をやっているインフラ系エンジニア。オンプレとクラウドのコラボレーションなど、興味ある技術を綴る。

LXCの非特権コンテナにホストのGPUをパススルーする

  • 2020/11/08 一部のtypoを修正

大要

  • Proxmox VEのLXCの非特権コンテナにGPUをパススルーし、CUDAを使う
  • KVMVGAパススルーと違って、ホストに搭載するGPUは1つでよい
  • GPUは(性能を考慮しないなら)そのままホストでも利用できる
  • (たぶん、)生のLXCでも同等の手順で実現できる

背景

  • 我が家では Ryzen マシンに Proxmox VE を入れて、IaaS モドキを運用している(以前は CloudStack + XenServer だったが、XenServer 無料版 の制限強化の際にやめた)
  • RyzenGPUを持たないので、コンソール用に GeForce GTX 1050Ti を挿しているが、非常時以外は使わないので勿体ない
  • LXCの非特権コンテナ上で 1050Ti を使って CUDA ができれば、追加費用なしで手軽にGPUインスタンスが手に入るのではと思った

ターゲット環境

手順

大まかな手順は以下の通り。

  1. ホストでGPUのドライバをビルド
  2. ホストでGPUのデバイスファイルを有効化
  3. ゲストにGPUのデバイスファイルをリマップ
  4. ゲストでGPUのドライバをビルド
  5. ゲストでCUDAのライブラリをインストール(割愛)

ホストでGPUドライバをビルド

サブスクリプションを持たない人向けの Proxmox リポジトリを有効にする。もちろん、サブスクリプションをお持ちの場合は不要。

# vim /etc/apt/sources.list
deb http://download.proxmox.com/debian/pve buster pve-no-subscription

Proxmox VE のリポジトリには nvidia-driver がないので、Proxmox VE 6.x のベースである Debian 10 のリポジトリを有効にする。なお、stable リポジトリnvidia-driver は古いので、backports リポジトリを用いる。

# vim /etc/apt/sources.list
deb http://deb.debian.org/debian buster-backports main contrib non-free
deb-src http://deb.debian.org/debian buster-backports main contrib non-free

nvidia-driver を導入する。nvidia-driver はバイナリで提供されていないので、DKMS*1 でその場でビルドされる。そのため、カーネルヘッダやビルドツールも一式必要になる。

# apt update
# apt upgrade
# apt install pve-headers build-essentials
# apt install -t buster-backports nvidia-driver nvidia-smi

ホストでGPUバイスファイルを有効化

DKMSでビルドされたドライバモジュールを有効にする。

# vim /etc/modules-load.d/nvidia-gpu.conf
nvidia-drm
nvidia
nvidia_uvm

udevルールを作成して、ドライバがロードされた時に非特権コンテナからでもデバイスファイルを扱えるように、アクセス権が変更されるようにする。(特権コンテナの場合は不要)

余談だが、非特権コンテナから扱えるようにアクセス権を緩めると、ホストの一般ユーザ(Proxmox VEに一般ユーザはないが)からでもデバイスファイルが叩けるようになってしまう。でも、コンテナを特権コンテナにするよりはまだマシか…?

# vim /etc/udev/rules.d/70-nvidia-gpu.rules
KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L && /bin/chmod 666 /dev/nvidia*'"
KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u && /bin/chmod 0666 /dev/nvidia-uvm*'"

ホストを一度再起動する。

# reboot

起動したら、GPUが正しくホストで認識されているか確認する。

# nvidia-smi
Sun Oct  4 15:42:59 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.100      Driver Version: 440.100      CUDA Version: N/A      |
|-------------------------------+----------------------+----------------------+
| 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 GTX 105...  On   | 00000000:0A:00.0 Off |                  N/A |
| 29%   29C    P8    N/A /  75W |      1MiB /  4040MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

ゲストにGPUバイスファイルをリマップ

コンテナの設定ファイルを開いて、GPUのデバイスファイルをリマップする。

例えば、対象のコンテナの vmid が 101 だった場合は、以下の通り。

# vim /etc/pve/lxc/101.conf 
lxc.cgroup.devices.allow: c 195:* rwm
lxc.cgroup.devices.allow: c 236:* rwm
lxc.cgroup.devices.allow: c 226:* rwm

lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir

コンテナを起動して、コンテナの中からもデバイスファイルが見えている事を確認する。

$ ls /dev/nvidia* /dev/dri/*
/dev/nvidia0
/dev/nvidiactl
/dev/nvidia-modeset
/dev/nvidia-uvm
/dev/nvidia-uvm-tools
/dev/dri/card0
…(省略)…

ゲストでGPUドライバをビルド

ホストと同様にコンテナでGPUのドライバをビルドする。今回は Proxmox VE のベースである Debian 10 のコンテナなので、カーネルヘッダが異なる以外は同じ。

まずは、backports リポジトリを有効にして…。

$ sudo vim /etc/apt/sources.list
deb http://deb.debian.org/debian buster-backports main contrib non-free
deb-src http://deb.debian.org/debian buster-backports main contrib non-free

次に、DKMS でドライバをビルドする。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install build-essential
$ sudo apt install -t buster-backports nvidia-driver nvidia-smi

コンテナの内部からGPUにアクセスできるか確認する。

$ nvidia-smi
…(省略)…

あとは、実機でCUDAを使うときと同様に、コンテナにCUDAドライバをインストールする。ここでは割愛する。

備考

  • ホストとゲストのドライバは同じバージョンが望ましい。
  • Debian 10 以外のコンテナにパススルーする場合は、ディストリビューションのドライバのバージョンを確認して、少しでも違ったら公式ドライバでバージョン合わせをするのが無難
  • Proxmox VE 固有の手順は殆どないので、多分、生のLXCでも同じ手順で使えると思う

まとめ

  • Proxmox VE 6.2 上に構築した、LXCの非特権コンテナに GeForce GTX 1050Ti をパススルーして、お手軽GPUインスタンスを作った
  • ホストでも引き続きGPUを使えるので、RyzenLinuxなをお持ちの方は、とりあえずGPUを握ったコンテナを用意しておくと何かと捗るかもしれない

*1:Dynamic Kernel Module Support。ライセンスがポリシに合わなかったり、そもそもフリーじゃなかったりの理由で、Linuxカーネルのソースツリーの外にソースが存在するカーネルモジュールを、利用者の責任の下でその場でビルドさせる仕組み。