跳转至

SLURM 集群健康检查行业标准改造 SOP

项目: SLURM 集群健康检查机制标准化改造 执行日期: 2026-08-14 SLURM 版本: 21.08.8-2 适用系统: CentOS 7 / RHEL 7 (systemd 219) 最终状态: 全量修复完成并验证通过 ✅(含 P0 严重逻辑错误修复 + P1 配置优化) 目标读者: 运维工程师,具备 SLURM/systemd 基础


〇、快速索引

章节 场景 关键词
一 改造背景与架构对比 自研 vs 行业标准、三层保障体系
二 从零部署行业标准 HealthCheck(复现 SOP) slurm.conf、health_check.sh、configless
二-B P0 严重逻辑错误修复(全量检查) 服务依赖链、Restart=always、wait-for-slurmctld
三 验证清单 部署后如何确认全链路
四 自研方案保留策略 降频、定位调整、备份巡检层
五 故障排查 HealthCheck 不触发、节点不 DRAIN
六 回滚方案 如何撤销所有变更
七 日常运维速查 常用命令清单

一、改造背景与目标

1.1 改造前状态诊断

检查项 改造前状态 是否行业标准
ReturnToService=1 ✅ 已配置 ✅ 是
SlurmdTimeout=300 ✅ 已配置 ✅ 是
systemd Restart=on-failure ✅ 已配置 ✅ 是
SLURM 内置 HealthCheck ❌ 未配置 ❌ 否(关键差距)
自研 cluster_health_check.py ✅ 已部署(5分钟) ❌ 非官方标准
Prometheus+Grafana 监控 ✅ 已对接 ✅ 是

核心差距:SLURM 官方推荐的内置 HealthCheck 机制(HealthCheckInterval+HealthCheckProgram)未启用。自研方案通过 SSH 远程检查,依赖网络连通性,且非 SchedMD 官方标准。

1.2 自研方案 vs 行业标准对比

对比维度 自研方案(改造前) SLURM 内置 HealthCheck(行业标准)
检查执行者 控制节点通过 SSH 远程检查 各计算节点 slurmd 本地执行
检查方式 systemctl is-active + scontrol 执行 HealthCheckProgram 脚本
网络依赖 依赖 SSH 连通性(单点风险) 无网络依赖,本地执行
失败处理 SSH 重启 slurmd + scontrol resume 节点自动 DRAIN + 记录原因
服务重启能力 ✅ 可重启 slurmd 服务 ❌ 仅 DRAIN,不重启服务
标准性 自研,非官方 SchedMD 官方推荐
配置分发 需手动部署到控制节点 configless 模式自动分发

1.3 改造后三层保障体系

第 1 层:SLURM 原生自动恢复(秒级,行业标准)
├── ReturnToService=2          → 节点 DOWN 后自动恢复 IDLE(比 1 更积极,任何 DOWN 都恢复)
├── SlurmdTimeout=300          → 心跳超时 5 分钟判定 DOWN
├── systemd Restart=on-failure → slurmd 异常退出自动重启
└── HealthCheckInterval=300    → slurmd 本地周期性健康检查(新增)

第 2 层:自研备份巡检(15 分钟,补充层)
├── cluster_health_check.py    → SSH 远程检查 + slurmd 服务重启
└── slurm-cluster-health.timer → 定时器(从 5 分钟降频至 15 分钟)

第 3 层:Prometheus 监控告警(告警层)
├── slurm-exporter             → 指标采集
├── Grafana 大盘               → 可视化展示
└── Alertmanager               → 邮件告警

职责分工: - 第 1 层(内置 HealthCheck):本地检测节点不健康 → 自动 DRAIN,防止作业分配到故障节点 - 第 2 层(自研备份):SSH 远程重启异常 slurmd 服务(内置机制无法重启服务,只能 DRAIN) - 第 3 层(Prometheus):告警通知运维人员


二、从零部署行业标准 HealthCheck(复现 SOP)

步骤 0:前置条件检查

# 1. 确认 SLURM 集群正常运行
ssh root@192.168.0.2 'sinfo'
# 期望: 3 节点 idle

# 2. 确认 slurm.conf 位置
ssh root@192.168.0.2 'ls /usr/local/slurm/etc/slurm.conf'

# 3. 确认 configless 模式(计算节点无需本地 slurm.conf)
ssh root@k8s-node-01 'ps aux | grep slurmd | grep -v grep'
# 期望: 包含 --conf-server 参数

步骤 1:编写 HealthCheck 脚本

原理:SLURM 内置 HealthCheck 由 slurmd 在各节点本地执行,脚本退出码 0=健康、非 0=不健康(节点自动 DRAIN)。

文件路径:slurm_health_check.sh

检查项(按 HPC 节点常见故障设计): 1. 根文件系统可写(防止只读挂载) 2. /tmp 可写(作业临时目录) 3. NFS 挂载点已挂载(用户家目录/EDA 工具/数据共享) 4. 关键目录可写(/home、/mnt/nfs/slurm、/opt/lsf) 5. 可用内存 > 100MB(防止 OOM 僵死) 6. 磁盘使用率 < 90%(防止 100% 占满导致 exitcode 126/127) 7. munge 服务运行(SLURM 认证依赖)

步骤 2:部署脚本到所有节点

# ⚠️ 高危操作前置:脚本需在所有运行 slurmd 的节点上存在
# 部署到控制节点 + 所有计算节点
for node in k8s-master-01 k8s-node-01 k8s-node-02 k8s-node-03; do
    echo "=== 部署到 ${node} ==="
    scp /home/rian/Documents/slurm/slurm_health_check.sh root@${node}:/usr/local/bin/
    ssh root@${node} '
        chmod +x /usr/local/bin/slurm_health_check.sh
        # 手动执行验证(退出码应为 0)
        /usr/local/bin/slurm_health_check.sh; echo "退出码: $?"
    '
done
# 期望: 所有节点退出码为 0

步骤 3:配置 slurm.conf 启用 HealthCheck

