AI 时代的 OS 适配 —— GPU 调度、NUMA 与大模型训练

服务器 OS 过去 20 年都是为 CPU + 网卡 + 磁盘设计的。AI 时代加进来了 GPU、NPU、HBM、NVLink 这些新东西——OS 需要适配。本文从 GPU 调度讲到大模型训练 OS 优化。

AI 时代 OS 的新挑战

graph TB
  TRAD[传统服务器 OS]
  TRAD --> T1[CPU 调度]
  TRAD --> T2[内存管理]
  TRAD --> T3[文件 / 网络 IO]
  
  AI[AI 时代追加]
  AI --> A1[GPU / NPU 调度]
  AI --> A2[HBM 内存管理]
  AI --> A3[GPUDirect / NVLink]
  AI --> A4[NUMA + GPU 拓扑]
  AI --> A5[大量 IO(数据集 / checkpoint)]
  AI --> A6[超大进程地址空间]

GPU 设备文件

NVIDIA GPU 在 Linux 下表现为字符设备:

1
2
3
4
5
6
7
8
9
10
11
12
ls -la /dev/nvidia*
# /dev/nvidia0
# /dev/nvidia1
# /dev/nvidiactl
# /dev/nvidia-uvm
# /dev/nvidia-uvm-tools
# /dev/nvidia-modeset

# 设备权限和容器:
ls /dev/dri/
# /dev/dri/card0 # DRM 设备
# /dev/dri/renderD128 # 渲染节点

容器里要把这些设备 mount 进去——nvidia-container-toolkit 自动做这件事:

1
2
3
4
5
6
7
8
9
10
11
12
# Docker 用
docker run --gpus all nvidia/cuda:12.4-base nvidia-smi
docker run --gpus '"device=0,1"' ...

# K8s 用 nvidia-device-plugin
kubectl describe node <node>
# Allocatable: nvidia.com/gpu: 8

# Pod 申请
resources:
limits:
nvidia.com/gpu: 1

GPU 资源在 K8s 里的模型

graph TB
  PLUGIN[nvidia-device-plugin
DaemonSet] PLUGIN --> EXPOSE[暴露 nvidia.com/gpu 资源] EXPOSE --> SCHED[K8s Scheduler
按 GPU 数量调度] SCHED --> POD[Pod 申请 GPU] POD --> RUNTIME[nvidia-container-runtime
映射设备]

这个模型有几个根本问题

1
2
3
4
5
6
7
8
9
1. 整数粒度:     一个 Pod 只能要 1/2/4/8 个 GPU
→ 小模型推理浪费 GPU 资源

2. 单 GPU 不可分享:
→ 一个 Pod 占整张卡

3. 没有拓扑感知:
→ 8 GPU Pod 可能跨 NUMA
→ NCCL 性能受影响

GPU 资源细分:MIG / vGPU / Time-Slicing

第 7.2 已经讲过——这里讲 K8s 层适配:

graph TB
  GPU[GPU 资源细分]
  GPU --> MIG[NVIDIA MIG
硬件级] GPU --> VGPU[NVIDIA vGPU
软件许可] GPU --> TS[Time-Slicing
K8s 软件] GPU --> MPS[CUDA MPS
进程级]

MIG(A100/H100)

1
2
3
4
5
6
7
8
# 启用 MIG
nvidia-smi -i 0 -mig 1
nvidia-smi mig -cgi 9,9,9 # 创建 3 个 1g.10gb

# K8s 看到
nvidia.com/mig-1g.10gb
nvidia.com/mig-2g.20gb
nvidia.com/mig-3g.40gb

MIG 后每个实例独立显存、SM、L2 缓存——真正的硬件隔离

Time-Slicing

1
2
3
4
5
6
7
8
9
nvidia-device-plugin 配置:
sharing:
timeSlicing:
replicas: 4 # 每张 GPU 切成 4 份

K8s 看到: 8 GPU 节点 → 32 个 nvidia.com/gpu

适合: 推理 / 开发
不适合: 训练(互相抢,性能不可预测)

CUDA MPS(Multi-Process Service)

1
2
3
4
5
MPS:     多个 CUDA 进程共享一张 GPU
启用: nvidia-cuda-mps-control -d
应用: HPC 多进程任务

K8s 集成: 部分插件支持

NVIDIA GPU Operator

K8s 上 GPU 全栈管理:

1
2
3
4
5
6
7
nvidia-gpu-operator 包含:     
- Driver Container(容器化驱动)
- Device Plugin
- DCGM Exporter(监控)
- GPU Feature Discovery(标签)
- MIG Manager
- Network Operator(GPUDirect)

