跳转至

INCIDENT-20260918-Slurm 26.05 部署排障

本文档是排障台账与交接记录,不是正式事故复盘报告。 变更档案见 SOP.md;日常运维见 RUNBOOK.md。

一、概要

项 内容
发生时间 2026-09-18 05:00 – 13:30(UTC+8)
等级 非生产事故(新集群部署阶段,5 台初始为空机,无业务与数据受影响)
影响面 仅阻塞本次部署进度,集群未能按时交付
当前状态 已恢复。部署完成,主备双 UP,3 计算节点 idle,作业提交验证通过

一句话概括:新环境(Rocky 9.8 + cgroup v2)与部署工具原假设(CentOS 7 + cgroup v1)差异过大, 连续触发 10 个阻塞点,逐个定位并修复,其中 6 个已固化为部署器的自动处理能力。

二、影响范围

  • 业务:无。目标 5 台机器在本次部署前是空机,未承载任何业务。
  • 用户:无。集群尚未交付使用。
  • 数据:无损坏、无丢失。部署过程中创建的数据库 slurm_acct_db 为新建库; 宿主机 /etc/exports 的改动已备份且未影响同机 k8s 的既有导出。
  • 附带影响:排障期间在宿主机 192.168.31.249 上新增了一条 NFS 导出 (/data/nfs/slurm-state),并在 /etc/exports 留有 2 个备份文件(已登记台账)。

三、时间线

时间 事件
05:07 第 1 次部署启动,阶段 1 装包失败:dnf 报 Unable to find a match: munge-devel
05:12 启用 CRB 仓库后重跑,阶段 2 数据库初始化成功(建库/建号/授权)
05:19 阶段 3 编译失败:tar: command not found(.18 未装 tar)
05:25 补齐 tar/gzip 后重跑,configure 报 mysql_config not found
05:26 修正 --with-mysql_config 传参(应为目录而非文件路径),configure 通过;6 分钟后编译完成
05:34 slurmdbd 启动失败:Failed to access /var/run/munge/munge.socket.2
05:40 定位为 munge key 权限(640)被 munged 拒绝;改 400 后 munge 启动
05:43 slurmctld 启动失败:fatal: No Assoc usage file ... to recover
05:50 定位到需以 -i 初始化 state;同时发现 key 一致时会提前 return、跳过"确保 munge 运行"
05:52 以 -i 启动后新 fatal:Partition interactive has an invalid qos
05:58 调整启动顺序(QoS 先于 slurmctld)后 slurmctld 起来
06:04 slurmd 启动失败:wait-for-slurmctld.sh 连不上 slurm-master-01:6817
06:10 定位为 firewalld 只放通 ssh;部署器加入端口放通
06:23 slurmd 再次失败:libhwloc.so.15: cannot open shared object file
06:31 补齐运行库后仍失败:cannot create cgroup context for cgroup/v2
06:40 定位为缺 dbus-devel 导致未编译 cgroup_v2.so;补依赖重编译
06:50 各计算节点仍报缺插件(未重新分发);修正分发判据
12:43 部署首次完整成功:主备双 UP、3 节点 idle、作业可提交
12:45 开始修复性能问题:宿主机新建 async 专用 NFS 导出
13:04 完成共享存储迁移,fsync 由 500–800ms 降至 9–12ms
13:20 修复启动告警(MailProg / http_parser)与幂等缺陷,复跑验证零变更

四、根因

以下 10 个阻塞点按"环境差异 / 配置语义 / 代码缺陷"三类归因,均附证据。

A. 环境差异类(4 项)

A1. munge-devel 不在默认仓库

  • 证据:dnf install -y munge munge-devel ... → Error: Unable to find a match: munge-devel
  • 根因:Rocky/RHEL 9 的 -devel 包集中在 CRB 仓库,该仓库默认 disabled (dnf repolist --all 显示 crb ... disabled)

A2. tar/gzip 未安装

  • 证据:tar: command not found
  • 根因:Rocky 9 最小化安装不含 tar/gzip,而打包与解压依赖它们

A3. firewalld 只放通 ssh

  • 证据:.16 上 ss -lntp | grep 6817 显示监听 0.0.0.0:6817,但 .18 侧 /dev/tcp/slurm-master-01/6817 返回 CLOSED;firewall-cmd --list-all 仅 cockpit dhcpv6-client ssh
  • 根因:Rocky 默认启用 firewalld 且不放通 6817/6818/6819