⚠️ 高危操作:修改 slurm.conf 需重启 slurmctld 和所有 slurmd,会短暂中断调度。

# === 在控制节点执行 ===

# 1. 备份配置(必须执行)
ssh root@k8s-master-01 'cp /usr/local/slurm/etc/slurm.conf \
  /usr/local/slurm/etc/slurm.conf.bak.$(date +%Y%m%d%H%M%S)'

# 2. 在 SlurmdTimeout 行后插入 HealthCheck 三参数
ssh root@k8s-master-01 'sed -i "/^SlurmdTimeout=300/a\\
HealthCheckInterval=300\\
HealthCheckProgram=/usr/local/bin/slurm_health_check.sh\\
HealthCheckNodeState=ANY" /usr/local/slurm/etc/slurm.conf'

# 3. 验证配置
ssh root@k8s-master-01 'grep -A3 "^SlurmdTimeout" /usr/local/slurm/etc/slurm.conf'
# 期望输出:
# SlurmdTimeout=300
# HealthCheckInterval=300
# HealthCheckProgram=/usr/local/bin/slurm_health_check.sh
# HealthCheckNodeState=ANY

参数说明:

参数 值 说明
HealthCheckInterval 300 每 300 秒(5 分钟)执行一次检查
HealthCheckProgram /usr/local/bin/slurm_health_check.sh 检查脚本路径(各节点本地)
HealthCheckNodeState ANY 所有状态节点都检查(含 IDLE/ALLOC/MIXED/REGISTER/RESUME)

步骤 4:重启服务使配置生效

# 1. 重启 slurmctld(使新 slurm.conf 生效)
ssh root@k8s-master-01 'systemctl restart slurmctld; sleep 3; systemctl is-active slurmctld'
# 期望: active

# 2. 重启所有 slurmd(触发 configless 模式重新拉取配置)
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} 'systemctl restart slurmd; sleep 2; systemctl is-active slurmd'
    # 期望: active
done

# 3. 等待配置同步
sleep 15

# 4. 检查节点状态
ssh root@k8s-master-01 'sinfo'
# 期望: 3 节点 idle

步骤 5:验证 configless 配置分发

# 验证所有计算节点已获取 HealthCheck 配置
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    echo "=== ${node} ==="
    ssh root@${node} 'grep "^HealthCheck" /var/spool/slurmd/conf-cache/slurm.conf'
    # 期望: 3 行 HealthCheck 配置
done

步骤 6:调整自研方案频率(备份巡检层)

# 将自研脚本执行频率从 5 分钟降为 15 分钟(避免与内置 HealthCheck 重复)
# 修改 slurm-cluster-health.timer 中 OnUnitActiveSec=5min → 15min

ssh root@k8s-master-01 '
    sed -i "s/OnUnitActiveSec=5min/OnUnitActiveSec=15min/" /etc/systemd/system/slurm-cluster-health.timer
    systemctl daemon-reload
    systemctl restart slurm-cluster-health.timer
    systemctl list-timers slurm-cluster-health.timer
'
# 期望: NEXT 显示 15 分钟后

二-B、P0 严重逻辑错误修复(全量检查)

背景:2026-08-14 集群物理重启后,节点 DOWN 状态持续 4 小时 40 分钟未自动恢复。全量检查发现 9 个问题(4 个 P0 + 4 个 P1 + 1 个 P2),以下为完整修复记录。

2B.1 故障链复盘

12:20:47 系统物理启动
12:22:52 munge 就绪
12:22:58 k8s-node-01 slurmd 启动 ← 此时 slurmctld 尚未就绪!
12:23:16 slurmctld 开始启动
12:23:28 slurmd 失败: "failed to load configs" ← configless 拉取配置失败
         → Restart=on-failure 未触发自动重启(systemd 219 行为)
12:25:56 slurmctld: "Nodes k8s-node-[01-03] not responding, setting DOWN"
         → 健康检查引导服务超时被杀(120秒 < 引导模式需要180秒)
17:03:27 slurmd 才重新启动(4小时40分钟后)

2B.2 问题汇总与修复

# 严重度 问题 根因 修复方案
1 P0 slurmd 启动不等待 slurmctld configless 模式下 slurmd 在 slurmctld 就绪前启动 添加 ExecStartPre=wait-for-slurmctld.sh
2 P0 Restart=on-failure 未可靠触发 systemd 219 下 slurmd 失败后 4 小时+ 未重启 改为 Restart=always + RestartSec=10
3 P0 slurmctld 不等待 slurmdbd After= 缺少 slurmdbd.service 添加 After=...slurmdbd.service + Wants=slurmdbd.service
4 P0 引导服务超时不足 TimeoutStartSec=120 < 引导模式需要 180 秒 延长至 TimeoutStartSec=300
5 P1 ReturnToService=1 节点恢复不够积极 改为 ReturnToService=2
6 P1 JobAcctGatherType=none 不收集作业资源数据 改为 jobacct_gather/cgroup
7 P1 JobCompType=none 不记录作业完成信息 改为 jobcomp/slurmdbd
8 P2 DefMemPerCPU=100 默认内存过小(节点有 2000MB/CPU) 改为 DefMemPerCPU=1024
9 P2 RestartSec=5 重启间隔过短 改为 RestartSec=10

2B.3 修复1:创建 wait-for-slurmctld.sh 脚本

原理:configless 模式下 slurmd 通过 --conf-server 从 slurmctld 拉取配置。如果 slurmctld 尚未就绪,slurmd 报 failed to load configs 并退出。本脚本在 slurmd 启动前等待 slurmctld 可达。

文件路径:wait-for-slurmctld.sh

# 部署到所有节点(控制节点 + 计算节点)
for node in k8s-master-01 k8s-node-01 k8s-node-02 k8s-node-03; do
    scp wait-for-slurmctld.sh root@${node}:/usr/local/bin/
    ssh root@${node} 'chmod +x /usr/local/bin/wait-for-slurmctld.sh'
