GPU 環境搭建指南:如何在裸機、Docker、K8s 等環境中使用 GPU

探索云原生發表於2024-11-07

how-to-use-gpu

本文主要分享在不同環境,例如裸機、Docker 和 Kubernetes 等環境中如何使用 GPU。

跳轉閱讀原文:GPU 環境搭建指南:如何在裸機、Docker、K8s 等環境中使用 GPU

1. 概述

僅以比較常見的 NVIDIA GPU 舉例,系統為 Linux,對於其他廠家的 GPU 裝置理論上流程都是一樣的。


省流:

  • 對於裸機環境,只需要安裝對應的 GPU Driver 以及 CUDA Toolkit 。

  • 對應 Docker 環境,需要額外安裝 nvidia-container-toolkit 並配置 docker 使用 nvidia runtime。

  • 對應 k8s 環境,需要額外安裝對應的 device-plugin 使得 kubelet 能夠感知到節點上的 GPU 裝置,以便 k8s 能夠進行 GPU 管理。

注:一般在 k8s 中使用都會直接使用 gpu-operator 方式進行安裝,本文主要為了搞清各個元件的作用,因此進行手動安裝。

ps;下一篇分享下如何使用 gpu-operator 快速完成安裝

2. 裸機環境

裸機中要使用上 GPU 需要安裝以下元件:

  • GPU Driver
  • CUDA Toolkit

二者的關係如 NVIDIA 官網上的這個圖所示:

components-of-cuda

GPU Driver 包括了 GPU 驅動和 CUDA 驅動,CUDA Toolkit 則包含了 CUDA Runtime。

GPU 作為一個 PCIE 裝置,只要安裝好之後,在系統中就可以透過 lspci 命令檢視到,先確認機器上是否有 GPU:

root@test:~# lspci|grep NVIDIA
3b:00.0 3D controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)
86:00.0 3D controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)

可以看到,該裝置有兩張 Tesla T4 GPU。

安裝驅動

首先到 NVIDIA 驅動下載 下載對應的顯示卡驅動:

search-gpu-driver

最終下載得到的是一個.run 檔案,例如 NVIDIA-Linux-x86_64-550.54.14.run

然後直接 sh 方式執行該檔案即可

sh NVIDIA-Linux-x86_64-550.54.14.run

接下來會進入圖形化介面,一路選擇 yes / ok 就好

執行以下命令檢查是否安裝成功

nvidia-smi

如果出現顯示卡資訊則是安裝成功,就像這樣:

root@test:~ nvidia-smi
Wed Jul 10 05:41:52 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.08             Driver Version: 535.161.08   CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  Tesla T4                       On  | 00000000:3B:00.0 Off |                    0 |
| N/A   51C    P0              29W /  70W |  12233MiB / 15360MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
|   1  Tesla T4                       On  | 00000000:86:00.0 Off |                    0 |
| N/A   49C    P0              30W /  70W |   6017MiB / 15360MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|   
+---------------------------------------------------------------------------------------+

至此,我們就安裝好 GPU 驅動了,系統也能正常識別到 GPU。

這裡顯示的 CUDA 版本表示當前驅動最大支援的 CUDA 版本。

安裝 CUDA Toolkit

對於深度學習程式,一般都要依賴 CUDA 環境,因此需要在機器上安裝 CUDA Toolkit

也是到 NVIDIA CUDA Toolkit 下載 下載對應的安裝包,選擇作業系統和安裝方式即可

download-cuda-toolkit

和安裝驅動類似,也是一個 .run 檔案

# 下載安裝檔案
wget https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.run

# 開始安裝
sudo sh cuda_12.2.0_535.54.03_linux.run

注意:之前安裝過驅動了,這裡就不再安裝驅動,僅安裝 CUDA Toolkit 相關元件

安裝完成後輸出如下:

root@iZbp15lv2der847tlwkkd3Z:~# sudo sh cuda_12.2.0_535.54.03_linux.run
===========
= Summary =
===========

Driver:   Installed
Toolkit:  Installed in /usr/local/cuda-12.2/

