日前遇到需求是要在 GCP 的 GKE 上建置 nested container 且最內層的 Container 要可以連接到 GPU。
目前實測不考慮 GPU 下, 利用 Podman 或 docker in docker 或 crun + cgroup 都可以順利完成,但總是無法在最內層的容器抓到 GPU, 想詢問各位先進,有沒有相關經驗可以提供我作為 debug 的方向。
非常感謝!
Grok回答如下
在 GCP 的 GKE 上建置嵌套容器(nested container)並讓最內層容器連接到 GPU 是一個複雜的挑戰,涉及容器運行時、權限、cgroup 設定、GPU 驅動程式以及 GKE 的特定限制。以下是針對你的問題的一些分析和 debug 方向,結合現有的技術背景和可能遇到的問題:
---
### 問題分析
1. **GKE 的 GPU 支援**:
- GKE 支援 NVIDIA GPU,但需要特定的節點設定(例如,啟用 GPU 節點池並安裝 NVIDIA 驅動程式)。
- 在 GKE 中,GPU 是通過 Kubernetes 資源(`nvidia.com/gpu`)分配給 Pod 的,且需要 NVIDIA 容器工具包(NVIDIA Container Toolkit)來將 GPU 暴露給容器。
- 嵌套容器環境增加了複雜性,因為最內層容器需要正確繼承 GPU 設備和相關的驅動程式庫。
2. **嵌套容器的挑戰**:
- 嵌套容器(例如 Docker-in-Docker 或 Podman)需要特定的權限(通常是 `--privileged` 或特定的 capability)來訪問底層設備(如 `/dev/nvidia*`)。
- 在 GKE 上,預設的安全設定(例如 Pod 安全性策略或 seccomp)可能限制嵌套容器的設備訪問。
- cgroups(尤其是 cgroup v2)可能導致嵌套容器無法正確管理資源或設備,這在你的描述中已經提到。
3. **GPU 訪問問題**:
- 最內層容器無法看到 GPU,可能是因為:
- GPU 設備未正確傳遞到內層容器(例如,缺少 `--device` 或 CDI 設定)。
- NVIDIA 驅動程式庫未正確掛載到內層容器(例如,`/usr/lib/nvidia` 或 `/usr/local/nvidia`)。
- 嵌套容器運行時(Podman、Docker 或 crun)未正確配置 NVIDIA Container Toolkit。
- GKE 的安全限制(例如,SELinux、AppArmor 或 seccomp)阻止了設備訪問。
4. **Podman/Docker-in-Docker/crun 的行為**:
- **Podman**:作為無守護進程的容器工具,Podman 支援 rootless 和 privileged 模式,但在嵌套場景下,cgroup v2 和設備傳遞可能需要額外配置。
- **Docker-in-Docker (DinD)**:DinD 需要掛載 Docker socket 或以特權模式運行,這可能與 GPU 設備傳遞衝突。
- **crun**:作為輕量級 OCI 運行時,crun 支援 cgroup v2,但在嵌套容器中可能遇到 cgroup 路徑或權限問題。
---
### Debug 方向與建議
以下是一些具體的 debug 方向和解決方案,針對你在 GKE 上建置嵌套容器並讓內層容器訪問 GPU 的需求:
#### 1. 確認 GKE 節點的 GPU 設定
- **檢查 GPU 是否可用**:
確保你的 GKE 節點池已正確配置 GPU(例如,A100、V100 或 T4)。運行以下命令檢查:
```bash
kubectl describe node <node-name> | grep nvidia.com/gpu
```
確認 `nvidia.com/gpu` 資源存在且分配正確。
- **檢查 NVIDIA 驅動程式**:
GKE 使用 NVIDIA GPU 驅動程式 DaemonSet 自動安裝驅動程式。確保 DaemonSet 已正確運行:
```bash
kubectl get daemonset -n kube-system | grep nvidia
```
如果未看到 `nvidia-driver-installer` 或類似 DaemonSet,請按照 [GKE 文檔](https://cloud.google.com/kubernetes-engine/docs/how-to/gpus) 手動啟用 GPU 支援。
- **驗證 NVIDIA Container Toolkit**:
確保節點已安裝 NVIDIA Container Toolkit(`nvidia-container-toolkit`)。你可以進入節點並檢查:
```bash
nvidia-container-cli --version
```
如果未安裝,可能需要手動配置。
#### 2. 配置嵌套容器環境
- **使用特權模式**:
在嵌套容器場景中,外部容器通常需要以特權模式運行以訪問 GPU 設備。確保你的 Pod 配置文件包含:
```yaml
spec:
containers:
- name: outer-container
securityContext:
privileged: true
resources:
limits:
nvidia.com/gpu: 1
```
這確保外部 Pod 可以訪問 GPU 並將其傳遞給內層容器。
- **掛載 GPU 設備**:
確保外部容器正確掛載了 GPU 相關的設備和庫。例如,在 Docker 或 Podman 中運行外部容器時,添加以下參數:
```bash
podman run --privileged --device=/dev/nvidia0:/dev/nvidia0 --device=/dev/nvidia-ctl:/dev/nvidia-ctl --device=/dev/nvidia-uvm:/dev/nvidia-uvm -v /usr/lib/nvidia:/usr/lib/nvidia ...
```
對於嵌套容器,內層容器也需要類似的設備傳遞。
- **檢查 CDI 支援**:
NVIDIA Container Toolkit 支援容器設備介面(CDI),這是將 GPU 暴露給容器的現代方式。確保你的 Podman 或 Docker 配置使用了 CDI:
```bash
podman run --device nvidia.com/gpu=all ...
```
如果 CDI 未生效,檢查 `/etc/cdi` 是否包含 NVIDIA 相關的 CDI 配置文件(例如,`nvidia.yaml`)。
#### 3. 解決 cgroup 相關問題
- **檢查 cgroup 版本**:
GKE 預設使用 cgroup v2(尤其在較新的版本中)。確認節點的 cgroup 版本 ```bash
stat -fc %T /sys/fs/cgroup/
```
如果節點使用 cgroup v2,確保你的嵌套容器運行時(Podman、Docker 或 crun)支援 cgroup v2。根據你的描述,cgroup v2 可能導致嵌套容器無法正確訪問設備。
- **解決 cgroup 路徑問題**:
在嵌套容器中,內層容器可能無法訪問正確的 cgroup 路徑(例如,`/sys/fs/cgroup/devices/libpod_parent`)。嘗試以下解決方案:
- 啟用嵌套 cgroup 支援(僅適用於 cgroup v2):
```bash
mkdir -p /sys/fs/cgroup/init
xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || :
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers > /sys/fs/cgroup/cgroup.subtree_control
```
這確保內層容器可以在獨立的 cgroup 中運行。[](https://stackoverflow.com/questions/70872355/how-to-resolve-cgroup-error-when-running-docker-container-inside-a-docker-contai)
- 如果問題持續,考慮在外部容器中運行 systemd 以管理 cgroup:
```bash
podman run --privileged --systemd=always ...
```
- **檢查權限**:
確保內層容器的 cgroup 路徑(例如,`/sys/fs/cgroup/devices`)對運行用戶是可寫的。如果使用 rootless Podman,檢查 `XDG_RUNTIME_DIR` 是否正確設置並可寫。[](https://github.com/containers/podman/blob/main/troubleshooting.md)
#### 4. 驗證內層容器的 GPU 訪問
- **運行 nvidia-smi**:
在內層容器中運行以下命令檢查 GPU 是否可見:
```bash
nvidia-smi
```
如果失敗,檢查以下內容:
- 確保內層容器正確繼承了外部容器的設備和庫。
- 檢查是否有安全限制(例如,SELinux 或 AppArmor)阻止了訪問。
- **檢查日誌**:
查看內層容器的日誌,尋找與 GPU 相關的錯誤(例如,`nvidia-container-cli: mount error` 或 `no such device`)。這些錯誤可能指向設備掛載或權限問題。[](https://lists.podman.io/archives/list/podman%40lists.podman.io/thread/VOF6W7LQ5Q3464F5EO2YN2ZTJWE27H5E/)
- **測試簡單用例**:
使用一個簡單的 CUDA 容器(例如,`nvidia/cuda:11.0.3-base-ubuntu20.04`)作為內層容器,確保問題不是由應用程式本身引起:
```bash
podman run --rm --device nvidia.com/gpu=all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi
```
#### 5. 檢查 GKE 特定的限制
- **Pod 安全性策略**:
GKE 可能應用了 Pod 安全性策略(PodSecurityPolicy 或 Gatekeeper),限制了特權模式或設備訪問。檢查集群的安全策略:
```bash
kubectl get psp
```
如果需要,創建一個允許特權容器和設備訪問的策略。
- **網絡和 DNS 配置**:
確保內層容器的網絡配置正確,特別是如果 GPU 相關的操作需要訪問外部服務(例如,NVIDIA 驅動程式下載)。檢查 DNS 設置:
```bash
podman run --rm nvidia/cuda:11.0.3-base-ubuntu20.04 cat /etc/resolv.conf
```
如果 DNS 配置無效,添加 `--dns` 參數。[](https://docs.podman.io/en/latest/markdown/podman-run.1.html)
- **GKE 版本兼容性**:
確保你的 GKE 集群版本支援嵌套容器和 GPU。較舊的版本可能有已知的 cgroup 或設備傳遞問題。考慮升級到最新的穩定版本(例如,1.27 或更高)。
#### 6. 替代方案
如果嵌套容器無法穩定訪問 GPU,考慮以下替代方案:
- **直接在 Pod 中運行 GPU 工作負載**:
避免嵌套容器,直接在 GKE Pod 中運行 GPU 工作負載。這需要調整你的應用程式架構,但可以減少複雜性。
- **使用 Kubernetes Job 或 DaemonSet**:
將內層容器的任務封裝為 Kubernetes Job 或 DaemonSet,直接分配 GPU 資源。
- **使用 GKE 的 Autopilot 模式**:
GKE Autopilot 自動管理節點配置,可能簡化 GPU 和容器運行時的設定。
- **測試其他運行時**:
如果 crun 持續失敗,嘗試使用 runc 或其他 OCI 運行時,並確保它們與 NVIDIA Container Toolkit 兼容。
---
### Debug 步驟總結
1. 確認 GKE 節點的 GPU 資源和 NVIDIA 驅動程式是否正確配置。
2. 確保外部容器以特權模式運行並正確掛載 GPU 設備和庫。
3. 檢查 cgroup v2 配置,啟用嵌套支援或使用 systemd 管理 cgroup。
4. 在內層容器中運行 `nvidia-smi` 並檢查日誌以診斷 GPU 訪問問題。
5. 檢查 GKE 的安全策略、網絡配置和集群版本是否限制了嵌套容器。
6. 如果問題持續,簡化架構或測試替代運行時。
---
### 常見問題與解決方案
- **問題**:內層容器報錯 `nvidia-container-cli: mount error: permission denied`。
- **解決方案**:確保外部容器以特權模式運行,並檢查 SELinux/AppArmor 設定。嘗試禁用安全限制(僅用於測試):
```bash
podman run --security-opt label=disable ...
```
[](https://lists.podman.io/archives/list/podman%40lists.podman.io/thread/VOF6W7LQ5Q3464F5EO2YN2ZTJWE27H5E/)
- **問題**:cgroup 相關錯誤(例如,`cannot enter cgroupv2` 或 `pids.max: no such file`)。
- **解決方案**:啟用嵌套 cgroup 支援或在外部容器中運行 systemd。確保 `XDG_RUNTIME_DIR` 可寫。[](https://stackoverflow.com/questions/70872355/how-to-resolve-cgroup-error-when-running-docker-container-inside-a-docker-contai)[](https://access.redhat.com/solutions/5913671)
- **問題**:內層容器無法解析 NVIDIA 相關的域名。
- **解決方案**:添加 `--dns` 參數或檢查 `/etc/resolv.conf` 是否正確。[](https://docs.podman.io/en/latest/markdown/podman-run.1.html)
---
### 後續步驟
如果你已經嘗試了上述步驟但問題仍未解決,請提供以下信息以進一步診斷:
1. 你的 GKE 集群版本和節點作業系統(例如,COS 或 Ubuntu)。
2. 外部和內層容器的具體運行命令(包括所有參數)。
3. 內層容器運行 `nvidia-smi` 時的完整錯誤訊息。
4. 外部容器的 cgroup 配置(例如,`cat /sys/fs/cgroup/cgroup.controllers`)。
5. 是否使用了特定的安全策略或網絡配置。
這些信息可以幫助我提供更精確的建議。如果你有其他問題或需要更詳細的配置範例,請隨時告訴我!