done

关键设计: - 通过 scontrol ping 检测 slurmctld 就绪(注意:SLURM 21.08 输出 is UP,旧版输出 is alive) - MAX_WAIT=60 秒(需小于 systemd TimeoutStartPre 默认 90 秒) - 同时支持 TCP 端口检测作为备选

2B.4 修复2:重写 slurmd.service

文件路径:slurmd.service

关键变更:

[Service]
# P0修复1:启动前等待 slurmctld 就绪(configless 模式必需)
ExecStartPre=/usr/local/bin/wait-for-slurmctld.sh k8s-master-01 6817
TimeoutStartSec=180

# P0修复2:Restart=on-failure → always(确保任何退出都重启)
Restart=always
RestartSec=10

部署命令:

# ⚠️ 高危操作:批量修改计算节点配置前必须备份
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    # 备份
    ssh root@${node} 'cp /etc/systemd/system/slurmd.service \
      /etc/systemd/system/slurmd.service.bak.$(date +%Y%m%d%H%M%S)'
    # 推送新配置
    scp slurmd.service root@${node}:/etc/systemd/system/
    ssh root@${node} 'systemctl daemon-reload'
done

2B.5 修复3:重写 slurmctld.service

文件路径:slurmctld.service

关键变更:

[Unit]
# P0修复3:添加 slurmdbd 依赖(slurmctld 依赖 slurmdbd 做记账)
After=network-online.target munge.service slurmdbd.service
Wants=network-online.target slurmdbd.service

[Service]
# P0修复2:增强重启策略
Restart=always
RestartSec=10

2B.6 修复4:健康检查服务超时调整

# slurm-cluster-health.service & slurm-cluster-health-bootstrap.service
# P0修复4:120 → 300 秒
TimeoutStartSec=300

2B.7 修复5:slurm.conf 配置优化

# 在控制节点执行(修改后 configless 模式自动分发到计算节点)
ssh root@192.168.0.2 '
# P1-5: ReturnToService=1 → 2
sed -i "s/^ReturnToService=1/ReturnToService=2/" /usr/local/slurm/etc/slurm.conf
# P1-6: JobAcctGatherType=none → cgroup
sed -i "s|^JobAcctGatherType=jobacct_gather/none|JobAcctGatherType=jobacct_gather/cgroup|" /usr/local/slurm/etc/slurm.conf
# P1-7: JobCompType=none → slurmdbd
sed -i "s|^JobCompType=jobcomp/none|JobCompType=jobcomp/slurmdbd|" /usr/local/slurm/etc/slurm.conf
# P2-8: DefMemPerCPU=100 → 1024
sed -i "s/^DefMemPerCPU=100/DefMemPerCPU=1024/" /usr/local/slurm/etc/slurm.conf
'

2B.8 服务重启顺序(依赖链)

⚠️ 必须按依赖顺序重启:slurmdbd → slurmctld → slurmd → 健康检查服务

# 1. 重启 slurmdbd(最底层依赖)
ssh root@192.168.0.2 'systemctl restart slurmdbd; sleep 5; systemctl is-active slurmdbd'

# 2. 重启 slurmctld(等 slurmdbd 就绪后)
ssh root@192.168.0.2 'systemctl restart slurmctld; sleep 5; systemctl is-active slurmctld'

# 3. 验证 slurmctld 就绪
ssh root@192.168.0.2 'scontrol ping'
# 期望: Slurmctld(primary) at k8s-master-01 is UP

# 4. 重启所有 slurmd(wait-for-slurmctld.sh 会先等待 slurmctld 就绪)
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} 'systemctl restart slurmd'
done
sleep 20

# 5. 重启健康检查服务
ssh root@192.168.0.2 'systemctl restart slurm-cluster-health.timer slurm-cluster-health-bootstrap.service'

2B.9 验证 Restart=always 自动恢复

# 模拟 slurmd 崩溃,验证自动恢复
ssh root@k8s-node-03 'pkill -9 -f "slurmd --conf-server"'
sleep 15
ssh root@k8s-node-03 'systemctl is-active slurmd'
# 期望: active(Restart=always 自动恢复)

# 查看恢复日志
ssh root@k8s-node-03 'journalctl -u slurmd --since "20 sec ago" --no-pager | tail -10'
# 期望: 看到 "scheduling restart" → "wait-for-slurmctld.sh" → "slurmd started"

三、验证清单

部署完成后按以下顺序验证:

# 验证项 命令 期望输出
1 slurm.conf 配置 ssh root@192.168.0.2 'grep "^HealthCheck" /usr/local/slurm/etc/slurm.conf' 3 行配置
2 配置分发同步 ssh root@k8s-node-01 'grep -c "^HealthCheck" /var/spool/slurmd/conf-cache/slurm.conf' 3
3 脚本部署 ssh root@k8s-node-01 'test -x /usr/local/bin/slurm_health_check.sh && echo OK' OK
4 脚本手动执行 ssh root@k8s-node-01 '/usr/local/bin/slurm_health_check.sh; echo $?' 退出码 0
5 slurmd 启动检查 ssh root@k8s-node-01 'journalctl -u slurmd --since "5 min ago" \| grep HealthCheck' HealthCheck 通过
6 集群节点状态 ssh root@192.168.0.2 'sinfo' 3 节点 idle
7 自研 timer 频率 ssh root@192.168.0.2 'systemctl list-timers slurm-cluster-health.timer' NEXT 15 分钟后
8 Restart=always ssh root@k8s-node-03 'systemctl show slurmd -p Restart' Restart=always
9 wait-for-slurmctld ssh root@k8s-node-01 'journalctl -u slurmd --since "5 min ago" \| grep 就绪' slurmctld 已就绪
10 slurmctld 依赖 ssh root@192.168.0.2 'systemctl show slurmctld -p After \| grep slurmdbd' 包含 slurmdbd.service
11 ReturnToService ssh root@192.168.0.2 'grep ReturnToService /usr/local/slurm/etc/slurm.conf' ReturnToService=2
12 JobAcctGather ssh root@192.168.0.2 'grep JobAcctGatherType /usr/local/slurm/etc/slurm.conf' jobacct_gather/cgroup
13 崩溃恢复测试 ssh root@k8s-node-03 'pkill -9 slurmd; sleep 15; systemctl is-active slurmd' active

