做运维这么多年,我最大的体会是:GPU服务器和普通CPU服务器的运维逻辑,完全是两码事。你可以在传统服务器上靠几条命令和监控大盘混得风生水起,但到了GPU集群面前,如果还是那套“CPU、内存、磁盘”三板斧,大概率会栽跟头——不是AI任务跑不起来,就是GPU利用率低到让人怀疑人生,更别提驱动装不上、显卡突然消失这种能把人逼疯的问题了。
这篇文章我想认真聊一聊GPU运维的基础知识。它不是什么高深的理论课,而是我在实际维护GPU服务器、GPU集群过程中,从零到一积累下来的一套实用方法论。包括硬件层面要看什么、驱动和CUDA环境怎么管、日常巡检该盯哪些指标、多卡调度和资源隔离怎么做、以及遇到问题时怎么一步步排查。无论你是刚接触GPU服务器的新手运维,还是已经被大模型训练任务折磨过几轮的工程师,这篇文章应该都能给你一些可以直接抄作业的经验。
1. 为什么GPU运维和传统服务器运维完全不同
1.1 核心差异:算力资源的可观测性完全不同
传统服务器运维,看的是CPU使用率、内存占用、磁盘IO、网络带宽。这些东西的监控体系非常成熟,指标含义清晰,阈值也好定。但GPU不一样,它的核心资源是显存容量、计算核心利用率、显存带宽、温度与功耗,这意味着你需要一整套全新的监控维度和排查思路。
举个例子。CPU占用率高了,你可以通过top命令快速定位到进程,然后判断是业务负载大还是代码出问题了。但GPU利用率高,不代表你的任务就是健康的——它可能是某个kernel写得很烂导致的无效计算;GPU利用率低,也不代表GPU在偷懒——它可能在频繁等待CPU喂数据,也就是常说的“数据加载瓶颈”。这就是为什么很多AI训练任务在GPU上的利用率上不去,即使GPU、CPU、内存占用都不高,整体训练速度还是卡得要命。
我在维护GPU集群初期就遇到过这种问题:一块V100,显存用了30多G,但GPU利用率一直在2%到5%之间徘徊,看起来GPU根本没在干活。后来排查发现,问题根本不在GPU侧,而是数据加载线程数太少,CPU端从磁盘读图片的速度跟不上GPU的消费速度,GPU空转等数据。这个经历给我一个很重要的教训——GPU运维的视野必须从GPU本身向外延伸,CPU、磁盘、网络、甚至数据加载代码,都会直接影响GPU的工作状态。
1.2 硬件形态:从PCIe卡到整机柜GPU集群
另一个让GPU运维头疼的问题是硬件形态的多样化。家用游戏显卡是一张PCIe卡,插在主板上就能用;数据中心里的GPU服务器则完全不同,常见的形态有这么几种:
- 单卡工作站:一台机器插1到2块GPU,适合模型调试、小规模训练,运维量大但复杂度可控。
- GPU服务器(4U/8U机架式):一台服务器插4到8块GPU,比如常见的DGX系列或者各类国产GPU服务器,需要额外关注供电、散热、PCIe拓扑和NVLink互联。
- GPU集群:多台GPU服务器通过网络(InfiniBand或RoCE)互联组成集群,用于大模型分布式训练,除了单机运维,还要考虑集群调度、任务编排、并行策略、故障域管理等复杂的运维问题。
从单卡到集群,运维的复杂度是指数级上升的。单卡出问题,影响的是一个任务;集群里某块卡出问题,可能导致整个分布式训练任务hang住,所有节点都在等那个“掉队”的卡。
因此,做GPU运维,首先要建立的第一认知就是:你管理的不是一个CPU周边设备,而是一整套独立的、有自己生态的计算系统。把它当成像“一台没有屏幕的计算机”来对待,很多问题的排查思路就会清晰很多。
1.3 CPU、GPU、NPU、VPU、DPU:别再傻傻分不清
在热词里有一串很有代表性的缩写:CPU、GPU、NPU、VPU、DPU。把它们串起来看,其实能很好地理解整个算力阵营的分布:
| 单元 | 全称 | 擅长的任务 | 运维关注点 |
|---|---|---|---|
| CPU | 中央处理器 | 通用计算、逻辑控制、任务调度 | 核心数、主频、缓存、内存通道 |
| GPU | 图形处理器 | 大规模并行计算、图形渲染、AI训练 | 显存、SM数量、利用率、温度、功耗 |
| NPU | 神经网络处理器 | AI推理/训练专用加速 | 算子支持、驱动框架适配、利用率 |
| VPU | 视频处理单元 | 视频编解码 | 编解码通道数、延迟、帧率 |
| DPU | 数据处理单元 | 网络/存储/安全卸载 | 网络吞吐、连接数、卸载能力 |
在运维实践中,我见过很多人把GPU当成一个万能加速器,什么都往上面塞。实际上,适合GPU的是计算密集、并行度高的任务,比如矩阵乘法、卷积操作。如果你拿GPU去做文件处理、字符串匹配这种逻辑型任务,性能反而可能不如CPU。
2. 环境搭建:驱动、CUDA与容器三件套
2.1 驱动安装:不是装上就行,版本匹配才是关键
GPU驱动是整个GPU软件栈的地基。驱动装错了,后面全白搭。
Linux环境下安装NVIDIA驱动的常见方式有两种:
方式一:通过包管理器安装(推荐新手使用)
Ubuntu/Debian系统下可以这样操作:
# 1. 先确认自己的GPU型号 lspci | grep -i nvidia # 2. 安装驱动前先禁用nouveau开源驱动 cat /etc/modprobe.d/blacklist-nouveau.conf echo "blacklist nouveau" >> /etc/modprobe.d/blacklist-nouveau.conf echo "options nouveau modeset=0" >> /etc/modprobe.d/blacklist-nouveau.conf # 3. 更新initramfs并重启 update-initramfs -u reboot # 4. 通过apt安装驱动(以535版本为例) apt install nvidia-driver-535方式二:通过NVIDIA官方runfile安装(推荐进阶用户使用)
# 1. 下载对应型号的驱动 wget https://us.download.nvidia.com/XFree86/Linux-x86_64/535.161.08/NVIDIA-Linux-x86_64-535.161.08.run # 2. 可能需要先装编译依赖 apt install build-essential # 3. 执行安装,--no-opengl-files避免覆盖系统OpenGL库 chmod +x NVIDIA-Linux-x86_64-535.161.08.run ./NVIDIA-Linux-x86_64-535.161.08.run --no-opengl-files安装完成后,用nvidia-smi验证驱动是否正常。如果能看到GPU信息,说明驱动已经搞定了。
驱动版本的选择上,我个人的经验是:不要盲目追新,也不要使用太老的版本。NVIDIA驱动有长期支持分支(比如470、535系列),生产环境建议选择这类稳定分支。另外,一定要把驱动的版本号记清楚,因为后续安装CUDA时,CUDA要求的最低驱动版本是硬门槛。
注意:如果服务器是带GPU直通的虚拟化环境,比如VMware ESXi或者KVM,驱动安装过程和物理机很不一样,需要宿主机和虚拟机两端配合处理,这里面的坑比物理机多得多,建议单独实验验证后再上生产。
2.2 CUDA与cuDNN:版本治理是门学问
驱动装好后,接下来就是CUDA。很多人把CUDA和驱动混为一谈,其实是两个不同的东西:驱动是操作系统和GPU硬件之间的桥梁,CUDA是让开发者可以利用GPU做通用计算的软件开发平台。
CUDA的安装方式也有几种:
方法一:使用conda安装(最省事,适合日常开发)
# 自动安装对应版本的cuda toolkit和cudnn conda install cudatoolkit=11.8 cudnn=8.6.0 -c conda-forge方法二:使用系统级安装包(适合生产环境)
# 以CUDA 11.8为例 wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run ./cuda_11.8.0_520.61.05_linux.run --toolkit --samples --silent # 配置环境变量 echo 'export PATH=/usr/local/cuda-11.8/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc关于版本兼容,我这里直接给出最实用的建议:
- CUDA版本和驱动版本的关系:CUDA要求驱动版本不低于某个阈值。例如CUDA 11.8要求驱动版本不低于520.61。装完CUDA后可以用
nvidia-smi右上角看到“CUDA Version”,这个值表示当前驱动支持的最高CUDA版本,只要你的CUDA toolkit版本不高于这个数字,就能正常工作。 - cuDNN版本和CUDA版本的关系:cuDNN是针对深度学习场景优化过的深度神经网络加速库,它和CUDA有严格的版本对应关系,不能随意搭配。
所以我在实际运维中的做法是:在服务器上维护一张“环境兼容性表”,记录每台机器上驱动、CUDA、cuDNN的版本组合,以及这个环境跑过的任务和踩过的坑。这个东西在后续处理问题时会特别有用。
2.3 容器化GPU环境:Docker与nvidia-container-toolkit
现在的AI训练环境,几乎都是容器化部署。原因很简单:不同项目对CUDA版本的要求可能完全不同,直接在物理机上切换CUDA版本纯属自虐。而容器化之后,每个容器可以有自己独立的CUDA环境,互不干扰。
先看传统方式如何在Docker里调用GPU:
# 这是老式的写法,仅支持NVIDIA GPU docker run --runtime=nvidia --rm nvidia/cuda:11.8.0-base nvidia-smi后来NVIDIA官方推荐使用nvidia-container-toolkit:
# 安装nvidia-container-toolkit distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add - curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list > /etc/apt/sources.list.d/nvidia-docker.list apt update apt install -y nvidia-container-toolkit # 重启docker服务 systemctl restart docker # 使用--gpus参数挂载GPU docker run --rm --gpus all nvidia/cuda:11.8.0-base nvidia-smi容器化之后,GPU运维的方式也发生了变化。以前排查问题要登录到物理机上敲命令,现在更多是在容器层面操作。这里有一个很重要的经验:容器内看到的GPU信息和物理机上是一致的,但权限隔离要特别注意。我在实践中遇到过容器内的进程占满显存,导致其他容器无法分配内存的情况。所以,如果多个团队共用一台GPU服务器,强烈建议在容器层面做资源限制:
# 限制容器最多使用2块GPU和16G显存 docker run --rm --gpus '"device=0,1","capabilities=compute,utility"' \ --shm-size=16g -m 32g \ your-training-image python train.py--shm-size这个参数容易被忽略,但它很关键。PyTorch的DataLoader在多进程模式下会使用共享内存来传递数据,默认的shm大小只有64MB,数据量一大就会报“No space left on device”,不是磁盘没空间,而是共享内存不够。
3. 日常巡检与监控:别只盯着利用率
3.1 nvidia-smi逐项解读:每一列都有意义
nvidia-smi是GPU运维里最基础、最核心的命令,没有之一。很多新手只会看“GPU-Util”那一列,实际上这个命令输出的信息量非常大。我逐项拆解一下:
+-----------------------------------------------------------------------------+ | 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 NVIDIA A100-PCIE-40GB On | 00000000:3B:00.0 Off | On | | 30% 52C P0 120W / 250W | 35250MiB / 40960MiB | 96% Default | | | | Disabled | +-----------------------------+----------------------+----------------------+运维时需要重点关注的信息:
Driver Version和CUDA Version:这是排查环境问题时最先需要确认的信息。CUDA Version显示的是驱动支持的最高CUDA版本,不是当前实际使用的版本,这里经常有人看错。Temp:GPU核心温度,A100/H100这类数据中心GPU的满载温度一般在70到85摄氏度之间。超过90度就要警惕散热问题,超过95度基本可以断定散热或者风道有问题了。Perf:电源性能状态,从P0到P12,P0是最高性能状态。如果发现GPU在负载很高时Perf状态却是P2或者更低,说明GPU可能因为温度、功耗限制而降频了。Pwr:Usage/Cap:当前功耗和最大功耗的对比,这个指标能直观反映GPU是否真正跑满了。有些卡利用率看着是90%多,但功耗只有一半,说明kernel的负载并没有真正压满计算单元。Memory-Usage:显存使用情况。显存溢出(OOM)是最常见的GPU问题之一,后面故障排查部分会专门讲。GPU-Util:GPU计算单元利用率。这是大家最关注的指标,但也是最容易误判的指标,后面章节展开说。Volatile Uncorr. ECC:不可纠正的ECC错误数量。这个值如果出现非零,说明GPU显存可能有硬件故障,需要重点关注。对于A100/H100这类数据中心卡,ECC错误是硬件健康的重要预警信号。
3.2 监控指标体系:利用率之外还要看什么
在实际运维中,我建议至少监控GPU的五个维度:
第一维度:计算利用率
这里要区分GPU-Util和SM占用率的区别。nvidia-smi里的GPU-Util是一个采样值,它反映的是采样周期内GPU是否有kernel在执行。如果kernel执行得很密集,利用率可能是90%以上,但如果kernel执行之间有大量空隙,利用率就会呈现锯齿状波动。
第二维度:显存使用率
显存使用率不仅影响任务能否跑起来,还影响性能。因为显存带宽是固定资源,使用率过高可能引发内存访问冲突,过多进程共享显存更是会影响各自的数据局部性。
第三维度:温度与功耗
这两个指标直接决定了GPU能否持续满血运行。机房散热不良是最常见的问题,我见过一块跑训练任务的A100因为机房空调故障,温度飙到92度,功耗直接被限制在额定功耗的60%左右,训练速度肉眼可见地下降。
第四维度:PCIe/NVLink带宽
nvidia-smi也提供了一些链路信息,比如:
# 查看GPU拓扑结构,可以看到NVLink连接情况 nvidia-smi topo -m # 查看NVLink带宽 nvidia-smi nvlink -s在分布式训练场景下,多GPU之间的通信带宽至关重要。NVLink如果出现降速或者断链,会导致训练任务整体变慢,而且这种问题不容易在常规监控指标里体现出来。
第五维度:ECC错误与Xid错误
ECC错误会在nvidia-smi里显示,Xid错误则会输出到系统日志中。这类错误一旦出现,基本上预示GPU硬件出了问题,需要提前准备更换或RMA。
3.3 GPU压力测试:用gpu-burn给新卡“上强度”
新到的GPU服务器,或者怀疑GPU有硬件问题时,最直接的办法就是做压力测试。目前用的比较多的是gpu-burn这个工具。
# 安装gpu-burn git clone https://github.com/wilicc/gpu-burn cd gpu-burn make # 对全卡跑压力测试,测试时间可以自定义,单位是秒 ./gpu_burn 300 # 也可以指定GPU CUDA_VISIBLE_DEVICES=0,1 ./gpu_burn 300gpu-burn的测试逻辑是通过持续执行CUDA计算kernel,让GPU在满载状态下运行。跑完后重点关注几个地方:
- 温度曲线:满载运行温度应该稳定在一个合理区间,比如A100大约在80度上下。如果温度一直往上冲,说明散热系统有问题。
- 功耗稳定性:满载功耗应该稳定在额定功耗附近,比如250W的卡应该稳定在240W以上。如果功耗明显偏低或波动大,可能是供电或固件问题。
- 是否报错:如果运行过程中出现CUDA错误、驱动报错,甚至机器直接hang住重启,那基本可以判定硬件或驱动有问题。
我实践中新到一批卡,都会跑至少30分钟的压力测试,同时记录每块卡的稳定温度、功耗和峰值频率,这些数据后续可以用来对比,一旦后续性能下降,可以快速定位是老化还是故障。
4. GPU集群:多卡调度、资源隔离与任务管理
4.1 单机多卡:用CUDA_VISIBLE_DEVICES来控制
单机多卡的情况下,最常见的需求就是怎么让任务使用指定的GPU。
# 只使用第2号GPU CUDA_VISIBLE_DEVICES=2 python train.py # 使用第0和第3号GPU CUDA_VISIBLE_DEVICES=0,3 python train.pyCUDA_VISIBLE_DEVICES的原理是在CUDA层面屏蔽掉其他GPU设备,程序里看到的GPU编号会被重新映射。比如你设置CUDA_VISIBLE_DEVICES=2,3,程序里看到的device 0实际对应物理GPU 2,device 1对应物理GPU 3。
这个机制在运维中很有用,可以用它来做简单的资源隔离:
# 用户A的GPU任务 CUDA_VISIBLE_DEVICES=0,1 python train.py # 用户B的GPU任务 CUDA_VISIBLE_DEVICES=2,3 python train.py不过u200b单卡运维这种方式的隔离能力很弱——它只管显存和计算资源的一部分,但不限制显存上限。如果用户A的任务写得很糙,把0号卡的显存全占了,用户B在2号卡上跑任务不会直接受到影响,但如果用户A在任务里显式指定了cuda:2之类的设备编号而没有设置CUDA_VISIBLE_DEVICES,就很容易发生跨设备冲突。
4.2 多机集群:从裸机调度到K8s
多机GPU集群是真正考验运维能力的地方。我在实践中见过两种主流方案:
方案一:Python生态的调度方案
PyTorch等框架自带分布式训练能力,配合ssh可以直接在多机之间拉起任务:
# 以PyTorch分布式训练为例,需要在所有节点上准备好环境 python -m torch.distributed.launch --nproc_per_node=8 \ --nnodes=2 --node_rank=0 --master_addr=192.168.1.10 \ --master_port=29500 train.py这种方案适合小规模集群,几台机器、十几个用户,手动管理问题不大。但一旦规模上来,手动方式完全不可行,原因有几点:
- GPU资源分配容易冲突,用户A和用户B同时看中了一块卡。
- 任务异常退出后,显存不能及时释放,导致后面的任务无法启动。
- 没有排队机制,多个用户提交任务时只能靠协商。
方案二:调度系统方案
生产环境更推荐使用专业的集群调度器。目前业界用得比较多的是Slurm和Kubernetes。
Slurm的结构比较清晰:控制节点(controller)负责调度,计算节点(node)提供资源,用户通过srun或sbatch提交任务。它天然支持GPU资源管理。
Kubernetes管理GPU的方式则有所不同,主要通过Device Plugin机制:
# 以NVIDIA官方device plugin为例 kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/nvidia-device-plugin.yml部署完成后,K8s节点上就能看到nvidia.com/gpu这种扩展资源:
kubectl describe node gpu-node-01 | grep -A 5 "nvidia.com/gpu"在Pod中申请GPU的写法是这样的:
apiVersion: v1 kind: Pod metadata: name: gpu-pod spec: containers: - name: gpu-container image: nvidia/cuda:11.8.0-base resources: limits: nvidia.com/gpu: 1这里有一个关键细节:K8s的Device Plugin模型下,GPU是以“整数设备”为单位进行调度的,不能申请“半块卡”。如果你需要更细粒度的显存隔离,目前常见的思路有:
- MIG(Multi-Instance GPU):A100/H100等硬件支持的GPU实例切分技术,可以把一块物理GPU切成多个不同规格的实例,每个实例拥有独立的显存和计算单元。
- vGPU(虚拟GPU):需要配合虚拟化平台使用,主要用于虚拟桌面场景。
- 显存配额型调度:一些开源的方案(如HAMi)可以在K8s上实现显存级别的资源隔离。
4.3 任务排队与资源规划:别让GPU闲着
GPU集群运维里最容易出现的问题是资源碎片化——总共100块卡,有90块已经被占用了,剩下的10块分散在不同机器上,每个用户的任务都需要8块卡,结果谁都跑不了。
这个问题在K8s这套体系里尤其常见,因为K8s默认的调度器只认“单个节点上是否有足够的GPU”,不会自动把一个任务的多块GPU分配到不同节点上。对于需要多机多卡的任务,通常要配合框架级别的分布式训练能力,先由调度系统把多个Pod调度到不同节点,再让它们相互通信组成一个训练集群。
从运维角度,我会给每个提交GPU任务的团队定一个“资源规划表”,提前预估任务的显存需求和卡数需求。比如:
- 微调7B参数的大模型,全参数微调大约需要显存60到80GB,A100 40G需要2卡,或者A100 80G单卡+梯度累积。
- 推理7B模型,fp16精度下大约需要14GB显存,单张A10(24G)就够用。
- 微调13B模型,全参数微调需要超过120GB显存,至少需要2张A100 80G。
如果任务需求超出单卡显存,优先用gradient_checkpointing、梯度累积这类技术降低显存需求,不要一开始就上多卡。多卡训练带来的通信开销、同步开销,在小规模模型上是得不偿失的。
4.4 GPU租用:云上GPU运维的思路转变
现在很多团队倾向于云上租GPU,比如按小时租用GPU实例来跑训练任务。云上GPU运维的核心逻辑和自建机房有一个很大的不同:你不关心底层硬件,但必须关心实例的生命周期管理。
租用GPU实例时有几个容易踩的坑:
- 实例释放后数据丢失:很多GPU实例是临时型的,停止或释放后本地数据会被清空。一定要把训练代码和数据放到云盘或对象存储上。
- 镜像环境不统一:不同时间创建的GPU实例,可能使用不同版本的驱动和CUDA环境,导致训练结果或性能有差异。强烈建议使用自建镜像,固定依赖版本。
- 竞价实例被回收:很多云厂商提供价格更低的竞价实例,但这些实例可能在短时间内被回收,不适合长时间训练任务。
我建议的做法是:所有GPU任务都通过容器镜像的方式在云上运行,这样本地开发和云端部署是完全一致的环境,实例到期了随时销毁重建,不会产生环境差异导致的问题。
5. GPU故障排查实战:那些年我们踩过的坑
5.1 高频问题速查表
GPU运维的问题千奇百怪,但高频问题基本都能归纳到下面几类。我整理了一个速查表,方便大家遇到问题时先对号入座:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| nvidia-smi不显示GPU | 驱动未安装/驱动崩溃 | dmesg | grep -i nvidia | 重装驱动,检查内核模块 |
| nvidia-smi报错“Failed to initialize NVML” | 驱动与CUDA不匹配 | modinfo nvidia | grep version | 升级或降级驱动 |
| PyTorch报CUDA OOM | 显存被其他进程占用 | nvidia-smi查看显存占用 | 按需释放,或用CUDA_VISIBLE_DEVICES隔离 |
| GPU利用率低 | 数据加载慢/CPU瓶颈/小batch | 查看CPU和磁盘IO | 增加DataLoader线程数、加大batch size |
| 训练中途卡死 | 分布式通信超时/卡异常 | 检查NCCL日志/系统日志 | 检查网络、NVLink状态、ECC错误 |
| GPU温度过高 | 散热不良/风道堵塞 | nvidia-smi -q -d TEMPERATURE | 清理灰尘,调整机房空调 |
| GPU功耗低但利用率高 | 降频/供电不足 | 检查Perf状态与Pwr | 检查电源和固件设置 |
| Xid错误 | 显存故障/GPU硬件问题 | dmesg | grep Xid | 查询Xid错误码,必要时返修 |
5.2 典型问题处理实录
案例一:GPU利用率上不去,但CPU和内存占用都不高
这是热词里提到“GPU CPU 内存占用都不高但卡”的情况。我遇到的一个真实案例是:训练一个图片分类模型,GPU利用率始终只有10%左右,显存也没用满,CPU使用率也很低,磁盘IO也很低,但训练速度就是很慢。
排查过程:
- 先用
nvidia-smi确认GPU确实在跑计算,从GPU-Util看确实有kernel在执行,但间隔很大。 - 用
nvidia-smi --query-gpu=utilization.gpu,utilization.memory --format=csv看细粒度指标,发现GPU的计算利用率和显存利用率都很低。 - 在训练代码中统计每个step的耗时,发现大部分时间花在数据加载上(DataLoader的worker在等数据)。
- 进一步排查发现,数据存储在机械硬盘上,读取速度太慢,而且单张图片的处理逻辑比较复杂,CPU预处理成为瓶颈。
解决方案:
# 增加DataLoader的worker数量 train_loader = DataLoader(dataset, batch_size=128, shuffle=True, num_workers=8, # 原来为2 persistent_workers=True) # 复用worker进程 # 开启prefetch train_loader = DataLoader(dataset, batch_size=128, shuffle=True, num_workers=8, prefetch_factor=4)同时把数据迁移到SSD上,之后GPU利用率从10%提升到85%以上。这个案例说明,GPU利用率低的时候,不要急着怀疑GPU坏了,先看数据链路是不是喂得饱GPU。
案例二:容器内显存冲突导致训练任务互相干扰
有一次我们的训练平台上两个任务同时跑,任务A在0号卡上,任务B在1号卡上,但任务A突然报显存不足。后来排查发现,任务A虽然没有设置CUDA_VISIBLE_DEVICES,但它内部有一行代码使用了torch.cuda.set_device(0),然后程序里误用了全部GPU的索引,把1号卡的显存也申请了。
这种问题的根因是任务没有明确指定可见GPU。解决方法是统一要求所有训练任务都必须通过环境变量指定GPU:
# 在启动脚本中强制指定,防止应用内部越界使用GPU export CUDA_DEVICE_ORDER=PCI_BUS_ID export CUDA_VISIBLE_DEVICES=0同时,在容器层面通过--gpus '"device=0"'做硬限制,双保险。这里也推荐使用nvidia-smi --query-compute-apps=pid,used_memory --format=csv定期检查每个进程的显存占用情况,防止越界。
案例三:分布式训练任务偶发卡死
这个问题的排查难度比较大。现象是:8卡训练任务,跑到第2000步左右突然卡住不动,等了很久也没有报错,日志停在一个step的输出上。
排查过程:
- 先看所有节点上的GPU利用率,发现有的卡利用率是100%,有的卡利用率是0%,说明节点间的步调不一致了。
- 查看系统日志
dmesg,发现有一条Xid错误记录,指向第5块GPU。 - 用
nvidia-smi -q -d ECC检查ECC错误计数,发现该卡的Volatile Uncorr. ECC计数非零。 - 最终定位为GPU显存出现单比特翻转,导致NCCL通信数据出错,训练hang住。
解决方案:更换故障GPU,之后训练恢复正常。
这个案例教会我的经验是:分布式训练任务卡死的第一时间,不要急于重启重跑,先检查所有GPU的ECC错误和Xid错误。如果是硬件问题,重启多少次都没用,只会反复消耗时间。如果确认是硬件故障,及时更换或将该卡从资源池中剔除。
5.3 GPU驱动开发相关:运维需要知道多少
热词里还出现了“GPU驱动开发”和“昇腾系列有哪些GPU”这样的词。虽然在一线运维中很少需要去写GPU驱动,但了解一些GPU驱动的内部机制,对排查问题很有帮助。
比如,为什么GPU驱动有内核态和用户态之分?简单理解,内核态驱动负责和硬件直接交互,用户态驱动则给开发者提供API接口。NVIDIA驱动的运行逻辑是:用户态的CUDA库调用内核态的驱动模块,再由驱动模块控制GPU硬件执行计算。
遇到GPU挂了,常规操作是查看dmesg:
dmesg | tail -50如果看到NVRM: Xid (PCI:0000:3b:00.0): 79这样的日志,就可以根据错误码去NVIDIA官方文档查对应的故障类型。例如Xid 79是GPU已经离开总线,通常意味着GPU设备消失了;Xid 13是图形引擎异常;Xid 48是显存ECC错误。这些错误码直接指向硬件问题,可以大幅缩短排查时间。
昇腾系列目前在建设国产算力集群时用得越来越多,作为运维人员也需要了解这类设备的运维。昇腾GPU的驱动和CANN工具链与NVIDIA的CUDA体系不兼容,监控命令也完全不同,需要在项目建设时重新学习和适配。如果团队同时管理NVIDIA和昇腾两套集群,建议维护两套独立的监控平台和运维手册,不要混在一起管理。
6. 关于GPU运维的一些长期建议
最后想分享几点我做了这几年GPU运维之后总结出来的长期建议。
第一,建立完整的资产台账。每块GPU的序列号、所在的物理位置、所属服务器、驱动版本、固件版本、首次上架日期、历史故障记录,都要登记清楚。尤其是集群规模大了以后,这不仅仅是资产管理,更重要的是硬件生命周期的预判和管理。
第二,自动化巡检脚本要尽早落地。人工盯着nvidia-smi看是不现实的。建议把巡检脚本化,定期收集GPU的关键指标,写入时序数据库,配合告警规则。比如温度超过85度、ECC错误非零、显存占用率持续100%、GPU利用率持续0%但进程还在跑,这些都应该触发告警。热词里的“运维效率工具”“系统运维工具”就是这类场景的落地产物。
第三,权限管控要前置。GPU服务器上跑的往往是研发人员的训练任务,不要在root权限上太随意,但也别因为权限管控过严导致研发效率大幅下降。比较好的实践是:运维管底层环境(驱动、容器运行时、网络),研发管自己的容器镜像和训练代码,双方职责清晰,互不打扰。
第四,故障预案一定要试跑。很多团队写了故障预案但不演练,真出问题的时候发现预案完全不可操作。建议定期做一次“杀卡”演练——随机挑一台GPU服务器,模拟显卡故障,让负责的同事按照预案走一遍处理流程。这个过程能暴露出很多预案里没考虑到的细节问题。
第五,日志和报错信息是最大财富。很多GPU问题不是第一次发生,只是没人记录。我建议团队维护一个“GPU问题知识库”,每次排查完问题,把现象、排查路径、根因、解决方案写下来。几个月后你就会发现,大部分问题都能在知识库里找到答案,处理效率翻倍。
GPU运维这个方向,在AI全面落地的这几年变得越来越核心。起码在可以预见的未来,大模型训练、推理、科学计算,都离不开GPU算力底座。希望这篇文章能帮刚入门的朋友少踩一些我当年踩过的坑,也欢迎有经验的老手在评论区分享你们遇到的奇葩GPU问题——互相交流,总比一个人埋头摸索强。