在過去的十年間,容器化技術,特別是以 Docker 為首的解決方案,徹底改變了應用程式的開發、封裝與部署模式。容器提供了一個輕量、可攜、一致的執行環境,將應用程式及其所有依賴項打包在一起,從而消除了「在我的機器上可以執行」的傳統困境。然而,當這股浪潮席捲到高效能運算 (HPC) 與人工智慧 (AI) 及深度學習 (deep learning) 領域時,一個新的挑戰浮現了:標準的 Docker 容器在設計上與硬體解耦,這使得它難以原生支援像 NVIDIA GPUs 這樣的專用硬體。
早期的解決方案試圖將 NVIDIA 驅動程式完整安裝在容器映像檔 (images) 內,但這種方法極其「脆弱」。容器映像檔與主機的驅動程式version必須完全匹配,一旦主機驅動升級,所有相關的容器images都可能失效,這嚴重破壞了容器最核心的可攜性優勢。
為瞭解決這個根本性問題,NVIDIA 推出了 nvidia-docker,並逐步演進為今日的 NVIDIA Container Toolkit。這套工具集提供了一個優雅的解決方案,它作為 Docker 執行環境的插件,在容器啟動時,動態地將主機上必要的 NVIDIA 驅動程式元件與 GPU 設備掛載至容器內。如此一來,容器映像檔本身可以保持驅動程式中立,實現了真正的可攜性與隔離性。
本文旨在提供一份詳細且完整的指南與documentation,深入探討 NVIDIA Container Toolkit 的核心概念、安裝設定、實務應用,並延伸至 Kubernetes 等進階整合,助您完全掌握在容器化環境中駕馭 GPU 的強大能力。
為何需要 NVIDIA Container Toolkit?容器化 GPU 的挑戰與解決方案
要理解 NVIDIA Container Toolkit 的價值,我們必須先深入瞭解 Docker 的運作原理及其在面對 GPU 時的限制。
Docker 的核心優勢:封裝與隔離
Docker 容器本質上是一個在主機作業系統上執行的獨立使用者空間程序。它透過 Linux 核心的命名空間 (namespaces) 和控制羣組 (cgroups) 技術,為應用程式創造了一個隔離的環境。
- 封裝 (Encapsulation):一個 Docker 映像檔包含了應用程式的程式碼、執行時期、系統工具、函式庫等一切所需。這保證了應用程式在任何支援 Docker 的systems中都能以完全相同的方式執行。
- 隔離 (Isolation):每個容器都有自己獨立的檔案系統、網路堆疊和程序空間,與主機及其他容器互不幹擾。
- 可攜性 (Portability):映像檔可以在開發人員的筆記型電腦、公司的測試伺服器或雲端資料中心之間無縫轉移和部署。
GPU 帶來的挑戰
當applications需要使用 GPU 進行加速運算時(例如TensorFlow深度學習訓練),它不僅需要存取 GPU 硬體本身(如 /dev/nvidia0 設備檔案),還需要與之通訊的軟體堆疊,這通常包含兩部分:
- 核心模式驅動程式 (Kernel-mode Driver):直接與硬體互動,由主機作業系統管理。
- 使用者模式元件 (User-mode Components):應用程式在執行時所連結的函式庫,例如 CUDA 函式庫 (libcuda.so)、NVIDIA 管理庫 (libnvidia-ml.so) 等。
標準的 Docker 容器可以透過 –device 參數掛載硬體設備,但它無法感知到使用者模式元件的需求。這導致了前述的「脆弱」解決方案:將使用者模式元件甚至整個驅動程式安裝包打包進映像檔。這種做法的弊病顯而易見:
- 破壞可攜性:容器映像檔與特定版本的主機驅動程式緊密耦合。
- 增加維護成本:主機驅動程式的任何更新都可能需要重建所有相關的 GPU 容器映像檔。
- 映像檔臃腫:驅動程式檔案會顯著增加映像檔的大小。
NVIDIA Container Toolkit 的解決方案
NVIDIA Container Toolkit 巧妙地繞過了這些問題。它並非一個獨立的容器執行時期,而是 Docker 的一個擴充。它的核心是一個稱為「執行時期掛鉤 (runtime hook)」的機制。
當使用者請求啟動一個 GPU 容器時,其運作流程如下:
- 使用者執行 docker run –gpus all … 指令。
- Docker Engine 識別到 –gpus 參數,並調用已設定好的 NVIDIA Container Runtime。
- NVIDIA Container Runtime 在容器的命名空間被建立之前介入,它會偵測主機上的 NVIDIA 驅動程式版本。
- 接著,它將主機上與該驅動程式版本相符的使用者模式函式庫和 GPU 設備檔案動態地掛載到即將建立的容器內部。
- 最後,容器啟動。在容器內部,應用程式可以像在實體機上一樣找到並使用 CUDA 函式庫和 GPU 設備,但它所使用的驅動元件實際上來自於主機。
這個設計帶來了革命性的好處:
- 真正的可攜性:容器映像檔(例如 nvidia/cuda:12.2.2-runtime-ubuntu22.04,其中12.2.2-runtime-ubuntu22.04就是一個tag)是通用的,不包含任何驅動程式。它可以部署在任何安裝了相容 NVIDIA 驅動程式的主機上,無論驅動程式的具體版本為何。
- 簡化的維護:更新主機驅動程式後,無需重建任何映像檔。只需重新啟動容器,NVIDIA Runtime 就會自動掛載新版的驅動元件。
- 安全性:容器內的程序無法影響主機的核心模式驅動程式,維持了良好的隔離性。
安裝與設定:實戰指南
要成功部署 GPU 容器,正確的安裝與設定至關重要。以下步驟將引導您在主流的 Linux 發行版(以 Ubuntu 為例)上完成所有必要配置。
步驟一:前置作業
在安裝 NVIDIA Container Toolkit 之前,請確保您的系統已滿足以下條件:
安裝 NVIDIA 驅動程式:
系統必須已安裝好與您的 GPU 相容的 NVIDIA 驅動程式。在許多 Linux 發行版中,需要先禁用預設的開源 nouveau 驅動。
- 禁用 Nouveau:編輯 /etc/modprobe.d/blacklist.conf 檔案,加入以下兩行:
blacklist nouveau options nouveau modeset=0
儲存後,執行 sudo update-initramfs -u 並重新啟動系統。 - 安裝驅動:您可以從 NVIDIA 官網下載 .run script檔案手動安裝,或透過發行版的套件管理員安裝(推薦)。例如在 Ubuntu 上:
bash # 查找推薦的驅動版本 ubuntu-drivers devices # 安裝指定版本,例如 535 sudo apt install nvidia-driver-535
Note:手動安裝較為複雜,建議優先使用套件管理員。 - 驗證驅動:安裝完成後,執行 nvidia-smi 指令。如果能成功顯示 GPU 資訊,則表示驅動已正常運作。
安裝 Docker Engine:
您的系統必須已安裝 Docker。請參考 Docker 官方documentation完成安裝。安裝後,建議將目前使用者加入 docker 羣組,以避免每次都要輸入 sudo,這不僅是權限問題,也能減少不必要的CPU資源上下文切換。
bash sudo usermod -aG docker $USER # 需要重新登入或 newgrp docker 來讓羣組變更生效
步驟二:安裝 NVIDIA Container Toolkit
隨著技術演進,nvidia-docker2 已被 nvidia-container-toolkit 套件取代。安裝步驟如下:
安裝指令比較
您可以直接複製(copy)以下指令到您的clipboard執行。以下指令會設定the nvidia的官方repository並完成installation。
作業系統 | 指令 |
---|---|
Ubuntu / Debian | curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \| sudo gpg –dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \\ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \| \\ sed ‘s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g’ \| \\ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list \\ && sudo apt-get update \\ && sudo apt-get install -y nvidia-container-toolkit |
RHEL / CentOS | `distribution=(. /etc/os-release;echo IDVERSION_ID) |
curl -s -L https://nvidia.github.io/nvidia-docker/distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo \ && sudo yum install -y nvidia-container-toolkit` |
步驟三:設定 Docker Daemon
這是最關鍵也最容易被忽略的步驟。我們需要告訴 Docker Engine,當遇到 GPU 請求時,應使用 NVIDIA Runtime。
自動設定:
NVIDIA 提供了一個便捷的工具來自動修改 Docker 的設定檔。
sudo nvidia-ctk runtime configure –runtime=docker
此指令會檢查並修改 /etc/docker/daemon.json 檔案,將 nvidia 新增為一個可用的 runtime。
手動設定與最佳化:您也可以手動編輯 /etc/docker/daemon.json。一個完整的設定檔範例如下,其中包含了將 NVIDIA Runtime 設為預設以及日誌輪替的最佳實踐:
json { “default-runtime”: “nvidia”, “runtimes”: { “nvidia”: { “path”: “/usr/bin/nvidia-container-runtime”, “runtimeArgs”: [] } }, “log-driver”: “json-file”, “log-opts”: { “max-size”: “10m”, “max-file”: “20” } }
- “default-runtime”: “nvidia”:將 nvidia 設為預設執行時期。這意味著您執行所有容器時都會經過 NVIDIA 的檢查,並且在執行 GPU 容器時不再需要加上 –runtime=nvidia 參數,只需 –gpus 即可。
- log-driver 和 log-opts:設定日誌輪替,避免容器日誌無限制地增長,佔滿磁碟空間。此處設定為每個日誌檔案最大 10MB,最多保留 20 個檔案。
重啟 Docker 服務:無論是自動還是手動修改,儲存設定後必須重啟 Docker 服務以使其生效。
sudo systemctl restart docker
步驟四:驗證安裝
完成所有設定後,執行一個簡單的測試容器來驗證 GPU 是否能在容器內被正確識別。
docker run --rm --gpus all nvidia/cuda:12.2.2-runtime-ubuntu22.04 nvidia-smi
如果安裝成功,您將在終端機看到與在主機上執行 nvidia-smi 時非常相似的輸出,顯示出 GPU 的型號、驅動版本和 CUDA 版本等資訊。這證明瞭 NVIDIA Container Toolkit 已成功運作。
進階應用與整合
掌握了基本的安裝與使用後,我們可以將 GPU 容器化應用於更複雜的場景,例如與 Kubernetes 或傳統的 HPC 工作負載管理器整合。
整合 Kubernetes (K8s)
在 Kubernetes 這個容器編排的霸主中,要讓 Pod (K8s 中最小的部署單元) 能夠使用節點上的 GPU,需要一個額外的元件:NVIDIA Device Plugin for Kubernetes。
它的作用是什麼?
Device Plugin 是一個 DaemonSet (在每個節點上都執行一個副本的 K8s 物件),其主要職責是:
- 探索 GPU:在所在的節點上偵測有多少個可用的 NVIDIA GPU。
- 註冊資源:向 Kubelet (節點上的代理程式) 報告,將NVIDIA GPUs註冊為一種可調度的資源,名稱為 nvidia.com/gpu。
- 服務請求:當有 Pod 請求使用 GPU 時,Device Plugin 會確保該 Pod 能正確地存取到分配給它的 GPU 設備。
部署步驟:
部署 Device Plugin:
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.1/nvidia-device-plugin.yml
建立請求 GPU 的 Pod 來執行您的applications:
在 Pod 的規格 (spec) 中,透過 resources.limits 來宣告需要多少個 GPU。
yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-test-pod
spec:
restartPolicy: Never
containers:
– name: cuda-container
image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.6.0-ubuntu20.04
resources:
limits:
nvidia.com/gpu: 1 # 請求 1 個 GPU
tolerations:
- key: “nvidia.com/gpu”
operator: “Exists”
effect: “NoSchedule”
驗證:Pod 啟動後,可以查看其日誌。
kubectl logs gpu-test-pod
如果看到類似 [VectorAdd]…Result = PASS 的訊息,就代表 Pod 內的 CUDA 程式已成功在 GPU 上執行。
整合 HPC 工作負載管理器 (以 IBM Spectrum LSF 為例)
在傳統的高效能運算環境中,使用者通常不會直接執行 docker 指令,而是透過作業排程器 (如 LSF, Slurm) 提交工作。這些排程器需要被設定,才能將工作以 NVIDIA Docker 容器的形式執行。
在 IBM Spectrum LSF 中,可以透過定義一個應用程式設定檔 (Application Profile) 來實現。
設定 lsb.applications:
編輯此設定檔,為特定的應用程式 (例如 mydockerapp) 新增 CONTAINER 參數。
ini Begin Application NAME = mydockerapp DESCRIPTION = An application profile for running NVIDIA Docker jobs CONTAINER = nvidia-docker[image(ubuntu:latest) options(–rm –net=host –ipc=host)] End Application
參數說明:
- nvidia-docker:指定使用 NVIDIA Docker 整合。
- image(…):定義要使用的容器映像檔。
- options(…):傳遞給 docker run 的額外參數。對於平行工作,–net=host 和 –ipc=host 通常是必要的,以便 LSF 的管理程序能與容器內的作業進行通訊。
設定完成後,使用者提交工作時只需指定此應用程式設定檔,LSF 便會自動將該工作封裝在指定的 NVIDIA Docker 容器中執行。
常見問題 (FAQ)
Q1: nvidia-docker、nvidia-docker2 和 NVIDIA Container Toolkit 有什麼區別?
A1: 它們是同一技術的演進版本。nvidia-docker 是第一代。nvidia-docker2 是第二代,對架構進行了重構。NVIDIA Container Toolkit 是目前最新的version,名稱上更強調其模組化的工具集特性,它包含了執行時期、掛鉤等所有必要的元件,是目前官方推薦安裝的套件。
Q2: 我需要把 NVIDIA 驅動程式安裝在 Docker 映像檔裡面嗎?
A2: 絕對不要。這正是 NVIDIA Container Toolkit 要解決的問題。您只需在主機上安裝好 NVIDIA 驅動程式。容器映像檔 (images) 應該保持乾淨,不含任何驅動程式相關檔案,只需包含您的應用程式和 CUDA Toolkit 等應用程式層級的函式庫即可。
Q3: 如何讓我的容器只使用特定的 GPU?
A3: 使用 docker run 的 –gpus 參數。例如:
- 使用第一張 GPU (索引為 0):–gpus ‘”device=0″‘
- 使用第一和第三張 GPU:–gpus ‘”device=0,2″‘
- 使用指定 UUID 的 GPU:–gpus ‘”device=GPU-a1b2c3d4…”‘
Q4: 更新主機上的 NVIDIA 驅動程式後,我需要重新建構所有 Docker 映像檔嗎?
A4: 不需要。這是 NVIDIA Container Toolkit 的最大優點之一。因為映像檔本身是驅動程式中立的,所以您只需更新主機驅動程式,然後重新啟動您的容器即可。NVIDIA Runtime 會在容器啟動時自動掛載新版驅動程式的對應元件。
Q5: 在 Kubernetes 中,如何確認我的 Pod 真的有用到 GPU?
A5: 有兩種方法:
- 描述 Pod:執行 kubectl describe pod <your-pod-name>,在 Resources 和 Allocated Resources 區塊,您應該能看到 nvidia.com/gpu: 1 的記錄,表示 K8s 已經為其分配了 GPU 資源。
- 進入 Pod 檢查:執行 kubectl exec -it <your-pod-name> — nvidia-smi。如果能成功在 Pod 內部執行 nvidia-smi 並看到 GPU 資訊,就代表 Pod 已確實獲得並能使用 GPU。
總結
從最初的 nvidia-docker 到如今成熟的 NVIDIA Container Toolkit,NVIDIA 為在容器化環境中利用 GPU 算力鋪平了道路。這項技術不僅解決了驅動程式耦合的根本性難題,更透過其優雅的執行時期掛鉤機制,完美地融合了 Docker 的可攜性與 GPU 的強大效能。無論是個人開發者在本機上透過Docker Hub分享AI實驗,還是大型企業在 Kubernetes 叢集上部署複雜的 MLOps applications,NVIDIA Container Toolkit 都已成為不可或缺的基石。
掌握這套工具,意味著您不僅能夠輕鬆地建立、分享和部署 GPU 加速應用程式,還能在日新月異的雲原生生態中,保持技術的領先性。隨著 AI 的應用日益普及,高效、可靠地管理 GPU 資源的能力,將是所有開發者與系統管理員的核心競爭力之一。
資料來源
- 安裝Docker 及nvidia-docker2
- NVIDIA Docker:讓GPU 服務器應用程序部署變得容易
- 安裝NVIDIA Container Toolkit以GPU配置Docker及K8S