四、自研方案保留策略

4.1 保留原因

自研方案 cluster_health_check.py 仍有不可替代的价值:

能力 内置 HealthCheck 自研方案 结论
本地健康检测 ✅ ✅(SSH 远程) 内置更优
节点自动 DRAIN ✅ ❌ 内置更优
重启异常 slurmd 服务 ❌ ✅ 自研不可替代
引导模式(重启后节点恢复) ❌ ✅ 自研不可替代
munge 服务前置检查 ❌ ✅ 自研更优

4.2 职责重新划分

内置 HealthCheck(主,5 分钟周期):
  → 检测节点不健康 → 自动 DRAIN(防止作业分配到故障节点)

自研 cluster_health_check.py(备,15 分钟周期):
  → SSH 远程检查 slurmd 服务状态
  → 自动重启异常 slurmd 服务(内置机制无法做到)
  → 引导模式:集群重启后等待节点就绪并恢复状态

4.3 配置变更

配置项 改造前 改造后
slurm-cluster-health.timer OnUnitActiveSec 5min 15min
定位 主健康检查 备份巡检层

五、故障排查

5.0 通用排障流程(故障现场标准 SOP,建议按顺序执行)

适用范围:任何节点 DOWN / 巡检不触发 / 服务异常的场景。以下 6 步设计为通用闭环,避免遗漏关键证据。

Step 1:现象定位(1 分钟)

# === 快速三件套(在控制节点 k8s-master-01 执行) ===
# 1. 集群节点状态(是否 down/drain/down* Not responding)
export PATH=/usr/local/slurm/bin:$PATH; sinfo -Nl

# 2. 自研备份巡检服务状态(是否 enabled、NEXT 是否在未来、最近 LAST 执行结果)
systemctl status slurm-cluster-health.timer slurm-cluster-health.service slurm-cluster-health-bootstrap.service --no-pager

# 3. 所有节点 slurmd / munge 存活
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    echo -n "$node slurmd="; ssh -o ConnectTimeout=3 root@${node} 'systemctl is-active slurmd' 2>&1
    echo -n "$node munge ="; ssh -o ConnectTimeout=3 root@${node} 'systemctl is-active munge' 2>&1
done

Step 2:日志抓取(2 分钟,按层级抓,限制行数避免刷屏)

# === 控制节点 ===
journalctl -u slurm-cluster-health.service --no-pager -n 80 | tail -60    # 自研巡检脚本最近日志
journalctl -u slurmctld --no-pager -n 40 | tail -30                         # slurmctld 调度层日志

# === 问题节点(例:k8s-node-03,按需替换)===
ssh root@k8s-node-03 "
    journalctl -u slurmd --no-pager -n 60 | tail -40                            # slurmd 启动 / HealthCheck 日志
    systemctl status slurmd 2>&1 | head -20                                      # 当前服务状态 + Main PID
    journalctl -u slurm-cluster-health-bootstrap.service --no-pager -n 20 2>/dev/null   # 若启动过引导服务
"

Step 3:根因快速判断(按优先级命中表)

采集到的现象 对应故障条目 快速修复入口
slurmd activating 超过 30 秒 + Main PID 为空 5.3.1 Type 与 -D 参数矛盾 应用 slurmd.service 标准 Type=simple 配置
slurmd inactive 但进程不在,且几小时未重启 5.3.1 + 2B.2 P0-2 Restart=always systemctl restart slurmd + 检查 Restart=always
timer NEXT 在过去时间(不继续调度) 5.3.2 RemainAfterExit=yes RemainAfterExit=no + daemon-reload
服务日志「start operation timed out. Terminating」SIGTERM 5.3.3 TimeoutStartSec 不足 巡检 300s / slurmd 180s
systemd 报 Unknown lvalue 'StartLimitIntervalSec' 5.3.4 CentOS 7 systemd 219 不兼容 删除 StartLimit* 两参数
3 节点 down* Not responding + wait-for-slurmctld 报超时 5.4 configless + scontrol ping 逻辑缺陷 替换 wait-for-slurmctld.sh 修复版
HealthCheck 不触发 / 节点未自动 DRAIN 5.1 / 5.2 查 conf-cache + 脚本权限
configless 配置未同步到计算节点 5.3 configless restart slurmd 强制重拉

Step 4:修复执行

按 Step3 命中的条目执行标准修复。批量修改 3 计算节点前,必须先执行 6.4 的强制备份。

Step 5:修复验证(必须 5 项全部通过)

# ✅ 1. 集群所有节点 idle
sinfo | awk 'NR>1 && !/idle|PARTITION/' | wc -l   # 应为 0(非 idle 的行)

# ✅ 2. 所有 slurmd 服务 active + Main PID 指向 slurmd 进程
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    echo -n "$node: "; ssh root@${node} 'systemctl is-active slurmd'  # 全部 active
done

# ✅ 3. 内置 HealthCheck 已部署(退出码 0)
ssh root@k8s-node-02 '/usr/local/bin/slurm_health_check.sh; echo EXIT=$?'  # EXIT=0

# ✅ 4. 自研备份巡检 timer NEXT 在未来
ssh root@k8s-master-01 'systemctl list-timers slurm-cluster-health.timer --no-pager'
# LEFT 列为正分钟数(LAST 与 NEXT 差距 15 分钟)

# ✅ 5. 崩溃恢复抽查(可选:杀掉 slurmd 验证 systemd Restart=always)
ssh root@k8s-node-03 'pkill -9 -f "slurmd --conf-server"; sleep 15; systemctl is-active slurmd'
# 应为 active(Restart=always 自动重启成功)

