1. 引言

本文主要分享在不同环境,例如ECS、Docker 和 Kubernetes 等环境中如何使用 GPU。 注:由于没有物理机裸机,在阿里云上申请ECS也可满足学习使用。

2. 规划

基于如下环境搭建: picture 0
查看worker节点GPU信息:

[root@k8s-worker1 ~]# lspci | grep -i nvidia
00:07.0 VGA compatible controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)
[root@k8s-worker1 ~]# 

说明该worker节点有1张NVIDIA Tesla T4的GPU卡。

3. 概述

仅以比较常见的 NVIDIA GPU 举例,系统为 Linux,对于其他厂家的 GPU 设备理论上流程都是一样的。

省流:

  • 对于ECS环境,只需要安装对应的 GPU Driver(GPU计算型实例为Tesla驱动,GPU虚拟化型实例为GRID驱动) 以及 CUDA Toolkit 。
  • 对应 Docker 环境,需要额外安装 nvidia-container-toolkit 并配置 docker 使用 nvidia runtime。
  • 对应 k8s 环境,需要额外安装对应的 device-plugin 使得 kubelet 能够感知到节点上的 GPU 设备,以便 k8s 能够进行 GPU 管理。

注:一般在 k8s 中使用都会直接使用 gpu-operator 方式进行安装,本文主要为了搞清各个组件的作用,因此进行手动安装。

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

4. ECS环境

裸机中要使用上 GPU 需要安装以下组件:

  • GPU Driver
  • CUDA Toolkit 二者的关系如 NVIDIA 官网上的这个图所示:

picture 0

GPU Driver 包括了 GPU 驱动和 CUDA 驱动,CUDA Toolkit 则包含了 CUDA Runtime。

GPU 作为一个 PCIE 设备,只要安装好之后,在系统中就可以通过 lspci 命令查看到,先确认机器上是否有 GPU:

[root@k8s-worker1 ~]# lspci | grep -i nvidia
00:07.0 VGA compatible controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)
[root@k8s-worker1 ~]# 

说明该worker节点有1张NVIDIA Tesla T4的GPU卡。

4.1. 安装GRID驱动

由于主机规格是ecs.sgn6i-vws-m2.xlarge,是虚拟化型规格,不能安装Tesla驱动的,需要安装grid驱动。 安装Tesla驱动的话将会出现ERROR: Unable to load the kernel module ‘nvidia.ko’.错误安装失败。

安装步骤参考:在GPU虚拟化型实例中安装GRID驱动(Linux)

4.1.1. 准备安装脚本

install-grid.sh内容如下:

#!/bin/bash
if acs-plugin-manager --list --local | grep grid_driver_install > /dev/null 2>&1
then
            acs-plugin-manager --remove --plugin grid_driver_install
fi

acs-plugin-manager --exec --plugin grid_driver_install

4.1.2. 安装grid

[root@k8s-worker1 ~]# sh install-grid.sh 
RemovePlugin success, plugin[grid_driver_install]
INFO[0000] Starting environment pre-check               
INFO[0000] environment pre-check done                   
INFO[0000] Check gpu device present                     
INFO[0000] Check gpu device present done                
INFO[0000] current installed gird driver is, 470.239.06 
INFO[0000] current installed gird driver is already SWL driver 
[root@k8s-worker1 ~]# 

4.1.3. 验证

root@k8s-worker1 ~]# nvidia-smi

运行成功,可看到有一张T4-2Q的GPU卡。 picture 6
至此,我们就安装好 GPU 驱动了,系统也能正常识别到 GPU。

这里显示的 CUDA 版本表示当前驱动最大支持的 CUDA 版本。

4.2. 安装CUDA

4.2.1. 安装软件

wget https://developer.download.nvidia.com/compute/cuda/11.4.0/local_installers/cuda_11.4.0_470.42.01_linux.run
sudo sh cuda_11.4.0_470.42.01_linux.run

由于已安装了GRID驱动,需取消cuda自带的驱动安装: picture 7
01) 安装全部组件: picture 8
02) 输出: picture 9

03) 执行以下命令,重启GPU实例

reboot

04) 依次执行以下命令,配置CUDA环境变量。

echo 'export PATH=/usr/local/cuda/bin:$PATH' | sudo tee /etc/profile.d/cuda.sh
source /etc/profile

05) 检查CUDA是否成功安装。

a) 执行nvcc -V命令,检查CUDA安装版本是否正确。 picture 10

b) 依次执行以下命令,测试CUDA Samples,验证CUDA是否安装成功。

    [root@k8s-worker1 ~]# cd /usr/local/cuda-11.4/extras/demo_suite/
    [root@k8s-worker1 demo_suite]# ./deviceQuery  

如果返回结果显示Result=PASS,则表示CUDA安装成功。 picture 11

06) 测试 我们使用一个简单的 Pytorch 程序来检测 GPU 和 CUDA 是否正常。

整个调用链大概是这样的: picture 12
使用下面代码来测试能够正常使用, 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

python -m pip install torch

运行一下

python check_cuda_pytorch.py

正常输出应该是这样的: picture 13

4.3. 更新CUDA

由于有些应用需要更高级的版本的CUDA,需升级,如下升级为12.6版本。

sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
sudo dnf clean all
sudo dnf -y install cuda-toolkit-12-6

picture 18
验证: picture 19
说明12.6版本安装成功。 picture 20

5. Docker环境

上一步中我们已经在裸机上安装了 GPU Driver,CUDA Toolkit 等工具,实现了在宿主机上使用 GPU。

现在希望在 Docker 容器中使用 GPU,需要怎么处理呢?

