Podmanのrootlessコンテナ内からGPUにアクセスする方法。 rocm.docs.amd.com
環境
| 項目 | 値 |
|---|---|
| OS | Ubuntu 24.04.4 LTS |
| GPU | Radeon 890M (gfx1150, RDNA 3.5) |
| ROCm | 10.0.0 |
| Podman | 4.9.3 |
Podmanのrootlessコンテナとは
Podmanはデフォルトでrootlessモードで動作する。(sudo podman runで実行した場合はrootfulモードになる。)
rootlessモードでは、root権限なしでコンテナを実行できるためセキュリティ上の利点がある。
rootlessを実現するためにユーザーネームスペースが使われている。これはLinuxカーネルの機能で、コンテナ内のUID/GIDをホスト上の別のUID/GIDにマッピングする。
マッピングの定義は/etc/subuidと/etc/subgidにある:
$ cat /etc/subuid hirotaka:100000:65536 $ cat /etc/subgid hirotaka:100000:65536
これは「hirotakaユーザーは、ホストUID 100000〜165535の範囲をコンテナ内のUIDにマッピングできる」という意味。
ホスト上でのGPUアクセス
まず、コンテナを介さずホスト上で直接GPUにアクセスできることを確認する。
ROCmのGPUコンピュートに必要なデバイスは2つ。
/dev/kfd(Kernel Fusion Driver): GPUのコンピュートインターフェース。
$ ls -la /dev/kfd crw-rw---- 1 root render 511, 0 9月 15 16:28 /dev/kfd
/dev/dri/renderD128: GPUのDirect Rendering Interfaceノード。
$ ls -la /dev/dri/renderD128 crw-rw----+ 1 root render 226, 128 9月 15 16:28 /dev/dri/renderD128
どちらもグループがrender(GID 110)。自分のユーザーがこのグループに所属しているか確認する。
$ id uid=1000(hirotaka) gid=1000(hirotaka) groups=...,44(video),110(render),...
render(110)グループに所属しているので、ホスト上ではrocminfoも問題なく動く。
問題
ROCm Dockerドキュメントに記載されているコマンドをsudoなしのPodman(rootlessモード)で実行すると、Permission deniedになる。
$ podman run --rm \ --device /dev/kfd --device /dev/dri \ --security-opt seccomp=unconfined \ docker.io/rocm/rocm-terminal:latest \ rocminfo
ROCk module is loaded Unable to open /dev/kfd read-write: Permission denied root is not member of "nogroup" group, the default DRM access group.
ROCk module is loaded: カーネルのROCkモジュール(amdgpu)は読み込まれているUnable to open /dev/kfd read-write: Permission denied:/dev/kfdを開こうとしたが権限がないroot is not member of "nogroup" group:rocminfoのエラーメッセージでは"root"と表示されているが、実際のコンテナ内ユーザーはrocm-user(UID 1000)。いずれにしても、/dev/kfdの所有グループ(コンテナ内では"nogroup"に見える)にはどのユーザーも所属していないためアクセスできない
コンテナ内のユーザーを確認:
$ podman run --rm --device /dev/kfd --device /dev/dri \ docker.io/rocm/rocm-terminal:latest \ id uid=1000(rocm-user) gid=1000(rocm-user) groups=1000(rocm-user),27(sudo),44(video)
rocm-terminalイメージのデフォルトユーザーはrocm-user(UID 1000)で、rootではない。
原因
2つの原因がある。
1. /dev/kfdにACLがない
$ getfacl /dev/kfd user::rw- group::rw- other::--- $ getfacl /dev/dri/renderD128 user::rw- user:hirotaka:rw- group::rw- mask::rw- other::---
/dev/dri/renderD128にはuser:hirotaka:rw-のACLが設定されているが、/dev/kfdにはない。
/dev/dri/renderD128のACLは、udevルール(70-uaccess.rules)のTAG+="uaccess"によりログイン時に自動設定される。/dev/kfdにはこのルールが適用されていないため、手動で追加する。
$ sudo setfacl -m u:$(whoami):rw /dev/kfd
確認:
$ getfacl /dev/kfd user::rw- user:hirotaka:rw- group::rw- mask::rw- other::---
2. ACLを追加してもコンテナ内からアクセスできない
ACLを追加した状態でrootlessコンテナを実行しても、同じPermission deniedになる。原因はPodmanのrootlessコンテナが使うユーザーネームスペースにある。
ユーザーネームスペースはUID/GIDをリマッピングする。コンテナ内の状態を見てみる:
$ podman run --rm --device /dev/kfd --device /dev/dri \ docker.io/rocm/rocm-terminal:latest \ bash -c "ls -la /dev/kfd; id"
crw-rw----+ 1 nobody nogroup 511, 0 Sep 15 07:28 /dev/kfd uid=1000(rocm-user) gid=1000(rocm-user) groups=1000(rocm-user),27(sudo),44(video)
コンテナ内ではデバイスの所有者がnobody:nogroupに変換されている。
ホストとコンテナの比較
| ホスト | コンテナ(デフォルト) | |
|---|---|---|
| /dev/kfd 所有者 | root:render |
nobody:nogroup |
| /dev/dri/renderD128 所有者 | root:render |
nobody:nogroup |
| ユーザー | uid=1000(hirotaka) |
uid=1000(rocm-user) |
| renderグループ | 所属している(GID 110) | なし |
| ACLによるアクセス | あり(user:hirotaka:rw-) |
UID不一致でアクセス不可 |
一見するとコンテナ内でもUID 1000で同じに見えるが、ホストのUID 1000(hirotaka)とコンテナのUID 1000(rocm-user)は別物。
コンテナ内からUIDマッピングを確認する:
$ podman run --rm --device /dev/kfd --device /dev/dri \
docker.io/rocm/rocm-terminal:latest \
cat /proc/self/uid_map
0 1000 1
1 100000 65536
/proc/self/uid_mapの出力は、左から順に3つの数字が1セットになっている。
| 1列目 | 2列目 | 3列目 |
|---|---|---|
| コンテナ内の開始UID | 対応するホストの開始UID | 割り当てる個数 |
1行目: 0 1000 1
- コンテナのUID 0から、ホストのUID 1000にマッピング、範囲は1個
- つまり: コンテナのroot(UID 0) = ホストのhirotaka(UID 1000)
2行目: 1 100000 65536
- コンテナのUID 1から、ホストのUID 100000にマッピング、範囲は65536個
- つまり: コンテナのUID 1〜65536 = ホストのUID 100000〜165535
この2つのルールを使って、コンテナ内のrocm-user(UID 1000)がホスト上でどのUIDになるか計算する。
UID 1000は2行目のルール(UID 1〜65536の範囲)に当てはまる。2行目は「コンテナのUID 1 = ホストのUID 100000」から始まる。コンテナのUID 1からUID 1000までは999個ずれているので、ホスト側もスタート地点(100000)から999ずらす。
【対応表】 コンテナ UID 1 → ホスト UID 100000 コンテナ UID 2 → ホスト UID 100001 コンテナ UID 3 → ホスト UID 100002 ... コンテナ UID 1000 → ホスト UID 100999
コンテナ内では「UID 1000」で実行していても、ホストOS(Linuxカーネル)からは「UID 100999の知らないユーザーがGPUを触ろうとしてきた」と認識されるため、アクセスが拒否される。
ACLはホストUID 1000(hirotaka)に対して設定されているので、ホストUID 100999とは一致しない。
解決策
--userns=keep-idで実行
--userns=keep-idはホストのUIDをコンテナ内でもそのまま保持するオプション。
--userns=keep-idの場合:
$ podman run --rm --userns=keep-id --device /dev/kfd --device /dev/dri \
docker.io/rocm/rocm-terminal:latest \
cat /proc/self/uid_map
0 1 1000
1000 0 1
1001 1001 64536
1行目: 0 1 1000
- コンテナのUID 0から、ホストのUID 1にマッピング、範囲は1000個
- つまり: コンテナのUID 0〜999 = ホストのUID 1〜1000
2行目: 1000 0 1 ※
- コンテナのUID 1000から、ホストのUID 0にマッピング、範囲は1個
- つまり: コンテナのUID 1000 = ホストのUID 0(root)
3行目: 1001 1001 64536
- コンテナのUID 1001から、ホストのUID 1001にマッピング、範囲は64536個
- つまり: コンテナのUID 1001〜65536 = ホストのUID 1001〜65536
※ rocm-terminalイメージのデフォルトユーザーはUID 1000。2行目のルールによりコンテナのUID 1000はホストのUID 0(root)にマッピングされるが、Podmanは--userns=keep-id時にホストのUID(1000)をコンテナプロセスの実行UIDとして設定するため、カーネルのACLチェックはホストUID 1000で行われる。結果としてACLが効く。
$ podman run --rm \ --userns=keep-id \ --device /dev/kfd --device /dev/dri \ --security-opt seccomp=unconfined \ docker.io/rocm/rocm-terminal:latest \ rocminfo 2>&1 | grep -E '(Agent|Name:|Marketing|gfx)'
HSA Agents
Agent 1
Name: AMD Ryzen AI 9 HX 370 w/ Radeon 890M
Marketing Name: AMD Ryzen AI 9 HX 370 w/ Radeon 890M
Vendor Name: CPU
Agent 2
Name: gfx1150
Marketing Name: AMD Radeon Graphics
Vendor Name: AMD
Name: amdgcn-amd-amdhsa--gfx1150
Name: amdgcn-amd-amdhsa--gfx11-generic
sudoなしでGPUが認識された。
デフォルトと--userns=keep-idの比較
デフォルト:
$ podman run --rm --device /dev/kfd --device /dev/dri \ docker.io/rocm/rocm-terminal:latest \ bash -c "ls -la /dev/kfd /dev/dri/renderD128; echo '---'; id"
crw-rw----+ 1 nobody nogroup 226, 128 Sep 15 07:28 /dev/dri/renderD128 crw-rw----+ 1 nobody nogroup 511, 0 Sep 19 09:49 /dev/kfd --- uid=1000(rocm-user) gid=1000(rocm-user) groups=1000(rocm-user),27(sudo),44(video)
--userns=keep-id:
$ podman run --rm --userns=keep-id --device /dev/kfd --device /dev/dri \ docker.io/rocm/rocm-terminal:latest \ bash -c "ls -la /dev/kfd /dev/dri/renderD128; echo '---'; id"
crw-rw----+ 1 nobody nogroup 226, 128 Sep 15 07:28 /dev/dri/renderD128 crw-rw----+ 1 nobody nogroup 511, 0 Sep 19 09:49 /dev/kfd --- uid=1000(rocm-user) gid=1000(rocm-user) groups=1000(rocm-user),27(sudo),44(video)
コンテナ内から見たls -laとidは完全に同じ。しかしrocminfoの結果は異なる:
| デフォルト | --userns=keep-id | |
|---|---|---|
| rocminfo | Permission denied | 動作する |
ls -laやidの出力はコンテナ内のユーザーネームスペースから見た値なので違いがないように見える。しかしカーネルがデバイスアクセスを判定するときはホスト側の実UIDを使う。デフォルトではホストUID 100999、--userns=keep-idではホストUID 1000(hirotaka)になるため、ACLの判定結果が変わる。
ACLの永続化
setfaclによる設定は再起動時にリセットされる。永続化するにはudevルールを追加する。
$ sudo tee /etc/udev/rules.d/70-kfd-acl.rules << 'EOF' KERNEL=="kfd", SUBSYSTEM=="kfd", TAG+="uaccess" EOF
TAG+="uaccess"はログインユーザーに対して自動的にACLを設定する(systemd-logindの機能)。/dev/dri/renderD128に既にACLが付いているのもこの仕組みによるもの。
設定の反映:
$ sudo udevadm control --reload-rules $ sudo udevadm trigger /dev/kfd
まとめ
PodmanのrootlessコンテナでROCm GPUを使うには:
/dev/kfdにACLを追加(初回のみsudoが必要、udevルールで永続化)podman runに--userns=keep-idを付ける