Step 6:预防措施(避免重复踩坑)

  • 所有 systemd unit 变更后 必须 systemctl daemon-reload。
  • 修改 slurmd.service 后 必须在至少 1 节点验证 Main PID 正确指向 slurmd 进程(Type=simple + -D 组合必须同时成立)。
  • 批量改动完成后,必须完整执行 Step 5 的 5 项验证,而不是只看 sinfo 一行。
  • 所有计算节点的 StartLimitIntervalSec / StartLimitBurst 两参数绝对不要写(CentOS 7 systemd 219 不支持,会直接导致 unit 加载失败)。

5.1 HealthCheck 不触发

# 步骤 1: 确认配置已同步到计算节点
ssh root@k8s-node-01 'grep "^HealthCheck" /var/spool/slurmd/conf-cache/slurm.conf'
# 无输出 → 配置未分发,重启 slurmd: systemctl restart slurmd

# 步骤 2: 确认脚本存在且可执行
ssh root@k8s-node-01 'ls -l /usr/local/bin/slurm_health_check.sh'
# 不存在 → 重新部署脚本

# 步骤 3: 手动执行脚本排查
ssh root@k8s-node-01 '/usr/local/bin/slurm_health_check.sh 2>&1; echo $?'
# 非0退出 → 查看具体报错,修复后重新执行

# 步骤 4: 检查 slurmd 日志
ssh root@k8s-node-01 'journalctl -u slurmd --since "10 min ago" | grep -i health'

5.2 节点未自动 DRAIN

# 步骤 1: 确认 HealthCheckNodeState 配置
ssh root@192.168.0.2 'grep HealthCheckNodeState /usr/local/slurm/etc/slurm.conf'
# 应为 ANY(所有状态都检查)

# 步骤 2: 手动模拟故障验证
ssh root@k8s-node-03 'echo "exit 1" > /usr/local/bin/slurm_health_check.sh && chmod +x /usr/local/bin/slurm_health_check.sh'
# 等待 5 分钟(HealthCheckInterval=300)或重启 slurmd 触发
ssh root@k8s-node-03 'systemctl restart slurmd'
sleep 10
ssh root@192.168.0.2 'sinfo'
# 节点应变为 drain

# 步骤 3: 恢复脚本
scp /home/rian/Documents/slurm/slurm_health_check.sh root@k8s-node-03:/usr/local/bin/
ssh root@192.168.0.2 'scontrol update nodename=k8s-node-03 state=resume'

5.3 configless 配置未同步

# 检查 slurmd 是否使用 conf-server 模式
ssh root@k8s-node-01 'ps aux | grep slurmd | grep -v grep'
# 期望: 包含 --conf-server k8s-master-01:6817

# 检查配置缓存目录
ssh root@k8s-node-01 'ls /var/spool/slurmd/conf-cache/'
# 期望: slurm.conf cgroup.conf

# 强制重新拉取配置
ssh root@k8s-node-01 'systemctl restart slurmd'
sleep 5
ssh root@k8s-node-01 'grep HealthCheck /var/spool/slurmd/conf-cache/slurm.conf'

5.3.1 slurmd 卡 activating 不重启(Type 与 -D 参数矛盾)

历史问题:自研方案 v0.1~v0.2 时代的典型故障(2026-08-14 v0.2 修复),现 cluster_health_check.py 重启 slurmd 时仍可能遇到早期遗留配置。

现象:

sinfo:节点 down* / idle*
systemctl status slurmd:Active: activating (start) 超过 30 秒,Main PID 为空
但 systemd 不触发 Restart=on-failure,服务长期卡死 activating

根因:Type=forking 与 -D 前台运行冲突。

配置项 实际行为 systemd 期望 矛盾结果
Type=forking slurmd 被 fork 后父进程退出、子进程守护 systemd 等父进程退出才标记 active ✅ 匹配(但我们用了 -D)
ExecStart=... -D -D 强制 slurmd 前台运行,不 fork systemd 一直等父进程退出,永远等不到 ❌ 矛盾

后果链:

slurmd 启动 → systemd 看进程在前台 → 状态卡在 activating (start)
  → 若 slurmd 因 munge 未就绪退出
    → systemd 认为 "启动中 activating" 而非 "failed" → Restart=on-failure 不触发 → 节点永久 down

标准修复(当前规范,已固化到 slurmd.service):

# 关键原则:Type=simple 必须配合 -D(前台运行),二者缺一不可
[Service]
Type=simple                                           # systemd 直接追踪主进程
ExecStart=/usr/local/slurm/sbin/slurmd \
  --conf-server k8s-master-01:6817 -D -s $SLURMD_OPTIONS
Restart=always                                        # 任何退出都自动重启(P0-2 强化)
RestartSec=10

5.3.2 巡检 timer 执行一次后不再触发(RemainAfterExit=yes)

历史问题:自研 v0.3 之前 slurm-cluster-health.service 误配了 RemainAfterExit=yes,导致 timer 只跑一次就停摆。

现象:

systemctl list-timers slurm-cluster-health.timer
NEXT 显示为过去时间(如 "19min ago"),说明 timer 不再调度
手动 systemctl start slurm-cluster-health.service 能正常执行完

根因:Type=oneshot + RemainAfterExit=yes 组合会让脚本退出后服务永久保持 active (exited),定时器检测到"服务还在 active"就不触发下一次 OnUnitActiveSec。

标准修复(已固化到 slurm-cluster-health.service):

[Service]
Type=oneshot
ExecStart=/root/.pyenv/versions/3.9.6/bin/python3 /usr/local/bin/cluster_health_check.py --fix
TimeoutStartSec=300
RemainAfterExit=no     # ✅ 执行完毕服务状态变 inactive,timer 才能继续触发
# RemainAfterExit=yes  # ❌ 错误,仅允许 bootstrap 一次性服务使用

注意:slurm-cluster-health-bootstrap.service(开机只执行一次的引导服务)必须保留 RemainAfterExit=yes,因为它不需要重复触发,保持 active (exited) 代表"已执行过引导逻辑"的标记位。