A4. 运行库随编译环境缺失

  • 证据:ldd slurmd | grep 'not found' → libhwloc.so.15; 修正检查范围后又发现 libhttp_parser.so.2、libibmad.so.5、libibumad.so.3
  • 根因:编译节点的 -devel 包不会随产物分发到计算节点

B. 配置语义类(3 项)

B1. --with-mysql_config 应传目录

  • 证据:configure: error: *** mysql_config not found
  • 根因:Slurm 的该参数接收目录,它在该目录下查找 mysql_config / mariadb_config; 传入二进制文件路径会导致探测失败

B2. munge key 权限必须 400

  • 证据:munged: Error: Keyfile is insecure: "/etc/munge/munge.key" should not be readable or writable by group or world
  • 根因:误判"SlurmUser 需要读 key"而设为 640。实际 Slurm 经 munge socket 认证, 不需要直接读 key,munged 强制要求 key 仅属主可读

B3. QoS 必须先于 slurmctld 存在

  • 证据:fatal: Partition interactive has an invalid qos (interactive), please check your configuration
  • 根因:slurm.conf 的 PartitionName 引用了 QOS,启动时会校验其存在性; 原实现把 QoS 初始化放在启动之后,形成死锁(启动需要 QoS,建 QoS 需要 slurmdbd)

C. 代码缺陷类(3 项)

C1. 状态判据过浅,出现"假一致"

  • 证据(分发):.19 上 lib/slurm/ 无 cgroup_v2.so,但部署器判定"目标版本已安装"而跳过分发
  • 证据(编译):WITH_LIBHTTP_PARSER_TRUE='#'(未启用),但 install_dir 已存在且版本号匹配, 使编译被跳过
  • 根因:判据只看版本号与目录存在性,未校验产物内容/构建输入

C2. 零变更检测遗漏关键状态

  • 证据(服务):sync_status 判定 19 条全 SKIP、直接退出,但当时 slurmdbd 仍处于 restart 循环
  • 证据(挂载):挂载源已变(/srv/nfs/k8s-hdd → /data/nfs/slurm-state), 但只判断 mountpoint -q 为 MOUNTED 即 SKIP
  • 根因:sync_status 只检查文件/版本,不检查服务运行态与挂载源

C3. 渲染结果含时间戳,破坏幂等

  • 证据:连续两次部署,第二次仍报"配置漂移,需重写"; 渲染的 slurm.conf banner 含 # 生成时间: <每次不同的时间戳>
  • 根因:配置 SHA256 每次都变,漂移检测永久判为有变更,幂等彻底失效

附带发现(非阻塞,但会造成静默失效)

D1. timer 用 Requires=slurmctld.service

  • 证据:一次部署(含 slurmctld 重启)后 systemctl is-active slurm-cluster-health.timer → inactive
  • 根因:Requires 是强依赖,slurmctld 停止会连带停止 timer,健康巡检静默失效(无任何报错)

D2. 凭据经命令行泄露到日志

  • 证据:部署日志中出现 mysqldump ... -u slurm -p'<明文>' 与 base64 编码的 cnf 内容
  • 根因:早期实现用命令行传参;虽为内网测试环境,仍属必须消除的隐患

五、处置过程

