Kickstart + 内网源 + Ansible:48 节点版本一致性的批量交付
48 台机器逐台手工装系统、装驱动、调参数,结局必然是"每台机器都不一样"。版本漂移是中小集群交付里最隐蔽的债务——它在第 3 个月的性能对比、第 6 个月的故障排查时一次性爆发。本文记录 384 卡 L40S IB 集群如何用 Kickstart + 内网源 + Ansible 把 48 节点钉在同一个软件版本上。
1. 为什么"版本一致性"是交付的核心目标
| 交付方式 | 节点间差异 | 可复现性 | 故障排查 |
|---|---|---|---|
| 逐台手工安装 | 高(路径、版本、参数随手改) | 无 | 每台都要重新对账 |
| 共享镜像克隆 | 中(镜像更新后新旧混杂) | 弱 | 不知道哪台是哪版 |
| Kickstart + 统一源 + Ansible | 低(同一答案文件 + 同一源) | 强 | 全节点对账 = 对一份配置 |
判断:版本一致性买的不只是"省事",是可复现性——出问题时能确定"这台机器跑的是什么",性能不达标时能排除"环境差异"这个变量。
2. Kickstart:把"装系统"变成声明式
- 一份 ks.cfg 覆盖分区、时区、软件包组、root 策略、SSH 配置;
- 内网 PXE/DHCP 引导:48 节点批量装机,全程无人工干预;
- 系统盘 RAID1(2×480G SATA)在 Kickstart 里用
%pre脚本预配置(storcli建卷、写缓存 Enabled),避免装完才发现 RAID 没组好。
踩坑点:
%pre里组 RAID 的脚本要幂等——重复装机时卷已存在,先删卷再建;- 固件/BIOS 差异会导致同型号机器盘序不同,用盘符而不是序号做 RAID 成员;
- Kickstart 装完第一件事就是拉内网源刷新,别让节点从公网装包。
3. 内网源:版本一致性的地基
- 统一 YUM 源:openEuler 24.03 基础源 + EPOL 镜像到内网,全部节点
baseurl指向内网; - 统一 pip 源:训练依赖(PyTorch、NCCL 相关 Python 包)从内网 PyPI 镜像装,锁版本号;
- 关键包锁定:内核、驱动、CUDA 栈、NCCL 的版本在构建清单里写死,不是"装最新"。
判断:内网源不是为了"快",是为了把公网漂移从交付里拿走——公网源今天和明天给的包可能不一样,内网源是冻结的。
4. Ansible:配置下发,禁止手工漂移
- 所有配置(内核参数、中断亲和、sysctl、NCCL 环境、MPI 环境)以 Ansible role 统一下发;
- 禁止单机手工改:凡是"先改这台试试"的,最终都要回流成 role;
- 幂等性:role 重跑 N 次结果一致,交付时全节点跑一遍 playbook 就是对账。
验证命令:
bash
# 全节点软件栈对账:跑同一 playbook + 比对关键包版本
ansible-playbook -i hosts.ini site.yml --check
ansible all -m shell -a "rpm -q kernel mlx5_objs cuda-12-x" | sort | uniq -c | wc -l
# 输出 1 行 = 48 台版本完全一致5. 验收:一致性怎么证明
| 检查项 | 判定 |
|---|---|
| 关键包版本 | 48 台 rpm -q 输出完全一致(去重后 1 条) |
| 内核参数 | /proc/cmdline 与 sysctl 关键项全节点一致 |
| 配置文件 hash | /etc/ 关键文件 sha256 一致(Ansible --check 0 变更) |
| 随机抽查 | 任选 2 台跑同一基准(STREAM/NCCL),结果在噪声内 |
6. 交付清单
- Kickstart:RAID 预配置幂等脚本 + 内网引导,48 台批量装机;
- 内网源:YUM + pip 全部指向内网,关键版本锁进构建清单;
- Ansible:全部配置 role 化,
--check零变更; - 一致性验收:包版本、内核参数、配置 hash 三查全过;
- 记录基线镜像 tag,作为后续变更的 diff 基准。
本文基于 384 卡 L40S IB 集群交付实测整理;具体版本号与命令以现场实测为准。