一键部署整个 GPU stack——但有性能开销和 license 复杂度

NUMA-aware 调度:GPU 拓扑

8 GPU 服务器 GPU ↔ CPU 的拓扑:

graph TB
  CPU0[CPU Socket 0
NUMA 0] --- PCIe0[PCIe Switch 0] CPU1[CPU Socket 1
NUMA 1] --- PCIe1[PCIe Switch 1] PCIe0 --- G0[GPU 0] & G1[GPU 1] & G2[GPU 2] & G3[GPU 3] PCIe1 --- G4[GPU 4] & G5[GPU 5] & G6[GPU 6] & G7[GPU 7] G0 ---|NVLink| G4 G1 ---|NVLink| G5 ...
1
2
3
4
5
6
7
8
9
10
# 看 GPU NUMA 关联
nvidia-smi topo -m

# 输出矩阵:
# X = self
# NV1-18 = NVLink 链路数
# PXB = PCIe Switch
# PIX = PCIe Adjacent
# SYS = 跨 NUMA / 跨 CPU
# NODE = 同 NUMA

调度的智慧:

1
2
3
- DataLoader 进程绑到 GPU 同侧 NUMA 的 CPU 上
- 内存分配也要 NUMA-bind
- 否则 PCIe 跨 socket 性能崩 30-50%

CPU pinning for AI

1
2
3
4
5
6
7
8
9
# PyTorch DataLoader
import os
import torch

# DataLoader workers 绑核
os.sched_setaffinity(0, {0, 1, 2, 3, 4, 5, 6, 7})

# 也可以用 numactl
# numactl --cpunodebind=0 --membind=0 python train.py
1
2
3
4
5
# 容器层面 NUMA 绑定
docker run --cpuset-cpus=0-15 --cpuset-mems=0 ...

# K8s 通过 Topology Manager
kubelet --topology-manager-policy=single-numa-node

K8s 1.18+ 支持 Topology Manager——同时考虑 CPU + GPU + 网卡的 NUMA 亲和。

GPUDirect 在 OS 层

GPUDirect RDMA 让网卡直接读写 GPU 显存——OS 要做的事:

1
2
3
4
5
6
7
8
9
10
11
12
1. peer_mem 模块加载:     
modprobe nvidia_peermem

2. GPU BAR 暴露给 PCIe peer-to-peer:
# nvidia-smi -q | grep -i bar

3. PCIe ACS 关闭:
# 否则 ACS 阻断 P2P
setpci -s <BDF> ECAP_ACS+0x6.w=0:1f0

4. IOMMU 设置:
intel_iommu=on iommu=pt # passthrough 模式

GPUDirect Storage(GDS)

GPU 直接读 NVMe:

1
2
3
4
5
6
7
8
9
10
GDS:     
- cuFile API
- 绕过 CPU 内存
- 大数据集训练加速
- PyTorch / NVIDIA DALI 支持

要求:
- NVIDIA MOFED 驱动
- GPU + NVMe 在同一 PCIe Switch(理想)
- 内核 5.x+

OS 对 HBM 的态度

GPU 显存(HBM)是不归 Linux 内核管的

1
2
3
4
5
6
7
8
9
10
Linux 看到:     主机 DRAM
GPU 显存: 由 NVIDIA driver / CUDA Runtime 管
- cudaMalloc
- 不在 vmstat / free 里显示
- nvidia-smi 看

Unified Memory(CUDA UM):
- 假装 CPU/GPU 共用内存
- 实际是 driver 在背后搬数据
- 大模型训练用得少(性能不可控)

CXL 时代会改变这个——未来 GPU 显存可能”挂到 CPU 主存空间”

1
2
3
4
5
6
7
CXL Type 3 远端内存设备:     
- 暴露成普通 NUMA 节点
- Linux 直接 mmap

CXL 上挂 HBM:
- 实验性,B200 后可能
- 会改变 OS 对显存的认知

大文件 / 大内存:调优

大模型训练对 OS 的特殊压力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
模型权重文件:     
Llama 70B FP16 = 140 GB
GPT-3 175B = 350 GB
→ 单文件超过 100 GB 是常态

ext4 / XFS 都没问题,但读写要:
- 大块 IO(≥ 1 MB)
- O_DIRECT 跳过 page cache
- 多线程并行

进程地址空间:
PyTorch 训练进程: 1-2 TB 虚拟内存
Linux 默认 64 TB 上限够用