部署验证:

systemctl daemon-reload
systemctl reset-failed slurm-cluster-health.service
systemctl stop slurm-cluster-health.service
systemctl start slurm-cluster-health.service
sleep 5
systemctl list-timers slurm-cluster-health.timer --no-pager
# NEXT 应显示:LAST + 15 分钟(未来时间)✅

5.3.3 引导服务 / 巡检服务启动超时被杀(TimeoutStartSec 不足)

历史问题:自研方案早期引导模式串行等待每个节点就绪 60 秒 × 3 节点 = 最多 180 秒,但 TimeoutStartSec=60 或 120,结果服务中途被 SIGTERM。

现象:

journalctl -u slurm-cluster-health.service -n 30
... start operation timed out. Terminating.
... code=killed, status=15/TERM

根因对比:

版本 引导逻辑 TimeoutStartSec 结论
旧版 串行等待:每节点就绪 60s × 3 节点 = 最多 180s 60 / 120 ❌ 超时被杀
新版 _ensure_slurmd_running 先确保所有 slurmd 启动,再并行等待 SLURM 内部同步(单节点≤30s)+ wait-for-slurmctld 前置等待 300(slurm-cluster-health.service / bootstrap) ✅ 足够
配套 slurmd 启动前 ExecStartPre=wait-for-slurmctld.sh 先等 slurmctld 就绪,避免启动时就拉不到 config slurmd TimeoutStartSec=180 ✅

标准修复(已固化到各 service):

# slurm-cluster-health.service & slurm-cluster-health-bootstrap.service
TimeoutStartSec=300          # P0 修复,从 120 → 300 秒
# slurmd.service
TimeoutStartSec=180          # 预留 ExecStartPre=wait-for-slurmctld.sh 60s + slurmd 启动

5.3.4 CentOS 7 systemd 报 Unknown lvalue StartLimitIntervalSec/Burst

历史问题:早期在 slurmd.service 里误用了 systemd 230+ 才引入的新参数。

现象:

systemctl status slurmd
Unknown lvalue 'StartLimitIntervalSec' in section 'Service'
Unknown lvalue 'StartLimitBurst' in section 'Service'

根因:CentOS 7 / RHEL 7 的 systemd 版本为 219,不支持以下 230+ 引入的参数: - StartLimitIntervalSec - StartLimitBurst

标准修复:从 unit 中删除这两行,使用基础重启策略即可:

# 删除以下两行:
# StartLimitIntervalSec=60
# StartLimitBurst=3

# 保留基础重启策略(已足够)
Restart=always
RestartSec=10

如需全局重启频率限制,可通过 /etc/systemd/system.conf 全局配置 DefaultStartLimitIntervalSec= / DefaultStartLimitBurst=,或交给健康巡检脚本处理。


5.4 冷开机重启后节点仍 DOWN 且巡检不生效(2026-08-27 实际案例)

场景:宿主机(KVM)冷开机重启后,sinfo -Nl 显示所有计算节点 down* Not responding,slurm-cluster-health.timer 虽然已 enabled 且 cluster_health_check.py --fix 引导模式反复运行,但节点状态始终无法恢复,用户感知为「巡检没生效」。 适用条件:configless 模式(计算节点无本地 slurm.conf,slurmd 以 --conf-server k8s-master-01:6817 启动)。

5.4.1 故障现象 & 快速判断

采集项 命令(在 k8s-master-01 上执行) 故障时典型结果
节点状态 export PATH=/usr/local/slurm/bin:$PATH; sinfo -Nl 3 节点 STATE=down* REASON=Not responding
计算节点 slurmd 状态 ssh root@k8s-node-01 'systemctl is-active slurmd; journalctl -u slurmd -n 20 \| grep -E "等待.*超时\|exit.*status=1"' activating,且 wait-for-slurmctld.sh 报「等待 slurmctld 就绪超时(60 秒)」
计算节点 scontrol ping 报错 ssh root@k8s-node-01 '/usr/local/slurm/bin/scontrol ping 2>&1; echo EXIT=$?' fatal: Could not establish a configuration source,EXIT=1(configless 节点天然如此,不是故障)
巡检引导模式日志 ournalctl -u slurm-cluster-health.service -n 60 \| grep -E "启动失败\|slurmd.*异常\|activating" 反复报「节点 k8s-node-xx slurmd 服务启动失败」

5.4.2 根因(一句话原理)

ExecStartPre=wait-for-slurmctld.sh 的逻辑缺陷:configless 计算节点没有本地 slurm.conf,scontrol ping 必然失败(上面 EXIT=1 的报错是 configless 天然特性)。但旧脚本要求「TCP 6817 端口通并且 scontrol ping 通过」才算 slurmctld 就绪,结果永远判定失败 → 60 秒超时 EXIT=1 → slurmd 永远无法启动 → slurmctld 300 秒未收到心跳 → 全节点 down* Not responding → 即便巡检脚本反复重启 slurmd 也无济于事(上游 ExecStartPre 卡死)。

5.4.3 修复 SOP(可直接复制执行)

# ============================================================
# 环境变量(按需替换)
SRC_SCRIPT="/home/rian/Documents/slurm/slurm_health_ensure/wait-for-slurmctld.sh"
NODES=("192.168.0.5:k8s-node-01" "192.168.0.6:k8s-node-02" "192.168.0.7:k8s-node-03")