Please make sure that
 -   PATH includes /usr/local/cuda-12.2/bin
 -   LD_LIBRARY_PATH includes /usr/local/cuda-12.2/lib64, or, add /usr/local/cuda-12.2/lib64 to /etc/ld.so.conf and run ldconfig as root

To uninstall the CUDA Toolkit, run cuda-uninstaller in /usr/local/cuda-12.2/bin
To uninstall the NVIDIA Driver, run nvidia-uninstall
Logfile is /var/log/cuda-installer.log

根據提示配置下 PATH

# 新增 CUDA 12.2 到 PATH
export PATH=/usr/local/cuda-12.2/bin:$PATH

# 新增 CUDA 12.2 的 lib64 到 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/cuda-12.2/lib64:$LD_LIBRARY_PATH

執行以下命令檢視版本,確認安裝成功

root@iZbp15lv2der847tlwkkd3Z:~# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Tue_Jun_13_19:16:58_PDT_2023
Cuda compilation tools, release 12.2, V12.2.91
Build cuda_12.2.r12.2/compiler.32965470_0

測試

我們使用一個簡單的 Pytorch 程式來檢測 GPU 和 CUDA 是否正常。

整個呼叫鏈大概是這樣的:

cuda-call-flow

使用下面程式碼來測試能夠正常使用, check_cuda_pytorch.py 內容如下:

import torch

def check_cuda_with_pytorch():
    """檢查 PyTorch CUDA 環境是否正常工作"""
    try:
        print("檢查 PyTorch CUDA 環境:")
        if torch.cuda.is_available():
            print(f"CUDA 裝置可用,當前 CUDA 版本是: {torch.version.cuda}")
            print(f"PyTorch 版本是: {torch.__version__}")
            print(f"檢測到 {torch.cuda.device_count()} 個 CUDA 裝置。")
            for i in range(torch.cuda.device_count()):
                print(f"裝置 {i}: {torch.cuda.get_device_name(i)}")
                print(f"裝置 {i} 的視訊記憶體總量: {torch.cuda.get_device_properties(i).total_memory / (1024 ** 3):.2f} GB")
                print(f"裝置 {i} 的視訊記憶體當前使用量: {torch.cuda.memory_allocated(i) / (1024 ** 3):.2f} GB")
                print(f"裝置 {i} 的視訊記憶體最大使用量: {torch.cuda.memory_reserved(i) / (1024 ** 3):.2f} GB")
        else:
            print("CUDA 裝置不可用。")
    except Exception as e:
        print(f"檢查 PyTorch CUDA 環境時出現錯誤: {e}")

if __name__ == "__main__":
    check_cuda_with_pytorch()

先安裝下 torch

pip install torch

執行一下

python3 check_cuda_pytorch.py

正常輸出應該是這樣的:

檢查 PyTorch CUDA 環境:
CUDA 裝置可用,當前 CUDA 版本是: 12.1
PyTorch 版本是: 2.3.0+cu121
檢測到 1 個 CUDA 裝置。
裝置 0: Tesla T4
裝置 0 的視訊記憶體總量: 14.75 GB
裝置 0 的視訊記憶體當前使用量: 0.00 GB
裝置 0 的視訊記憶體最大使用量: 0.00 GB

3. Docker 環境

上一步中我們已經在裸機上安裝了 GPU Driver,CUDA Toolkit 等工具,實現了在宿主機上使用 GPU。

現在希望在 Docker 容器中使用 GPU,需要怎麼處理呢?

為了讓 Docker 容器中也能使用 GPU,大致步驟如下:

  • 1)安裝 nvidia-container-toolkit 元件
  • 2)docker 配置使用 nvidia-runtime
  • 3)啟動容器時增加 --gpu 引數

安裝 nvidia-container-toolkit

NVIDIA Container Toolkit 的主要作用是將 NVIDIA GPU 裝置掛載到容器中。

相容生態系統中的任意容器執行時,docker、containerd、cri-o 等。

NVIDIA 官方安裝文件:nvidia-container-toolkit-install-guide

對於 Ubuntu 系統,安裝命令如下:

# 1. Configure the production repository
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

# Optionally, configure the repository to use experimental packages 
sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list

# 2. Update the packages list from the repository
sudo apt-get update

