集群自动化部署方案
集群自动化部署方案
GPU 集群的自动化部署涵盖从裸金属上架到可运行训练任务的全流程。本指南覆盖 OS 安装、驱动部署、K8s 初始化、GPU Operator 安装和配置管理。
概述
GPU 集群部署比通用 K8s 集群多出 GPU 驱动、CUDA、容器运行时、Device Plugin、DCGM 等组件。自动化程度直接影响运维效率和集群可扩展性。
📖 核心流程(已掌握)
裸金属上架
→ PXE/iDRAC OS 安装
→ Ansible 基础配置(网络/存储/安全)
→ GPU 驱动 + CUDA 安装
→ Container Runtime (nvidia-docker) 安装
→ K8s Cluster (kubeadm/k3s) 初始化
→ GPU Operator 安装
→ 监控采集 (dcgm-exporter + node-exporter)
→ 接入调度器 (Volcano)
→ 健康检查 → 上线
📖 1. PXE/MAAS 裸金属 OS 部署(已掌握)
1.1 PXE 服务端配置
# 安装所需服务
yum install -y dhcp-server tftp-server syslinux httpd
# TFTP 目录结构
mkdir -p /var/lib/tftpboot/{pxelinux.cfg,ubuntu22,rocky9}
cp /usr/share/syslinux/{pxelinux.0,menu.c32,ldlinux.c32} /var/lib/tftpboot/
# DHCP 配置 /etc/dhcp/dhcpd.conf
subnet 10.0.0.0 netmask 255.255.0.0 {
range 10.0.100.1 10.0.100.254;
option routers 10.0.0.1;
option domain-name-servers 10.0.0.10;
filename "pxelinux.0";
next-server 10.0.0.5;
}
1.2 Kickstart 示例(Rocky 9)
# /var/lib/tftpboot/pxelinux.cfg/default
DEFAULT menu.c32
PROMPT 0
TIMEOUT 50
LABEL rocky9-gpu
MENU LABEL Rocky 9 GPU Node
KERNEL rocky9/vmlinuz
APPEND initrd=rocky9/initrd.img inst.ks=http://10.0.0.5/ks/rocky9-gpu.ks ip=dhcp
# kickstart /var/www/html/ks/rocky9-gpu.ks
url --url="http://10.0.0.5/rocky9/"
lang en_US.UTF-8
keyboard us
timezone Asia/Shanghai
rootpw --iscrypted $6$xxx
bootloader --location=mbr --append="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau"
zerombr
clearpart --all --initlabel
autopart --type=lvm
network --bootproto=dhcp --hostname=gpu%02d --device=eno1
firewall --disabled
selinux --disabled
services --enabled=sshd,chronyd
reboot
%packages --ignoremissing
@core
@base
chrony
vim
wget
curl
nfs-utils
pciutils
%end
%post --log=/root/ks-post.log
# 禁用 nouveau 驱动
cat > /etc/modprobe.d/blacklist-nouveau.conf << EOF
blacklist nouveau
options nouveau modeset=0
EOF
dracut --force
%end
1.3 MAAS 快速部署(Ubuntu)
# MAAS 服务器安装
sudo snap install maas
sudo maas init region+rack --database-uri maas-test-db://
# 添加节点:节点侧 PXE 启动即可自动发现
# 设置节点电源管理 (IPMI)
maas $PROFILE machine update $SYSTEM_ID \
power_type=ipmi \
power_parameters_power_address=10.0.1.100 \
power_parameters_power_user=admin \
power_parameters_power_pass=password
# 批量部署 Ubuntu 22.04
maas $PROFILE machine deploy $SYSTEM_ID \
distro_series=focal
📖 2. Ansible 自动化编排(已掌握)
2.1 Inventory 结构
# inventories/gpu-cluster/hosts.yml
all:
children:
control:
hosts:
mgmt01: { ansible_host: 10.0.0.10 }
gpu_nodes:
hosts:
gpu01: { ansible_host: 10.0.1.1, gpu_model: "A100-80G", gpu_count: 8 }
gpu02: { ansible_host: 10.0.1.2, gpu_model: "A100-80G", gpu_count: 8 }
gpu03: { ansible_host: 10.0.1.3, gpu_model: "H100", gpu_count: 8 }
inference_nodes:
hosts:
inf01: { ansible_host: 10.0.2.1, gpu_model: "L40S", gpu_count: 4 }
vars:
cluster_name: "prod-gpu-cluster"
k8s_version: "1.29.6"
cuda_version: "12.4"
nvidia_driver_version: "550.90.07"
2.2 核心 Playbook 结构
# site.yml — 总入口
- name: Deploy GPU Cluster
hosts: all
gather_facts: true
roles:
- common # SSH、NTP、内核参数
- nvidia-driver # 驱动 + CUDA 运行时
- containerd # 容器运行时 + nvidia-container-toolkit
- k8s # kubeadm/k3s 初始化
- gpu-operator # NVIDIA GPU Operator Helm 部署
- monitoring # DCGM Exporter + Node Exporter + Prometheus
2.3 nvidia-driver Role
# roles/nvidia-driver/tasks/main.yml
- name: Blacklist nouveau
copy:
dest: /etc/modprobe.d/blacklist-nouveau.conf
content: |
blacklist nouveau
options nouveau modeset=0
- name: Install NVIDIA driver
shell: |
yum-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
dnf install -y --setopt=obsoletes=0 \
nvidia-driver-{{ nvidia_driver_version }} \
cuda-toolkit-{{ cuda_version.split('.')[:2] | join('-') }}
args:
creates: /usr/bin/nvidia-smi
- name: Enable persistence mode
copy:
dest: /etc/systemd/system/nvidia-persistenced.service
content: |
[Unit]
Description=NVIDIA Persistence Daemon
[Service]
ExecStart=/usr/bin/nvidia-persistenced --user nvidia-persistenced
[Install]
WantedBy=multi-user.target
notify: restart nvidia-persistenced
- name: Set GPU performance defaults
shell: |
nvidia-smi -pm 1
nvidia-smi -ac {{ gpu_mem_clock }},{{ gpu_core_clock }}
nvidia-smi -e 0 # 禁用 ECC(训练场景可关闭,推荐推理场景开启)
2.4 containerd Role(含 nvidia-container-toolkit)
# roles/containerd/tasks/main.yml
- name: Install containerd
get_url:
url: "https://github.com/containerd/containerd/releases/download/v{{ containerd_version }}/containerd-{{ containerd_version }}-linux-amd64.tar.gz"
dest: /tmp/containerd.tar.gz
register: dl
- name: Install nvidia-container-toolkit
shell: |
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
sed 's#https://#https://#g' | tee /etc/yum.repos.d/nvidia-container-toolkit.repo
dnf install -y nvidia-container-toolkit
- name: Configure containerd for NVIDIA runtime
shell: nvidia-ctk runtime configure --runtime=containerd
notify: restart containerd
# /etc/containerd/config.toml 关键配置
# /etc/containerd/config.toml — GPU 节点关键部分
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
2.5 k8s Role(kubeadm 初始化)
# roles/k8s/tasks/init-control.yml
- name: Initialize k8s control plane
shell: |
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--apiserver-advertise-address={{ ansible_default_ipv4.address }} \
--kubernetes-version=v{{ k8s_version }} \
--upload-certs
register: kubeadm_init
when: "'control' in group_names"
- name: Install Calico CNI
shell: |
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml
when: "'control' in group_names"
- name: Join worker nodes
shell: "{{ hostvars['mgmt01'].kubeadm_join_command }}"
when: "'gpu_nodes' in group_names or 'inference_nodes' in group_names"
📖 3. GPU Operator Helm 部署(已掌握)
3.1 生产级 Helm Values
# gpu-operator-values.yaml
operator:
defaultRuntime: containerd
useNvidiaDriverCRD: true
driver:
enabled: false # 驱动由 Ansible 管理,Operator 不再安装
# enabled: true # 若让 Operator 管理驱动则开启
# version: "550.90.07"
# repo: nvcr.io/nvidia
toolkit:
enabled: false # 由 Ansible 预装
# version: "1.14.6-ubuntu20.04"
devicePlugin:
enabled: true
version: "v0.15.0"
config:
name: device-plugin-config
default: "time-slicing"
args: ["--mig-strategy=mixed",
"--pass-device-specs=true",
"--fail-on-init-error=true"]
migManager:
enabled: true
config:
name: mig-config
default: "default-mig-parted-config"
dcgmExporter:
enabled: true
env:
- name: DCGM_EXPORTER_COLLECTORS
value: "dmon,name=pod"
serviceMonitor:
enabled: true
interval: 15s
gfd:
enabled: true # GPU Feature Discovery
validator:
enabled: true # GPU Operator 自检
toolkit:
enabled: false # 已有 nvidia-container-toolkit
sandboxDevicePlugin:
enabled: false
3.2 Helm 安装命令
# 添加 NVIDIA Helm repo
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
# 安装 GPU Operator
helm upgrade --install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator \
--create-namespace \
--version v24.9.0 \
--values gpu-operator-values.yaml \
--wait \
--timeout 10m
# 验证安装
kubectl get pods -n gpu-operator
kubectl get node -o json | jq '.items[].status.allocatable | with_entries(select(.key | startswith("nvidia")))'
3.3 Time-Slicing ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: device-plugin-config
namespace: gpu-operator
data:
time-slicing: |
version: v1
sharing:
timeSlicing:
renameByDefault: false
failRequestsGreaterThanOne: false
resources:
- name: nvidia.com/gpu
replicas: 4 # 每卡 4 个时间片
- name: nvidia.com/mig-1g.10gb
replicas: 2
📖 4. 自动化健康检查脚本(已掌握)
#!/bin/bash
# health-check.sh — GPU 节点上线前健康检查
set -euo pipefail
NODE=$1
LOG_FILE="/var/log/gpu-health-${NODE}-$(date +%Y%m%d-%H%M%S).log"
echo "=== GPU Health Check for ${NODE} ===" | tee -a "$LOG_FILE"
# 1. nvidia-smi 基础检测
echo "[1/6] nvidia-smi check..." | tee -a "$LOG_FILE"
ssh "$NODE" 'nvidia-smi -q -d TEMPERATURE,POWER,MEMORY | grep -E "GPU Current Temp|Power Draw|Total"' | tee -a "$LOG_FILE"
# ECC 错误检查
ssh "$NODE" 'nvidia-smi -q -d ECC | grep -A2 "Volatile"' | tee -a "$LOG_FILE"
# 2. NVLink 状态
echo "[2/6] NVLink check..." | tee -a "$LOG_FILE"
ssh "$NODE" 'nvidia-smi nvlink -s' | grep -c "active" | tee -a "$LOG_FILE"
# 3. CUDA sample bandwidthTest
echo "[3/6] CUDA bandwidth test..." | tee -a "$LOG_FILE"
ssh "$NODE" 'docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 \
/usr/local/cuda/extras/demo_suite/bandwidthTest 2>/dev/null' | tee -a "$LOG_FILE"
# 4. NCCL 多卡通信测试(all_reduce 带宽)
echo "[4/6] NCCL all_reduce test..." | tee -a "$LOG_FILE"
ssh "$NODE" "docker run --rm --gpus all --network host \
-e NCCL_DEBUG=INFO \
nvcr.io/nvidia/pytorch:24.06-py3 \
bash -c 'git clone -b v2.20.5 https://github.com/NVIDIA/nccl-tests.git && \
cd nccl-tests && make MPI=0 CUDA_HOME=/usr/local/cuda && \
./build/all_reduce_perf -b 8 -e 512M -f 2 -g 8'" | tee -a "$LOG_FILE"
# 5. 网络带宽测试(iperf3 或 nccl 跨节点)
echo "[5/6] Network bandwidth test..." | tee -a "$LOG_FILE"
# 从目标节点到已有节点做 ib_write_bw 或 nccl 跨节点测试
ssh "$NODE" "ib_write_bw -d mlx5_0 --report_gbits 10.0.1.1 2>/dev/null || \
iperf3 -c 10.0.1.1 -P 8" | tee -a "$LOG_FILE"
# 6. 存储性能测试
echo "[6/6] Storage benchmark..." | tee -a "$LOG_FILE"
ssh "$NODE" "fio --name=randrw --ioengine=libaio --rw=randrw --bs=4k --direct=1 \
--size=4G --numjobs=16 --runtime=60 --group_reporting \
--filename=/mnt/shared-storage/fio-test" | grep "iops\|bw" | tee -a "$LOG_FILE"
# 结果汇总
echo "=== Health check completed ===" | tee -a "$LOG_FILE"
grep -i "error\|fail\|ERR" "$LOG_FILE" && echo "❌ ISSUES FOUND!" || echo "✅ ALL CHECKS PASSED"
📖 5. 集群扩容流程(已掌握)
# ===== Step 1: 节点发现与信息录入 =====
# 记录节点信息到 inventory
cat >> inventories/gpu-cluster/hosts.yml << EOF
gpu10: { ansible_host: 10.0.1.10, gpu_model: "H100", gpu_count: 8 }
EOF
# ===== Step 2: 批量运行 Ansible Playbook =====
ansible-playbook -i inventories/gpu-cluster/hosts.yml site.yml \
--limit gpu10 \
--extra-vars "nvidia_driver_version=550.90.07 cuda_version=12.4"
# ===== Step 3: 加入 K8s 集群 =====
# 控制节点生成 join command
ssh mgmt01 "kubeadm token create --print-join-command" > /tmp/join-cmd.sh
# 在目标节点执行
ssh gpu10 "bash -s" < /tmp/join-cmd.sh
# ===== Step 4: 打标签和污点 =====
kubectl label node gpu10 \
nvidia.com/gpu.product=NVIDIA-H100-PCIe \
nvidia.com/gpu.count=8 \
node-role.kubernetes.io/gpu-worker=true
kubectl taint node gpu10 nvidia.com/gpu=true:NoSchedule
# ===== Step 5: 等待 GPU Operator 就绪 =====
kubectl wait --for=condition=Ready pod \
-l app=nvidia-device-plugin-daemonset \
-n gpu-operator \
--field-selector spec.nodeName=gpu10 \
--timeout=300s
# ===== Step 6: 运行健康检查 =====
./health-check.sh gpu10
# ===== Step 7: 验证调度能力 =====
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: gpu-test-gpu10
spec:
nodeName: gpu10
containers:
- name: test
image: nvidia/cuda:12.4.0-base-ubuntu22.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
EOF
kubectl logs gpu-test-gpu10 | grep "NVIDIA-SMI"
kubectl delete pod gpu-test-gpu10
📖 6. Day-2 运维(已掌握)
6.1 证书轮换
# K8s 证书检查
kubeadm certs check-expiration
# 自动续期(1 年内有效)
kubeadm certs renew all
# 手动续期特定证书
kubeadm certs renew apiserver
kubeadm certs renew apiserver-etcd-client
kubeadm certs renew etcd-server
kubeadm certs renew etcd-peer
# 重启组件使新证书生效
crictl ps | grep -E "kube-apiserver|kube-controller|kube-scheduler|etcd" | awk '{print $1}' | \
xargs -I {} crictl stop {}
# 分发新 admin.conf
cp /etc/kubernetes/admin.conf ~/.kube/config
6.2 ETCD 备份与恢复
# 定期备份(建议加入 cron)
#!/bin/bash
BACKUP_DIR="/var/backups/etcd/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
ETCDCTL_API=3 etcdctl snapshot save "$BACKUP_DIR/etcd-snapshot.db" \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
etcdctl snapshot status "$BACKUP_DIR/etcd-snapshot.db" --write-out=table
# 恢复流程
etcdctl snapshot restore /var/backups/etcd/etcd-snapshot.db \
--data-dir=/var/lib/etcd-restore \
--name=mgmt01 \
--initial-cluster=mgmt01=https://10.0.0.10:2380 \
--initial-advertise-peer-urls=https://10.0.0.10:2380
6.3 GPU 节点 Drain/Replace
# Step 1: 驱逐 GPU 节点
kubectl drain gpu03 --ignore-daemonsets --delete-emptydir-data --force
# Step 2: 从集群移除
kubectl delete node gpu03
# Step 3: 清理 GPU Operator(在目标节点)
ssh gpu03 "kubeadm reset -f"
ssh gpu03 "rm -rf /etc/cni /var/lib/kubelet /var/lib/etcd"
# Step 4: 硬件维护 / 更换 GPU
# ... 物理操作 ...
# Step 5: 重新加入集群(走扩容流程)
ansible-playbook -i inventories/gpu-cluster/hosts.yml site.yml --limit gpu03
# Step 6: 取消隔离
kubectl uncordon gpu03
6.4 GPU Operator 版本升级
# 先升级一个节点验证(canary)
kubectl patch daemonset -n gpu-operator nvidia-device-plugin-daemonset \
-p '{"spec":{"template":{"spec":{"nodeSelector":{"nvidia.com/gpu.canary":"true"}}}}}}'
kubectl label node gpu03 nvidia.com/gpu.canary=true --overwrite
helm upgrade --install gpu-operator nvidia/gpu-operator \
--version v24.12.0 \
--values gpu-operator-values.yaml \
--wait
# 验证 canary 节点
nvidia-smi # 在 canary Pod 中测试
# 全量升级
kubectl label node gpu03 nvidia.com/gpu.canary-
helm upgrade --install gpu-operator nvidia/gpu-operator \
--version v24.12.0 \
--values gpu-operator-values.yaml
关联知识
学习时间
| 阶段 | 时间 | 备注 |
|---|---|---|
| 骨架创建 | 2026-06-30 | 框架搭建 |
| 实战补充 | 2026-06-30 | PXE/Ansible/Helm 完整脚本 |
状态标记
📖 已掌握 — PXE/Kickstart OS 部署、Ansible 编排、GPU Operator Helm 部署、健康检查、扩容流程、Day-2 运维 📝 待补充 — GPU Operator 离线安装方案、大规模集群分批灰度策略、SR-IOV 网络集成