SOP — mnt-lustre.mount 自愈(启用健康巡检层,方案 A)¶
主题:
lustre_mount_selfheal_20261007|类型:C 类变更|状态:已落地并验证(2026-10-07) 对象:slurm_ins集群健康保障「机制 B」(控制节点巡检 + systemd timer) 关联:HANDOFF §3 #9(本项即其结案);诊断过程见同目录 README.md
1. 背景¶
2026-09-29 .18–.20 的 /mnt/lustre 因 MDS 拒连静默失败,4 天无人察觉(写延迟排查时才暴露)。
根因:客户端 fstab 带 nofail,systemd 挂载超时后 unit 落 failed 且不自动重试。
诊断(2026-10-07)发现 #9 原描述不完整——健康保障层声明了却整层未落地,且另有两个隐藏缺陷:
| # | 发现 | 证据 |
|---|---|---|
| D1 | health_assets.py 的 render_watchdog_py() / render_health_check_sh() 正则以 .*$ 结尾且带 re.S → 跨行一路吃到文件尾,把模板后半段整体替换掉 |
实测 span = (540, 24031)(EOF);渲染产物仅 1573 B 空壳(无 class/无 main())。现场 .18:/usr/local/bin/slurm_health_check.sh 恰为 1468 B,即该缺陷的历史产物 → 机制 A/B 从未真正工作过 |
| D2 | 控制节点 .16/.17 没有 root 私钥、无 known_hosts |
巡检靠 SSH 到各节点执行;实测 Permission denied (publickey) → 即使脚本正确,巡检也只会空转 |
| D3 | 机制 A/B 均未部署;slurm.conf 无 HealthCheckProgram |
配置声明 enabled/install_healthcheck/install_watchdog 均为真,现场无对应文件与 timer |
2. 目标¶
- 修掉 D1(渲染空壳)——否则任何"启用巡检"都只是装了个不会工作的空壳。
- 补齐 D2(
.16/.17→.18/.19/.20/.28root 免密)。 - 只启用机制 B(巡检 + 自愈),不启用机制 A(
HealthCheckProgram失败会 DRAIN 节点 = 调度语义变更)。 - 在巡检中新增:
mnt-lustre.mount处于failed时reset-failed+start(幂等,不动调度状态)。 - 故障注入验证:制造 failed → 巡检自动恢复。
3. 前置条件¶
- 管控机对集群各节点 root 免密 ssh(本机具备)。
- 目标节点 bash/systemd 正常;Lustre 客户端节点為
.18/.19/.20/.28。 - 无在跑作业要求:本变更不改调度状态、不重启 daemon;故障注入只在
.18做(squeue= 0 已验)。 - 备份:源文件 +
/etc/fstab/authorized_keys(见第 7 章)。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 控制节点 | .16 / .17:写 cluster_health_check.py、/etc/slurm-health/cluster.json、3 个 unit;启用 slurm-cluster-health.timer(15min) |
| 计算/提交节点 | .18–.20/.28:写 wait-for-slurmctld.sh;追加 .16/.17 公钥到 authorized_keys |
| 不改 | slurm.conf(机制 A 保持关闭)、任何 daemon 的启停、节点调度状态 |
| 停服 | 不需要;窗口任意(timer OnBootSec=3min + OnUnitActiveSec=15min) |
| 安全面 | 新增 .16/.17 → 四节点的 root 公钥登录(内网测试环境,与原 .249 已有信任同量级) |
5. 分步操作¶
步骤 1:修渲染缺陷(D1)¶
两处正则的结尾由 .*$ 改为 [^\n]*$(re.S 下 . 匹配换行,.*$ 会吞到文件尾)。
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python -m py_compile health_assets.py && echo compile_OK
.venv/bin/python -c "
import sys, yaml; sys.path.insert(0,'.')
from health_assets import HealthAssets
class L:
def info(self,*a): pass
warning=error=info
a=HealthAssets(yaml.safe_load(open('config/slurm_deploy.yaml',encoding='utf-8')),L())
for n in ('render_watchdog_py','render_health_check_sh'):
print(n, len(getattr(a,n)()))
"
预期:render_watchdog_py ≈ 24.6 k 字符(含 class / def fix_cluster / def main())、
render_health_check_sh ≈ 4.1 k。失败判断:仍只有 1.5 k → 正则未生效,勿继续。
步骤 2:新增 Lustre 挂载自愈¶
config/slurm_deploy.yaml:health_ensure.install_healthcheck: false(关机制 A); 新增lustre_unit/lustre_mount_point。health_assets.py:新增lustre_unit/lustre_mount_point/lustre_guards();cluster_json()增lustre_guards;render_watchdog_py()的配置块增LUSTRE_GUARDS = _CFG.get(...)。slurm_health_ensure/cluster_health_check.py:新增_ensure_lustre_mounts(), 并在fix_cluster()步骤 1 之后调用。
坑:自愈对象必须只含 slurm 侧(compute + submit);LSF 侧 .25–.27 不在本工程范围(归 lustre_ins)。
坑:判据必须是 systemctl is-failed = 0(真失败态)才动 —— 若用 is-active 取反会把 nofail
未配置的节点也当故障,导致反复 reset-failed。
# 渲染与自检(含新路径存在性断言)
cd /home/rain/Documents/slurm/slurm_ins && .venv/bin/python - <<'PY'
import sys, yaml
sys.path.insert(0,'.')
from health_assets import HealthAssets
class L:
def info(self,*a): pass
warning=error=info
a=HealthAssets(yaml.safe_load(open('config/slurm_deploy.yaml',encoding='utf-8')),L())
w=a.render_watchdog_py()
assert len(a.lustre_guards())==4
for t in ('class ','def fix_cluster','def _ensure_lustre_mounts','LUSTRE_GUARDS','def main()'):
assert t in w, t
print('render selftest PASS')
PY
步骤 3:dry-run 预演(必做)¶
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --health-ensure --dry-run
预期:待变更项全为机制 B(cluster_health_check.py / cluster.json / unit / 启用 timer),
不得出现 slurm.conf 或 HealthCheckProgram 相关项(出现即说明机制 A 没关掉)。
步骤 4:补齐节点间 SSH 免密(D2)¶
for s in 192.168.31.16 192.168.31.17; do
ssh root@$s "mkdir -p /root/.ssh && chmod 700 /root/.ssh; \
test -f /root/.ssh/id_ed25519 || ssh-keygen -q -t ed25519 -N '' -f /root/.ssh/id_ed25519"
done
K16=$(ssh root@192.168.31.16 'cat /root/.ssh/id_ed25519.pub')
K17=$(ssh root@192.168.31.17 'cat /root/.ssh/id_ed25519.pub')
for h in 192.168.31.18 192.168.31.19 192.168.31.20 192.168.31.28 192.168.31.16 192.168.31.17; do
ssh root@$h "mkdir -p /root/.ssh && chmod 700 /root/.ssh; touch /root/.ssh/authorized_keys; \
chmod 600 /root/.ssh/authorized_keys; \
cp -p /root/.ssh/authorized_keys /root/.ssh/authorized_keys.bak.\$(date +%Y%m%d-%H%M%S); \
grep -qF '$K16' /root/.ssh/authorized_keys || printf '%s\n' '$K16' >> /root/.ssh/authorized_keys; \
grep -qF '$K17' /root/.ssh/authorized_keys || printf '%s\n' '$K17' >> /root/.ssh/authorized_keys"
done
# 验证
for h in 18 19 20 28; do ssh root@192.168.31.16 "ssh -o BatchMode=yes -o StrictHostKeyChecking=no root@192.168.31.$h hostname"; done
坑:grep -qF 去重是幂等关键 —— 直接 >> 会在每次重跑时重复追加公钥。
注意:巡检脚本的 ssh 已带 StrictHostKeyChecking=no,故无需维护 known_hosts。
步骤 5:真实下发并启用¶
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --health-ensure
步骤 6:幂等复验(必做)¶
.venv/bin/python main.py --config config/slurm_deploy.yaml --health-ensure
预期:待执行 0 / 全跳过、rc=0。
步骤 7:巡检路径验证(健康态)¶
ssh root@192.168.31.16 'systemctl start slurm-cluster-health.service; \
grep -a "检查 Lustre" /var/log/slurm_cluster_health.log | tail -1'
预期:出现「检查 Lustre 客户端挂载状态...」,且无 ⚠️(4 台本就 active → 幂等跳过)。
步骤 8:故障注入验证(核心验收,必须在无作业窗口)¶
# ① 注入:把 .18 的 /mnt/lustre 源改为不存在路径 → unit 必失败
ssh root@192.168.31.18 "cp -p /etc/fstab /etc/fstab.bak.$(date +%Y%m%d-%H%M%S) && \
sed -i 's#^192.168.31.21@tcp:/edafs /mnt/lustre#192.168.31.21@tcp:/nonexist /mnt/lustre#' /etc/fstab && \
systemctl daemon-reload && systemctl restart mnt-lustre.mount; systemctl is-failed mnt-lustre.mount"
# ② 巡检:应见 ⚠️ 触发 + ❌ 自愈失败(源仍错)
ssh root@192.168.31.16 'systemctl start slurm-cluster-health.service'
# ③ 还原 fstab(**不手动 restart**,保持 failed 供自愈处理)
ssh root@192.168.31.18 "cp -p /etc/fstab.bak.<TS> /etc/fstab && systemctl daemon-reload; systemctl is-failed mnt-lustre.mount"
# ④ 巡检:应见 ⚠️ + ✅ 已恢复
ssh root@192.168.31.16 'systemctl start slurm-cluster-health.service'
6. 验证¶
| 项 | 结果 |
|---|---|
| 渲染缺陷修复 | render_watchdog_py 24611 字符(含 class/fix_cluster/_ensure_lustre_mounts/main)、render_health_check_sh 4081;双编译通过 |
| 离线自测 | PASS(4 条 guard + 5 个关键 token 断言) |
| dry-run | 待变更 8 全为机制 B,无 slurm.conf/HealthCheckProgram 项 |
| 真实下发 | rc=0,待执行 9 |
| 幂等复跑 | 待执行 0 / 跳过 3,rc=0 |
| 现场资产 | timer active、bootstrap enabled、cluster_health_check.py 29096 B(非空壳)、cluster.json 4 台 guard |
| SSH 免密 | .16/.17 → .18/.19/.20/.28 全部返回主机名 |
| 健康态巡检 | 新路径「检查 Lustre 客户端挂载状态...」执行;无 ⚠️(幂等跳过) |
| 故障注入① | 注入后 is-failed=failed;巡检 ⚠️ slurm-work-01: mnt-lustre.mount 处于 failed,执行自愈 + ❌ 自愈失败,需人工介入(源不可达,符合预期) |
| 故障注入② | 还原 fstab 后仍 failed;巡检 ⚠️ + ✅ slurm-work-01: /mnt/lustre 已恢复 |
| 恢复后核验 | .18 is-active=active、opts 含 noatime、touch WRITE_OK |
7. 回滚¶
备份位置:本主题 backup/(台账见 backup/README.md)
+ 远端 /root/.ssh/authorized_keys.bak.<TS>(.16/.17/.18–.20/.28)
+ 远端 /etc/fstab.bak.<TS>(.18 故障注入用)。
# A. 回滚机制 B 的部署(停 timer + 移除单元)
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --health-ensure --rollback
# B. 回滚 SSH 免密(各目标恢复 authorized_keys)
for h in 192.168.31.18 192.168.31.19 192.168.31.20 192.168.31.28 192.168.31.16 192.168.31.17; do
ssh root@$h 'cp -p /root/.ssh/authorized_keys.bak.<TS> /root/.ssh/authorized_keys'
done
# C. 回滚故障注入(若 .18 fstab 未还原)
ssh root@192.168.31.18 'cp -p /etc/fstab.bak.<TS> /etc/fstab && systemctl daemon-reload && systemctl restart mnt-lustre.mount'
回滚后验证:systemctl is-active slurm-cluster-health.timer → inactive;
.18 的 /mnt/lustre 仍 active(回滚不影响挂载本身)。
8. 风险说明¶
| 风险 | 等级 | 说明与缓解 |
|---|---|---|
自愈判据写错导致反复 reset-failed |
中 | 判据为 systemctl is-failed = 0(真失败态);健康态巡检实测无 ⚠️ |
--fix 顺带改动调度状态 |
中 | 机制 B 原就含节点恢复逻辑;本次未新增此类动作,且机制 A 保持关闭(不引入 DRAIN)。集群健康时实测无任何变更 |
| 新增 root 公钥互信 | 中 | 内网测试环境、与原 .249 已有信任同量级;authorized_keys 均已备份,可逐台恢复 |
故障注入影响 .18 的 /mnt/lustre |
中 | 注入前确认 squeue = 0;fstab 先备份;还原后由自愈恢复并实测 WRITE_OK |
| 渲染产物被再次改坏 | 中 | 已在 health_assets.py 两处加注释说明 .*$ 陷阱;离线自测含长度与 token 断言 |
附录 A:交付物清单¶
| 文件 | 说明 |
|---|---|
README.md |
诊断与方案对比(A/B) |
SOP.md |
本文件 |
backup/README.md |
备份台账 |
backup/slurm_ins/*.before.* |
health_assets.py / health_ensure_deployer.py / cluster_health_check.py / slurm_deploy.yaml 改前快照 |
dryrun.log / deploy_run1.log / deploy_run2.log |
预演与两次下发日志 |
| 实现体改动 | health_assets.py(正则修复 + lustre_guards)、config/slurm_deploy.yaml、slurm_health_ensure/cluster_health_check.py |
附录 B:本次未做的事¶
- 未启用机制 A(
HealthCheckProgram)——失败即 DRAIN,属调度语义变更,留待单独决策(用户 2026-10-07 明确选择)。 - 未改
extra_check_mounts/extra_writable_dirs:机制 A 未启用,改了不生效;待启用机制 A 时一并按当前环境填写 (/home /projects /tools /pdk /opt/modulefiles /mnt/lustre;可写/home /projects /local_scratch,只读层不得入列)。 - 未覆盖 LSF 侧
.25–.27:其/mnt/lustre失败的修复归lustre_ins§3.1 #14/#16。 - 未动
slurm_health_ensure/下的旧集群资产(其 SOP 引用k8s-node-0x//home/rian/pyenv 3.9.6):仅在cluster_health_check.py内做定点增补(新增自愈方法),未整体迁移。
来源:
/home/rain/Documents/slurm/slurm_ins/lustre_mount_selfheal_20261007/SOP.md| 最后更新:2026-10-07 03:31(由build/sync.py同步,请勿在站点侧修改)