为了让 Docker 容器中也能使用 GPU,大致步骤如下:

  1. 安装docker,已有则跳过这步骤。
  2. 安装 nvidia-container-toolkit 组件
  3. docker 配置使用 nvidia-runtime
  4. 启动容器时增加 –gpu 参数

5.1. Docker安装

5.1.1. 安装必要的一些系统工具

sudo yum install -y yum-utils

5.1.2. 添加软件源信息

sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

5.1.3. 安装Docker

sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

5.1.4. 开启Docker服务

sudo service docker start

5.1.5. 验证

root@k8s-worker1 pytorch]# docker version
Client: Docker Engine - Community
 Version:           26.1.3
 API version:       1.45
 Go version:        go1.21.10
 Git commit:        b72abbb
 Built:             Thu May 16 08:34:39 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          26.1.3
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.10
  Git commit:       8e96db1
  Built:            Thu May 16 08:33:34 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.32
  GitCommit:        8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
[root@k8s-worker1 pytorch]# 

5.2. 安装 nvidia-container-toolkit

NVIDIA Container Toolkit 的主要作用是将 NVIDIA GPU 设备挂载到容器中。 >兼容生态系统中的任意容器运行时,docker、containerd、cri-o 等。

NVIDIA 官方安装文档:nvidia-container-toolkit-install-guide

ALIOS或centos安装命令如下:

# Configure the production repository:
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo

# Optionally, configure the repository to use experimental packages:
sudo yum-config-manager --enable nvidia-container-toolkit-experimental

#Install the NVIDIA Container Toolkit packages:
sudo yum install -y nvidia-container-toolkit

安装成功如下: picture 14

5.3. 配置使用该 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 工具,执行以下命令即可一键配置然后重启docker:

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

picture 15

5.4. 验证

安装nvidia-container-toolkit 后,整个调用链如下: picture 16

调用链从 containerd –> runC 变成 containerd –> nvidia-container-runtime –> runC 。

然后 nvidia-container-runtime 在中间拦截了容器 spec,就可以把 gpu 相关配置添加进去,再传给 runC 的 spec 里面就包含 gpu 信息了。

Docker 环境中的 CUDA 调用大概是这样的: picture 17

从图中可以看到,CUDA Toolkit 跑到容器里了,因此宿主机上不需要再安装 CUDA Toolkit。

使用一个带 CUDA Toolkit 的镜像即可。

最后我们启动一个 Docker 容器进行测试,其中命令中增加 –gpu参数来指定要分配给容器的 GPU。

gpu 参数可选值:

  • gpus all:表示将所有 GPU 都分配给该容器
  • gpus “device=[,…]“:对于多 GPU 场景,可以通过 id 指定分配给容器的 GPU,例如 –gpu “device=0” 表示只分配 0 号 GPU 给该容器 GPU 编号则是通过nvidia-smi 命令进行查看

这里我们直接使用一个带 cuda 的镜像来测试,启动该容器并执行nvidia-smi 命令

[root@k8s-worker1 ~]# docker run --rm --gpus all  nvcr.io/nvidia/cuda:11.0.3-runtime-ubuntu20.04 nvidia-smi

picture 22
正常情况下应该是可以打印出容器中的 GPU 信息的。

注: 镜像需与grid驱动版本兼容,否则报错。

6. K8S环境

更进一步,在 k8s 环境中使用 GPU,则需要在集群中部署以下组件:

  • gpu-device-plugin 用于管理 GPU,device-plugin 以 DaemonSet 方式运行到集群各个节点,以感知节点上的 GPU 设备,从而让 k8s 能够对节点上的 GPU 设备进行管理。
  • gpu-exporter:用于监控 GPU

各组件关系如下图所示:

picture 23

左图为手动安装的场景,只需要在集群中安装 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 自行管理。

6.1. 集群环境

picture 24

安装 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

就像这样 picture 25

device-plugin 启动之后,会感知节点上的 GPU 设备并上报给 kubelet,最终由 kubelet 提交到 kube-apiserver。

因此我们可以在 Node 可分配资源中看到 GPU,就像这样: picture 26
可以看到,除了常见的 cpu、memory 之外,还有nvidia.com/gpu, 这个就是 GPU 资源,数量为 0,应该为1,错误原因待查。 picture 27

安装 GPU 监控

除此之外,如果你需要监控集群 GPU 资源使用情况,你可能还需要安装 DCCM exporter 结合 Prometheus 输出 GPU 资源监控信息。

安装略。

小结

本文主要分享了在ECS、Docker 环境、k8s 环境中如何使用 GPU。

  • 对于ECS环境,只需要安装对应的 GPU Driver 即可。

  • 对应 Docker 环境,需要额外安装 nvidia-container-toolkit 并配置 docker 使用 nvidia runtime。

  • 对应 k8s 环境,需要额外安装对应的 device-plugin 使得 kubelet 能够感知到节点上的 GPU 设备,以便 k8s 能够进行 GPU 管理。

现在一般都是在 k8s 环境中使用,为了简化安装步骤, NVIDIA 也提供了 gpu-operator来简化安装部署,后续分享一下如何使用 gpu-operator来快速安装。

7. 参考资料

【01】阿里云GPU服务器安装驱动(完整版)

【02】在GPU计算型实例中手动安装Tesla驱动(Linux)

【03】在GPU虚拟化型实例中安装GRID驱动(Linux)

8. FAQ

8.1. ERROR: Unable to load the kernel module ‘nvidia.ko’.

picture 4
错误详情提示: picture 5

微信扫一扫

作者:mospan
微信关注:墨斯潘園
本文出处:http://mospany.github.io/2024/01/16/gpu-on-k8s/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。