mmap 支持:
CUDA Runtime 用 mmap 管 page-locked memory
vm.max_map_count 要调大:
sysctl vm.max_map_count=1048576

训练专用的 K8s 调度

1
2
3
4
5
6
7
8
9
10
普通 K8s scheduler 缺陷:     
- 不懂 gang scheduling(几十个 Pod 要"全部启动 OR 不启动")
- 不懂拓扑(同 Pod 的 GPU 应该 NVLink 互联)
- 不懂训练队列优先级

AI 专用调度器:
Volcano(华为 + 社区): gang scheduling
Yunikorn: Apache 的多租户
KAI Scheduler(NVIDIA Run.ai): GPU 池化
Kueue(K8s 官方): Job Queueing

Gang Scheduling

1
2
3
4
5
6
7
8
9
10
11
12
传统 K8s:     一次调一个 Pod
Gang: 一组 Pod 必须同时调度

应用:
- 分布式训练(128 个 worker 必须一起启)
- MPI 作业
- RAY / Dask 集群

不 gang scheduling 时:
- 部分 Pod 已启动,等其他
- 占资源不释放
- 集群死锁

训练相关的内核参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 共享内存(PyTorch DataLoader 用)
sysctl kernel.shmmax=1099511627776 # 1 TB
echo "16G" > /sys/fs/cgroup/<group>/shm.size

# 文件描述符
ulimit -n 1048576
echo "* soft nofile 1048576" >> /etc/security/limits.conf
echo "* hard nofile 1048576" >> /etc/security/limits.conf

# 进程数
ulimit -u unlimited
echo "* soft nproc unlimited" >> /etc/security/limits.conf

# 内存锁定(GPUDirect 需要)
ulimit -l unlimited
echo "* soft memlock unlimited" >> /etc/security/limits.conf
echo "* hard memlock unlimited" >> /etc/security/limits.conf

# 大页
vm.nr_hugepages = 8192

# 网络(NCCL / IB)
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.core.netdev_max_backlog = 30000

NPU 调度:昇腾 / 寒武纪等

国产 NPU 的 OS 适配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
华为昇腾(Ascend):     
- /dev/davinci0 ~ N
- npu-smi 工具
- K8s ascend-device-plugin
- resources.limits: huawei.com/Ascend910

寒武纪(Cambricon):
- /dev/cambricon_dev*
- cnmon 工具
- K8s cambricon-device-plugin
- resources.limits: cambricon.com/mlu

摩尔线程:
- /dev/mtgpu*
- mthreads-gmi
- K8s mthreads-device-plugin

各家 device-plugin 仿照 NVIDIA 风格,但生态成熟度差异大。

checkpoint / 断点续训

大模型训练常见操作——对 OS IO 子系统的考验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
checkpoint 写入:     
- 70B FP16 模型: ~140 GB
- 训练优化器状态: 280-400 GB
- 激活值 / 元数据: 几十 GB
- 总: 500 GB - 1 TB / checkpoint
- 频率: 每 1-2 小时一次

要求:
- 高速文件系统: Lustre / WekaFS / GPFS
- 网络足够(万卡级用 InfiniBand 写)
- 所有 GPU 同时写 → 集合 IO

故障恢复:
- 节点挂了 → 训练框架自动从最近 checkpoint 恢复
- PyTorch FSDP / DeepSpeed 都有此能力
- SLA: 一次故障 < 30 分钟恢复

监控:DCGM / Prometheus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DCGM(Data Center GPU Manager):     
- NVIDIA 官方 GPU 监控
- dcgm-exporter 暴露 Prometheus 格式
- 关键指标:
GPU 利用率、显存、温度、功耗、ECC 错误

Prometheus + Grafana:
- dcgm-exporter
- node-exporter
- kube-state-metrics
- 一图看全集群

DCGM 监控万卡集群:
- 每秒几万 metric
- 异常自动告警
- 故障节点自动隔离

容器与 GPU 的几个老坑

坑 1:容器里看不到 nvidia-smi

1
2
3
4
5
6
7
# 没装 nvidia-container-toolkit
apt install nvidia-container-toolkit
# 或 RHEL:
dnf install nvidia-container-toolkit

# 重启 Docker
systemctl restart docker

坑 2:CUDA 版本不匹配

1
2
3
4
5
Host driver 版本必须 ≥ Container 内 CUDA 版本
查看: nvidia-smi(看左上角 CUDA Version)

Host driver 535 → 兼容 CUDA 12.x
Host driver 470 → 仅兼容 CUDA 11.x

坑 3:MIG 实例不可见