# 3. Install the NVIDIA Container Toolkit packages
sudo apt-get install -y nvidia-container-toolkit

配置使用該 runtime

支援 Docker, Containerd, CRI-O, Podman 等 CRI。

具體見官方文件 container-toolkit#install-guide

這裡以 Docker 為例進行配置:

舊版本需要手動在 /etc/docker/daemon.json 中增加配置,指定使用 nvidia 的 runtime。

    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }

新版 toolkit 帶了一個nvidia-ctk 工具,執行以下命令即可一鍵配置:

sudo nvidia-ctk runtime configure --runtime=docker

然後重啟 Docker 即可

sudo systemctl restart docker

測試

安裝nvidia-container-toolkit 後,整個呼叫鏈如下:

nv-container-runtime-call-flow

呼叫鏈從 containerd --> runC 變成 containerd --> nvidia-container-runtime --> runC 。

然後 nvidia-container-runtime 在中間攔截了容器 spec,就可以把 gpu 相關配置新增進去,再傳給 runC 的 spec 裡面就包含 gpu 資訊了。

Docker 環境中的 CUDA 呼叫大概是這樣的:

cuda-call-in-container.png

從圖中可以看到,CUDA Toolkit 跑到容器裡了,因此宿主機上不需要再安裝 CUDA Toolkit。

使用一個帶 CUDA Toolkit 的映象即可。

最後我們啟動一個 Docker 容器進行測試,其中命令中增加 --gpu 引數來指定要分配給容器的 GPU。

--gpu 引數可選值:

  • --gpus all:表示將所有 GPU 都分配給該容器
  • --gpus "device=<id>[,<id>...]":對於多 GPU 場景,可以透過 id 指定分配給容器的 GPU,例如 --gpu "device=0" 表示只分配 0 號 GPU 給該容器
    • GPU 編號則是透過nvidia-smi 命令進行檢視

這裡我們直接使用一個帶 cuda 的映象來測試,啟動該容器並執行nvidia-smi 命令

docker run --rm --gpus all  nvidia/cuda:12.0.1-runtime-ubuntu22.04 nvidia-smi

正常情況下應該是可以列印出容器中的 GPU 資訊的。

4. k8s 環境

更進一步,在 k8s 環境中使用 GPU,則需要在叢集中部署以下元件:

  • gpu-device-plugin 用於管理 GPU,device-plugin 以 DaemonSet 方式執行到叢集各個節點,以感知節點上的 GPU 裝置,從而讓 k8s 能夠對節點上的 GPU 裝置進行管理。
  • gpu-exporter:用於監控 GPU

各元件關係如下圖所示:

k8s-gpu-manual-instll-vs-gpu-operator

  • 左圖為手動安裝的場景,只需要在叢集中安裝 device-plugin 和 監控即可使用。

  • 右圖為使用 gpu-operotar 安裝場景,本篇暫時忽略

大致工作流程如下:

  • 每個節點的 kubelet 元件維護該節點的 GPU 裝置狀態(哪些已用,哪些未用)並定時報告給排程器,排程器知道每一個節點有多少張 GPU 卡可用。
  • 排程器為 pod 選擇節點時,從符合條件的節點中選擇一個節點。
  • 當 pod 排程到節點上後,kubelet 元件為 pod 分配 GPU 裝置 ID,並將這些 ID 作為引數傳遞給 NVIDIA Device Plugin
  • NVIDIA Device Plugin 將分配給該 pod 的容器的 GPU 裝置 ID 寫入到容器的環境變數 NVIDIA_VISIBLE_DEVICES中,然後將資訊返回給 kubelet。
  • kubelet 啟動容器。
  • NVIDIA Container Toolkit 檢測容器的 spec 中存在環境變數 NVIDIA_VISIBLE_DEVICES,然後根據環境變數的值將 GPU 裝置掛載到容器中。

在 Docker 環境我們在啟動容器時透過 --gpu 引數手動指定分配給容器的 GPU,k8s 環境則由 device-plugin 自行管理。

安裝 device-plugin

device-plugin 一般由對應的 GPU 廠家提供,比如 NVIDIA 的 k8s-device-plugin

安裝其實很簡單,將對應的 yaml apply 到叢集即可。

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.15.0/deployments/static/nvidia-device-plugin.yml

