跳转至

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. 目标

  1. 修掉 D1(渲染空壳)——否则任何"启用巡检"都只是装了个不会工作的空壳。
  2. 补齐 D2(.16/.17 → .18/.19/.20/.28 root 免密)。
  3. 只启用机制 B(巡检 + 自愈),不启用机制 A(HealthCheckProgram 失败会 DRAIN 节点 = 调度语义变更)。
  4. 在巡检中新增:mnt-lustre.mount 处于 failed 时 reset-failed + start(幂等,不动调度状态)。
  5. 故障注入验证:制造 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 同步,请勿在站点侧修改)