1
2
3
4
5
MIG 启用后,容器要明确请求 MIG 实例:
resources.limits:
nvidia.com/mig-1g.10gb: 1

不能再用 nvidia.com/gpu: 1(除非禁 MIG)

坑 4:训练 Pod OOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
显存 OOM:     batch size / sequence length 太大
主机内存 OOM: DataLoader workers 太多
SHM 不够: /dev/shm 默认 64 MB,DataLoader 经常需要更大

K8s 调大 SHM:
spec:
containers:
- volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 16Gi

坑 5:跨 NUMA 性能损失

1
2
3
4
5
6
7
8
8 GPU Pod 没有 numa-aware 调度:     
- 跨 socket PCIe 通信
- DataLoader CPU 在另一个 NUMA
- 实测性能损失 20-40%

解决:
- 启用 K8s Topology Manager
- 训练框架显式 numactl

坑 6:NCCL 启动失败

1
2
3
4
5
6
7
症状:     "NCCL WARN Failed to open socket"
常见原因:
- SHM 太小
- ulimit -l(memlock)太小
- IB 设备不可见
- PCIe ACS 没关
- Container 没 IPC=host

坑 7:GPU 假性死机

1
2
3
4
5
GPU "stuck" 但 nvidia-smi 还能查:     
- dmesg | grep "Xid" → 看 NVIDIA Xid 错误
- Xid 13/31/63/79:通常硬件 / 散热问题
- 重启 GPU:nvidia-smi --gpu-reset -i <id>
- 持续: 更换 GPU

大模型训练时 OS 实战清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# /etc/sysctl.d/99-ai-training.conf
fs.file-max = 2097152
fs.aio-max-nr = 1048576
kernel.shmmax = 1099511627776
kernel.shmall = 268435456
vm.max_map_count = 1048576
vm.swappiness = 10
vm.dirty_ratio = 5
vm.dirty_background_ratio = 2
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.core.netdev_max_backlog = 30000

# /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
* soft nproc unlimited
* hard nproc unlimited
* soft memlock unlimited
* hard memlock unlimited

# 启动参数(GRUB)
intel_iommu=on iommu=pt
default_hugepagesz=2M hugepagesz=2M hugepages=8192
nohz_full=8-31 isolcpus=8-31 # 隔离训练用 CPU
nosoftlockup
mitigations=off # 关 spectre 等(性能优先)

OS 在 AI 集群的角色变化

graph TB
  OLD[传统 OS]
  OLD --> O1[管理 CPU/MEM/IO]
  
  NEW[AI 集群 OS]
  NEW --> N1[GPU 调度 + 拓扑]
  NEW --> N2[NUMA + GPU 联合调度]
  NEW --> N3[大文件 IO + checkpoint]
  NEW --> N4[InfiniBand + RDMA]
  NEW --> N5[BPF observability]
  NEW --> N6[Container + K8s 集成]

一些数字直觉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nvidia-driver 升级:     
- 大版本(535→555): 需重启
- 同版本小修: 有时 modprobe 即可

GPU "ready" 时间:
- 节点重启: 5-10 分钟(初始化)
- 容器调度后启动: 5-30 秒

GPU 利用率监控:
- dcgm 1 秒采样
- K8s prometheus 15 秒间隔
- 8 GPU 节点: 生成 ~50 个 metric / 秒

万卡集群 OS 配置一致性:
- Ansible / Salt: 5-30 分钟
- 配置漂移检测: 必备

国产 OS 的 AI 适配

1
2
3
4
5
6
7
8
9
10
11
12
openEuler:     
- 与昇腾深度集成
- npu-smi、ascend-device-plugin 已包
- "openEuler AI 版"

龙蜥(Anolis OS):
- 与寒武纪 / 摩尔线程适配
- SIG-AI 工作组

麒麟(Kylin):
- 商用支持华为昇腾
- 政企信创主流

待补充:国产 OS 在大模型训练集群的实际部署。

小结

  • AI 时代 OS 要管的多了: GPU/NPU 调度、NUMA + GPU 拓扑、大 IO
  • nvidia-device-plugin + GPU Operator 是 K8s 标配
  • MIG / vGPU / Time-Slicing 是 GPU 资源细分手段
  • Volcano / KAI Scheduler 是训练专用调度器
  • GPUDirect / GDS 让网卡 / 存储直接访问 GPU 显存
  • 大模型训练对 OS 内核参数(SHM / memlock / file-max)有特殊要求
  • 国产 OS 已与昇腾 / 寒武纪深度适配

下一篇讲国产服务器 OS——欧拉、麒麟、龙蜥、统信、深度等。