就像這樣

root@test:~# kgo get po -l app=nvidia-device-plugin-daemonset
NAME                                   READY   STATUS    RESTARTS   AGE
nvidia-device-plugin-daemonset-7nkjw   1/1     Running   0          10m

device-plugin 啟動之後,會感知節點上的 GPU 裝置並上報給 kubelet,最終由 kubelet 提交到 kube-apiserver。

因此我們可以在 Node 可分配資源中看到 GPU,就像這樣:

root@test:~# k describe node test|grep Capacity -A7
Capacity:
  cpu:                48
  ephemeral-storage:  460364840Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             98260824Ki
  nvidia.com/gpu:     2
  pods:               110

可以看到,除了常見的 cpu、memory 之外,還有nvidia.com/gpu, 這個就是 GPU 資源,數量為 2 說明我們有兩張 GPU。

安裝 GPU 監控

除此之外,如果你需要監控叢集 GPU 資源使用情況,你可能還需要安裝 DCCM exporter 結合 Prometheus 輸出 GPU 資源監控資訊。

helm repo add gpu-helm-charts \
  https://nvidia.github.io/dcgm-exporter/helm-charts
  
 helm repo update
 
 
 helm install \
    --generate-name \
    gpu-helm-charts/dcgm-exporter

檢視 metrics

curl -sL http://127.0.0.1:8080/metrics
# HELP DCGM_FI_DEV_SM_CLOCK SM clock frequency (in MHz).# TYPE DCGM_FI_DEV_SM_CLOCK gauge# HELP DCGM_FI_DEV_MEM_CLOCK Memory clock frequency (in MHz).# TYPE DCGM_FI_DEV_MEM_CLOCK gauge# HELP DCGM_FI_DEV_MEMORY_TEMP Memory temperature (in C).# TYPE DCGM_FI_DEV_MEMORY_TEMP gauge
...
DCGM_FI_DEV_SM_CLOCK{gpu="0", UUID="GPU-604ac76c-d9cf-fef3-62e9-d92044ab6e52",container="",namespace="",pod=""} 139
DCGM_FI_DEV_MEM_CLOCK{gpu="0", UUID="GPU-604ac76c-d9cf-fef3-62e9-d92044ab6e52",container="",namespace="",pod=""} 405
DCGM_FI_DEV_MEMORY_TEMP{gpu="0", UUID="GPU-604ac76c-d9cf-fef3-62e9-d92044ab6e52",container="",namespace="",pod=""} 9223372036854775794
...

測試

在 k8s 建立 Pod 要使用 GPU 資源很簡單,和 cpu、memory 等常規資源一樣,在 resource 中 申請即可。

比如,下面這個 yaml 裡面我們就透過 resource.limits 申請了該 Pod 要使用 1 個 GPU。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  restartPolicy: Never
  containers:
    - name: cuda-container
      image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2
      resources:
        limits:
          nvidia.com/gpu: 1 # requesting 1 GPU

這樣 kueb-scheduler 在排程該 Pod 時就會考慮到這個情況,將其排程到有 GPU 資源的節點。

啟動後,檢視日誌,正常應該會列印 測試透過的資訊。

kubectl logs gpu-pod
[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done

至此,在 k8s 環境中也可以使用 GPU 了。


【Kubernetes 系列】持續更新中,搜尋公眾號【探索雲原生】訂閱,閱讀更多文章。


5. 小結

本文主要分享了在裸機、Docker 環境、k8s 環境中如何使用 GPU。

  • 對於裸機環境,只需要安裝對應的 GPU Driver 即可。

  • 對應 Docker 環境,需要額外安裝 nvidia-container-toolkit 並配置 docker 使用 nvidia runtime。

  • 對應 k8s 環境,需要額外安裝對應的 device-plugin 使得 kubelet 能夠感知到節點上的 GPU 裝置,以便 k8s 能夠進行 GPU 管理。

現在一般都是在 k8s 環境中使用,為了簡化安裝步驟, NVIDIA 也提供了 gpu-operator來簡化安裝部署,後續分享一下如何使用 gpu-operator 來快速安裝。

相關文章