TT Lab
はじめる
学ぶ 学習パス コース

Rootless Podmanの運用

CDIスペックの読み書き

TT Labで続きを見る

一言でいうと

CDIは、「このデバイスをコンテナに入れるには、何をしてあげる必要があるか」をYAMLで書いておく仕様です。ランタイムフックというブラックボックスを、標準のファイルに変えました。

なぜ必要なのか

以前は、DockerでGPUを使うには、nvidia-container-runtimeというランタイムフックを差し込む必要がありました。コンテナの起動直前に、そのフックが実行されて、ドライバーのライブラリをマウントし、デバイスノードを追加していました。動作はしましたが、問題がありました。

CDIは、これを宣言的な仕様に変えました。/etc/cdi/*.yamlに、「このデバイス名がリクエストされたら、こういうデバイスノードと、こういうマウントと、こういう環境変数を追加せよ」と書いておけば、CDIをサポートするランタイム(podman、containerd、CRI-O)が、そのまま実行します。

どう動くのか

スペックの構造

cdiVersion: "0.6.0"
kind: nvidia.com/gpu

devices:
  - name: "0"
    containerEdits:
      deviceNodes:
        - path: /dev/nvidia0
        - path: /dev/nvidiactl
        - path: /dev/nvidia-uvm
      mounts:
        - hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.90.07
          containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.90.07
          options: ["ro", "nosuid", "nodev", "bind"]
      env:
        - NVIDIA_VISIBLE_DEVICES=0
      hooks:
        - hookName: createContainer
          path: /usr/bin/nvidia-ctk
          args: ["nvidia-ctk", "hook", "update-ldcache"]

  - name: all
    containerEdits:
      deviceNodes:
        - path: /dev/nvidia0
        - path: /dev/nvidiactl

核心となるルールです。

実際の使い方

# 스펙 생성 (실제 GPU 가 있는 호스트에서)
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

# 등록된 장치 확인
nvidia-ctk cdi list
# nvidia.com/gpu=0
# nvidia.com/gpu=all

# 컨테이너에서 사용
podman run --rm --device nvidia.com/gpu=all nvidia/cuda:12.4.1-base nvidia-smi

dockerの--gpus allに対応するのが、--device nvidia.com/gpu=allです。最新のpodmanは、--gpusも互換フラグとして受け付けますが、CDIの表記が正式です。

rootlessでも、GPUが使えます。GPUドライバーはカーネル空間で動作するので、コンテナの権限レベルとは無関係です。システム全体の/etc/cdi/nvidia.yamlを読めるなら、そのまま使い、読めなければ、ユーザー空間にスペックを生成して、そのディレクトリを指定すれば済みます。

containerd側への登録

Kubernetesでは、containerdがCDIを読みます。config.tomlに有効化の設定が必要で、ランタイムを別に登録する古い方式も、今も使われています。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
  runtime_type = "io.containerd.runc.v2"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
  BinaryName = "/usr/bin/nvidia-container-runtime"
  SystemdCgroup = true

そして、そのランタイムを使うワークロードは、RuntimeClassで選択します。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: nvidia
handler: nvidia

default_runtime_nameをnvidiaに変えないのがコツです。そうすると、GPUを使わないPodまで、そのランタイムを経由することになります。RuntimeClassで、必要なワークロードだけを指定するほうが、安全です。

現場での姿

ドライバーの更新後に、GPUが認識されません。十中八九、CDIスペックの再生成(nvidia-ctk cdi generate)を忘れています。スペックには、ライブラリファイルのバージョンが埋め込まれたパスが入っているので、ドライバーが上がると、そのパスがなくなります。

コンテナ内でnvidia-smiは動くのに、CUDAプログラムが失敗します。必要なライブラリのうち、一部だけがマウントされた場合です。スペックのmountsの一覧を、確認する必要があります。

次のラボですること

/etc/cdi/に、CDIスペックを直接作成します。実際のGPUはないので、スペックの文法と配置、そして--device引数の形式を採点します。現場でミスが出る場所が、まさにそこだからです。