# ---------- 步骤 1:备份旧脚本 + 推送修复版 ----------
# 修复要点:TCP 6817 连通即视为就绪(configless 模式 slurmd 只要 TCP 通就能 --conf-server 拉配置),
#           不再强求 scontrol ping 通过。
for pair in "${NODES[@]}"; do
    IP="${pair%%:*}"
    NAME="${pair##*:}"
    echo "━━━ $NAME ($IP) ━━━━━"
    # 1.1 计算节点原脚本备份
    ssh -o StrictHostKeyChecking=no "root@${IP}" '
        cp /usr/local/bin/wait-for-slurmctld.sh \
           /usr/local/bin/wait-for-slurmctld.sh.bak.$(date +%Y%m%d_%H%M%S)
    '
    # 1.2 推送修复后脚本 + 赋权 + 停止卡住的 ExecStartPre + 重启 slurmd
    scp -o StrictHostKeyChecking=no "$SRC_SCRIPT" "root@${IP}:/usr/local/bin/wait-for-slurmctld.sh"
    ssh -o StrictHostKeyChecking=no "root@${IP}" '
        chmod +x /usr/local/bin/wait-for-slurmctld.sh
        systemctl daemon-reload
        systemctl stop slurmd
        pkill -f wait-for-slurmctld.sh || true
        sleep 1
        systemctl start slurmd
        sleep 3
        echo "slurmd 状态: $(systemctl is-active slurmd)"
    '
done

# ---------- 步骤 2:验证修复生效 ----------
echo ""
echo "=== 等待 20 秒让 slurmd 注册心跳 + ReturnToService=2 自动置 IDLE ==="
sleep 20
export PATH="/usr/local/slurm/bin:$PATH"
sinfo -Nl
echo ""
# 3 节点 STATE=idle + REASON=none → 修复完成

修复版本的脚本(已同步更新到项目归档 wait-for-slurmctld.sh)的核心逻辑: bash if scontrol ping 通过; then exit 0; fi # 非 configless 节点(有 slurm.conf)走这条 if TCP 连通 k8s-master-01:6817; then sleep 1; exit 0 # ✅ configless 节点走这条(TCP 通即拉配置) fi

5.4.4 修复验证清单(Pre-fix vs Post-fix)

验证项 修复前 修复后(合格判据)
sinfo -Nl 状态 3 节点 down* Not responding 3 节点 idle none
systemctl is-active slurmd(3 节点) activating 或 failed active,且 journalctl -u slurmd \| grep "slurmd started" 有启动日志
bash /usr/local/bin/wait-for-slurmctld.sh k8s-master-01 6817 EXIT=1(60 秒超时) EXIT=0,1~3 秒返回(TCP 连通即通过)
cluster_health_check.py 引导模式 反复报 slurmd 启动失败 日志显示 3/3 节点就绪(状态: idle),服务变 inactive 正常退出
systemctl list-timers slurm-cluster-health.timer LAST 与 NEXT 差距正常 NEXT 时间与 LAST 相差 15 分钟(OnUnitActiveSec=15min,作为备份巡检层补充),下一轮巡检会自动运行

六、回滚方案

6.1 回滚 HealthCheck 配置

# 恢复 slurm.conf(从备份)
ssh root@k8s-master-01 '
    BACKUP=$(ls -t /usr/local/slurm/etc/slurm.conf.bak.* | head -1)
    cp $BACKUP /usr/local/slurm/etc/slurm.conf
    systemctl restart slurmctld
'
# 重启所有 slurmd 使旧配置生效
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} 'systemctl restart slurmd'
done

6.2 回滚自研 timer 频率

# 恢复为 5 分钟
ssh root@k8s-master-01 '
    sed -i "s/OnUnitActiveSec=15min/OnUnitActiveSec=5min/" /etc/systemd/system/slurm-cluster-health.timer
    systemctl daemon-reload
    systemctl restart slurm-cluster-health.timer
'

6.3 删除 HealthCheck 脚本(可选)

for node in k8s-master-01 k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} 'rm -f /usr/local/bin/slurm_health_check.sh'
done

6.4 批量修改计算节点配置强制备份(必须执行)

高危警示:任何批量推送 slurmd.service / wait-for-slurmctld.sh / slurm_health_check.sh 之前,必须先对 3 个计算节点做本地备份,便于回滚。

# ⚠️ 强制备份(循环变量按需扩展)
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} '
        cp /etc/systemd/system/slurmd.service \
          /etc/systemd/system/slurmd.service.bak.$(date +%Y%m%d%H%M%S)
        [ -f /usr/local/bin/wait-for-slurmctld.sh ] && \
          cp /usr/local/bin/wait-for-slurmctld.sh \
             /usr/local/bin/wait-for-slurmctld.sh.bak.$(date +%Y%m%d%H%M%S)
        [ -f /usr/local/bin/slurm_health_check.sh ] && \
          cp /usr/local/bin/slurm_health_check.sh \
             /usr/local/bin/slurm_health_check.sh.bak.$(date +%Y%m%d%H%M%S)
    '
    echo "✅ $node 备份完成"
done

# ↩️ 一键回滚(如果修复失败,按 ls -t 取最新备份恢复)
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} '
        BACKUP=$(ls -t /etc/systemd/system/slurmd.service.bak.* 2>/dev/null | head -1)
        [ -n "$BACKUP" ] && cp $BACKUP /etc/systemd/system/slurmd.service
        BACKUP=$(ls -t /usr/local/bin/wait-for-slurmctld.sh.bak.* 2>/dev/null | head -1)
        [ -n "$BACKUP" ] && cp $BACKUP /usr/local/bin/wait-for-slurmctld.sh
        systemctl daemon-reload
        systemctl restart slurmd
        sleep 3
        echo "$(hostname): slurmd=$(systemctl is-active slurmd)"
    '
done

6.5 systemd 服务卡死紧急清理(reset-failed / 激活态清理)

当服务处于 failed / activating 卡住 / 「Main PID exited but state still activating」死锁时:

# ================
# 适用:slurmd / slurm-cluster-health 服务
# ================
UNIT="slurmd.service"                          # 按需替换

systemctl stop $UNIT                            # 1. 先尝试正常停
systemctl reset-failed $UNIT                    # 2. 清失败计数(关键!否则 Restart 计数器可能被锁)
systemctl daemon-reload                         # 3. 如果改了 unit 文件
systemctl start $UNIT                           # 4. 重新启动