问题 处置动作 是否已固化为自动能力
A1 CRB 仓库 检测到 -devel 包时自动 dnf config-manager --set-enabled crb ✅ 是
A2 tar/gzip 加入 base_packages,所有节点安装 ✅ 是
A3 firewalld 按 slurm.firewall_ports 自动放通并 reload ✅ 是
A4 运行库 ldd 探测 sbin/* + lib/slurm/*.so,dnf repoquery --whatprovides 反查包名安装 ✅ 是
B1 mysql_config 探测 mariadb_config/mysql_config 并取其目录 ✅ 是
B2 munge key 固定 chown munge:munge + chmod 400;且无论 key 是否一致都确保 munged 运行 ✅ 是
B3 QoS 顺序 启动顺序固定为 slurmdbd → cluster/QoS/账户 → slurmctld → slurmd ✅ 是
C1 假一致 引入编译输入指纹(版本+依赖+configure 参数),变化即重编译重分发 ✅ 是
C2 零变更遗漏 sync_status 增加服务运行态、挂载源比对、配置内容漂移检测 ✅ 是
C3 时间戳 移除渲染结果中的时间戳,并在代码中写明约束 ✅ 是
D1 timer 依赖 改 Requires= → Wants=;巡检同时校验 enabled 与 active ✅ 是
D2 凭据泄露 数据库操作改 0600 临时 cnf 传参;文件写入改走 SFTP;已清理含凭据日志 ✅ 是
首次 state 初始化 一次性 systemd override 注入 slurmctld -i,完成后立即移除 ✅ 是
共享存储性能 宿主机新建 async 专用导出(避开 k8s 共用的卷) ⚠️ 手工操作,已写入 SOP 第 5.6 节

止血与回滚动作:本次未执行整体回滚。每轮失败均发生在部署早期(装包、编译、服务启动), 失败即中止且不切换 symlink,因此未对系统造成半成品状态;期间仅手工清理过 1 处测试残留 (.16 上试验 -i 时创建的 systemd override 目录)。

六、恢复验证

# 集群与主备
/usr/local/slurm/bin/sinfo -N -o '%N %T %C' | sort -u
# 预期:slurm-work-01/02/03 均 idle
/usr/local/slurm/bin/scontrol ping
# 预期:Slurmctld(primary) at slurm-master-01 is UP / (backup) at slurm-master-02 is UP

# 共享存储
findmnt -o TARGET,SOURCE,FSTYPE /mnt/slurm-shared
# 预期:/mnt/slurm-shared  192.168.31.249:/data/nfs/slurm-state  nfs4

# 告警归零
journalctl -u slurmctld --since '10 min ago' --no-pager | grep -c 'Saving to StateSaveLocation'  # 0
journalctl -u slurmctld -n 50 --no-pager | grep -cE 'http_parser|plugin_load'                     # 0

# 端到端作业
cd /tmp && printf '#!/bin/bash\nhostname\necho JOB_OK\n' > t.sh
/usr/local/slurm/bin/sbatch -N1 -o /tmp/t_%j.out t.sh && sleep 15 && cat /tmp/t_*.out
/usr/local/slurm/bin/sacct -n -P -o JobID,State,ExitCode --starttime today | tail -1
# 预期:COMPLETED 0:0

# 幂等
cd /home/rain/Documents/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml 2>&1 | tail -3
# 预期:零变更,无需部署(全跳过)

实测结果:以上全部符合预期;共享存储 fsync 由 500–800ms 降至 9–12ms。

七、复盘

有效的做法

  1. 先定位再动手:每次失败都先取证据(journalctl 原文、ldd 输出、config.log 变量值), 避免了"猜测式重试"。例如 http_parser 问题是二次定位(先装 devel 包、再发现运行库缺失)才见底。
  2. dry-run 前置:--dry-run 会真实读取节点状态,因此能提前看到"挂载源变更""配置漂移"这类 只靠静态阅读发现不了的项。
  3. 幂等复跑作为验收手段:正是"第二次仍报配置漂移"暴露了时间戳缺陷; 正是"服务没起来却判零变更"暴露了 sync_status 过浅。
  4. 自动补齐而非固定清单:运行库用 ldd + dnf repoquery 推导,而非维护包名清单, 本次一次性发现并补齐了 4 个缺失库。

拖慢恢复的因素

  1. 每轮重跑成本高:sync_status 串行查询 5 个节点、单轮约 5–6 分钟, 加上编译 8 分钟,导致验证循环长。若早期就并发化查询,可显著缩短。
  2. 判据修复存在连带性:修好"运行库检查覆盖插件"后,又暴露"分发判据不校验插件", 属于同类问题分两次才发现——根因是缺少统一的"产物完整性"概念。
  3. cgroup v2 的报错信息不够直白:cannot create cgroup context 未指出缺的是插件还是库, 需要翻 config.log 与插件目录才能定位。

八、行动项

事项 责任人 期限 状态
把 sync_status 的节点查询并发化,缩短单轮巡检耗时 部署执行人 下次迭代 待办
补一次 --rollback 的真实演练(当前仅验证了幂等,未验证回滚) 部署执行人 2026-09-25 待办
评估 slurmdbd 双活方案,消除记账单点 部署执行人 按需 待办
在 RUNBOOK 中补充"共享存储单点"的容量与故障预案 部署执行人 2026-09-25 待办
清理宿主机 /etc/exports 的 2 个历史备份文件(对照 backup/README.md) 用户 自行决定 待确认
清理集群侧 /var/backups/slurm/ 的 66 个历史备份(对照 backup/README.md) 用户 自行决定 待确认

来源:/home/rain/Documents/slurm/slurm_ins/docs/INCIDENT-20260918-slurm-26.05部署排障.md | 最后更新:2026-09-24 21:30(由 build/sync.py 同步,请勿在站点侧修改)