# 特殊:slurmd 被 ExecStartPre 卡住(wait-for-slurmctld.sh 进程残留在后台)
ssh root@k8s-node-01 "
    systemctl stop slurmd
    pkill -f wait-for-slurmctld.sh || true      # 清理 ExecStartPre 残留子进程
    systemctl reset-failed slurmd
    systemctl daemon-reload
    systemctl start slurmd
    sleep 3
    systemctl is-active slurmd
"

6.6 节点 DOWN 手动恢复(三层自动恢复全部失效时的兜底)

当第 1 层 ReturnToService=2 + 第 2 层自研巡检 15 分钟都没把节点救回来时,手动干预:

# 步骤 1:先修底层(确保 3 节点 slurmd active)
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} "
        systemctl restart slurmd
        sleep 5
        systemctl is-active slurmd
    "
done

# 步骤 2:在控制节点强制将 DOWN / DRAIN 节点置为 resume
export PATH=/usr/local/slurm/bin:$PATH
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    scontrol update nodename=${node} state=resume reason="manual recovery ops"
done

# 步骤 3:20 秒后验证
sleep 20
sinfo -Nl
# 期望:3 节点 STATE=idle, REASON=none

七、日常运维速查

# === 查看 HealthCheck 配置 ===
ssh root@192.168.0.2 'grep HealthCheck /usr/local/slurm/etc/slurm.conf'

# === 查看各节点最近 HealthCheck 执行记录 ===
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    echo "=== ${node} ==="
    ssh root@${node} 'journalctl -u slurmd --since "10 min ago" --no-pager | grep HealthCheck | tail -1'
done

# === 手动触发 HealthCheck(重启 slurmd 即触发 REGISTER 检查)===
ssh root@k8s-node-01 'systemctl restart slurmd'

# === 查看节点 DRAIN 原因 ===
ssh root@192.168.0.2 'sinfo -o "%N %T %E"'

# === 手动恢复 DRAIN 节点 ===
ssh root@192.168.0.2 'scontrol update nodename=k8s-node-03 state=resume reason="manual recovery"'

# === 查看自研备份巡检状态 ===
ssh root@192.168.0.2 'systemctl list-timers slurm-cluster-health.timer'
ssh root@192.168.0.2 'journalctl -u slurm-cluster-health.service --since "1 hour ago" --no-pager | tail -20'

# === 查看 SLURM 集群状态 ===
ssh root@192.168.0.2 'sinfo; sinfo -o "%N %T %C %m %e"'

# === 查看所有服务状态 ===
ssh root@192.168.0.2 'systemctl is-active slurmctld'
for node in k8s-node-01 k8s-node-02 k8s-node-03; do
    ssh root@${node} 'systemctl is-active slurmd munge'
done

附录:项目文件清单

文件 用途
SLURM_HEALTHCHECK_STANDARD_SOP.md 本文档(行业标准 HealthCheck SOP)
slurm_health_check.sh SLURM 内置 HealthCheck 脚本(行业标准)
wait-for-slurmctld.sh configless 模式启动前等待 slurmctld 就绪脚本(P0修复)
slurmd.service 计算节点 slurmd 服务配置(含 ExecStartPre + Restart=always)
slurmctld.service 控制节点 slurmctld 服务配置(含 slurmdbd 依赖)
slurm-cluster-health.timer 自研备份巡检定时器(已降频至 15 分钟)
slurm-cluster-health.service 自研备份巡检服务(超时调整为 300 秒)
slurm-cluster-health-bootstrap.service 启动引导服务(超时调整为 300 秒)
cluster_health_check.py 自研健康检查脚本(备份巡检层,保留 15min 频率)
SLURM_CLUSTER_HEALTH_SOP.md ~~自研方案历史 SOP(参考)~~ → 已合并至本文档,源文件及旧 path.bak 已清理
SLURM_PROMETHEUS_GRAFANA_SUMMARY.md Prometheus+Grafana 监控 SOP(第 3 层告警)

变更记录

日期 版本 变更内容
2026-08-13 v0.1 自研方案首版部署:cluster_health_check.py 首版 + 引导模式标记机制 + slurm-cluster-health.timer(5 分钟频率,主健康检查角色)
2026-08-14 v0.2 修复 slurmd.service:明确 Type=simple 必须配合 -D 前台运行,解决 activating 卡死、Restart 不触发问题(对应 5.3.1 历史问题)
2026-08-14 v0.3 修复 slurm-cluster-health.service:RemainAfterExit=no,解决 oneshot+yes 导致 timer 只跑一次就停摆(对应 5.3.2 历史问题)
2026-08-14 v0.4 优化 引导模式逻辑:_ensure_slurmd_running + 并行 SLURM 状态同步,TimeoutStartSec=120(对应 5.3.3 历史问题旧版,后续 P0-4 升级至 300)
2026-08-14 v1.0 部署 SLURM 内置 HealthCheck(行业标准),自研方案降频至 15 分钟作为备份巡检层
2026-08-14 v2.0 P0 严重逻辑错误修复:4个P0(slurmd等待slurmctld+Restart=always+slurmdbd依赖+超时调整)+ 4个P1(ReturnToService/JobAcct/JobComp/DefMem)+ 1个P2(RestartSec)
2026-08-27 v2.1 ① 合并自研方案历史 SOP(新增 5.0 通用排障 Step1~6 + 5.3.1~5.3.4 四条历史问题条目 + 6.4~6.6 高危操作警示);② 修正与实机不符值:1.3 三层体系 ReturnToService=2、5.4.4 备份巡检 timer 15min;③ 清理重复的「〇、快速索引」;④ 追加 8-13~8-14 自研 v0.x 早期演进记录;⑤ 5.4 节记录 2026-08-27 冷开机 configless + wait-for-slurmctld.sh 逻辑缺陷的实际修复案例

来源:/home/rain/Documents/slurm/slurm_ins/slurm_health_ensure/SLURM_HEALTHCHECK_STANDARD_SOP.md | 最后更新:2026-08-27 19:34(由 build/sync.py 同步,请勿在站点侧修改)