跳转至

SOP — Epilog 覆盖 Cadence X 显示残留(孤儿 X socket / clsbd 清理)

主题目录:slurm_ins/epilog_xdisplay_cleanup_20261005/ 变更日期:2026-10-05 执行方式:部署器下发(--scratch-gc) 状态:已完成并验证

1. 背景

HANDOFF 第 3 节 #14 登记:上层 ADE-XL 经 Slurm 派发时,ICRP child 反复撞「:80 被占」而卡死。

根因:runICRP<N> 会启动 cdsVncserver :80,X server 异常退出后留下三类不带 jobid 的残留:

  • /tmp/.X11-unix/X80(X socket)
  • /tmp/.X80-lock(X lock)
  • /tmp/clsbd.<user|unknownUser>.<pid>(Cadence 客户端残留)

而 epilog_eda.sh 原有的 /tmp 清理只按 slurm<jobid>* / vcs*<jobid>* 模式匹配,文件名不带 jobid 的残留一律清不到——即「钩子在跑 ≠ 生效」。

实测(变更前):slurm-work-01(.18) 留有 2026-10-04 04:15 的孤儿 socket X80(无 lock、无 X 进程)与 15 个 clsbd 残留;slurm-work-02(.19) 有 2 个 clsbd;slurm-work-03(.20) 干净。

2. 目标

  1. epilog_eda.sh 增加「孤儿 X 显示 / clsbd」清理段:仅在无对应进程时删;
  2. 清理异常绝不影响 Epilog 退出码(非 0 会 DRAIN 节点);
  3. 顺手清掉三台节点上的当前残留;
  4. 全程幂等:复跑第二次零变更、全跳过。

3. 前置条件

项 值
执行机 部署机(本项目根,slurm_ins/.venv)
远端身份 root(免密,见 config/slurm_deploy.yaml 的 ssh 段)
目标节点 slurm-work-01/02/03 = 192.168.31.18/19/20(仅计算节点跑 slurmd、执行 Epilog)
判定工具 节点需有 fuser(实测 3 台均有 /usr/sbin/fuser);无则退 ss;两者皆无 → 保守保留
前置约束 squeue 为空再动手(--scratch-gc 不碰 daemon,但仍建议)
配置源 config/slurm_deploy.yaml 的 scratch_gc 段(hook_source_dir / hook_scripts / install_dir)

4. 影响范围

  • 涉及主机:192.168.31.18 / .19 / .20(3 台计算节点);不涉及控制节点、提交节点。
  • 涉及组件:/usr/local/slurm/epilog_eda.sh(作业 Epilog 钩子内容)。
  • 是否停服:否。钩子脚本内容变更即时生效,无需重启 slurmd、不改 slurm.conf。
  • 窗口期:无特殊要求;作业结束触发,单次开销为若干 stat + fuser(毫秒级)。
  • 在跑作业:条件允许时等 squeue 为空;Epilog 只在本作业结束时执行,不影响正在运行的作业。

5. 分步操作

实现体:.venv/bin/python main.py --config config/slurm_deploy.yaml --scratch-gc (scratch_gc_deployer.py + scratch_gc_assets.py;本主题只改源模板,实现体未改)

步骤 1:备份源模板

高危前置:改任何模板前先备份,失败可回原状。

TS=$(date +%Y%m%d-%H%M%S)
D=/home/rain/Documents/slurm/slurm_ins/epilog_xdisplay_cleanup_20261005
mkdir -p "$D/backup/slurm_ins/slurm_conf_old/pro_epilog"
cp -a /home/rain/Documents/slurm/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh \
      "$D/backup/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh.bak.$TS"
sha256sum "$D/backup/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh.bak.$TS"

预期:9af06fee…b6a1f(变更前版本)。台账见 backup/README.md。

步骤 2:修改源模板

在 slurm_conf_old/pro_epilog/epilog_eda.sh 第 2 段(scratch)之后、原「同步磁盘」之前插入第 3 段, 原第 3 段顺延为第 4 段。要点:

  • 三个函数:x_socket_in_use(fuser 优先、ss 兜底、都无则视为占用)、x_lock_alive(lock 内 PID 是否存活)、cleanup_stale_x(三段扫描);
  • 整段以 cleanup_stale_x || true 调用 —— 任何异常只记日志,不让 Epilog 返回非 0;
  • 禁用 set -u(原有约束不变);判定语句一律用 if,不用 cmd && continue(后者在 set -e 下有退出风险)。

注意事项与坑:

  1. cmd && continue 在 set -eo pipefail 下,当 cmd 失败会使整个 AND 列表非 0 → 触发出脚本退出。已统一改为 if cmd; then continue; fi(离线自测覆盖)。
  2. lock 编号解析:/tmp/.X84-lock 中 / 后紧跟 . 而非 X,故 不能用 ${lock##*/X}(永不命中);须用 ${lock##*/.X} 再去 -lock。此坑由离线自测抓出(首版写错,陈旧 lock 清不掉)。
  3. socket 编号解析用 ${sock##*/X}(socket 文件名是 X80,无点,正确)。

步骤 3:语法检查

bash -n /home/rain/Documents/slurm/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh && echo "syntax OK"
sha256sum /home/rain/Documents/slurm/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh

预期:syntax OK,sha 为 844715fc…fea8af(本次变更后版本)。

步骤 4:离线自测(上机前)

用沙箱路径替换 /tmp/ 后构造四类场景跑断言,代价远低于上机后才发现。判定矩阵:

场景 期望
孤儿 socket(bind 后关闭,无进程) 删除
活跃 socket(进程在 listen) 保留
孤儿 socket + lock 内 PID 存活 保留(socket 与 lock 都留)
陈旧 lock(PID 不存在、无 socket) 删除
clsbd.<user>.<存活pid> 保留
clsbd.<user>.<死pid> 删除
脚本整体退出码 0,且打印 REACHED_END

步骤 5:dry-run 预演

cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --scratch-gc --dry-run

预期:只有 3 台 epilog_eda.sh 显示「写入/更新」,task_prolog_eda.sh、slurm-scratch-gc.sh、service、timer 全部 [SKIP] 内容一致;汇总「待执行变更 7 / 幂等跳过 15」。 失败判断:出现 task_prolog_eda.sh 等其它文件的写入 → 说明误改了别的模板,停下排查;出现 unknown 状态 → 立即中止本次变更。

步骤 6:正式下发

cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --scratch-gc

预期:3 台各写入一次(批次 1/3、2/3、3/3),节点侧备份到 /var/backups/slurm/scratch_gc_<stamp>/, health_check 通过,退出码 0。 失败判断:某台写入失败 → 见第 7 章回滚;health_check 报「未达期望内容」→ 停手,按第 7 章恢复。

等价底层命令(仅用于阅读复核与应急复现;正常路径必须走部署器,以保留备份与幂等记录):

for h in 192.168.31.18 192.168.31.19 192.168.31.20; do
  scp /home/rain/Documents/slurm/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh \
      root@$h:/usr/local/slurm/epilog_eda.sh && ssh root@$h 'chmod 755 /usr/local/slurm/epilog_eda.sh'
done

步骤 7:清掉当前残留(一次性)

下发只更新文件,历史残留需执行一次钩子才会被清。以最小环境模拟一次作业结束:

for h in 192.168.31.18 192.168.31.19 192.168.31.20; do
  ssh root@$h 'bash /usr/local/slurm/epilog_eda.sh; echo "rc=$?"'
done

预期:rc=0;/var/log/slurm/epilog.log 出现 stale X display purged: / stale clsbd purged: 行。

步骤 8:幂等复跑

cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --scratch-gc

预期:待执行变更 0、幂等跳过 18、退出码 0,日志出现「状态已一致,零变更,无需部署」。

6. 验证

# ① 三台内容与源模板一致(sha 应为 844715fc…fea8af)
for h in 192.168.31.18 192.168.31.19 192.168.31.20; do
  ssh root@$h 'sha256sum /usr/local/slurm/epilog_eda.sh'
done
# ② 残留已清空(X / lock / clsbd 三项均应为空或 0)
for h in 192.168.31.18 192.168.31.19 192.168.31.20; do
  ssh root@$h 'ls /tmp/.X11-unix/X[0-9]* /tmp/.X[0-9]*-lock 2>/dev/null; ls /tmp/clsbd.* 2>/dev/null | wc -l'
done
# ③ 日志留痕
ssh root@192.168.31.18 'tail -20 /var/log/slurm/epilog.log'
# ④ 钩子退出码(最小环境复现 slurmd)
ssh root@192.168.31.18 'env -i PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
  SLURM_JOB_ID=0 SLURM_JOB_USER=root SLURM_JOB_EXIT_CODE=0 \
  bash /usr/local/slurm/epilog_eda.sh; echo EXIT=$?'

本次实测结果:

节点 变更前 变更后
slurm-work-01(.18) X80 + 15 个 clsbd 0 / 0
slurm-work-02(.19) 2 个 clsbd 0
slurm-work-03(.20) 无残留 0(脚本正常退出)

三台 sha 均为 844715fc…fea8af;脚本退出码均为 0;幂等复跑 0 变更。

7. 回滚

备份位置(两处,互为冗余):

  1. 本地:epilog_xdisplay_cleanup_20261005/backup/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh.bak.20261005-231956(sha 9af06fee…b6a1f);
  2. 节点侧:/var/backups/slurm/scratch_gc_20261005_232204/(部署器自动创建,含 epilog_eda.sh.bak 等)。

台账见本目录 backup/README.md。

回滚步骤:

D=/home/rain/Documents/slurm/slurm_ins/epilog_xdisplay_cleanup_20261005/backup/slurm_ins/slurm_conf_old/pro_epilog
cp -a "$D/epilog_eda.sh.bak.20261005-231956" \
      /home/rain/Documents/slurm/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --scratch-gc    # 重新下发回旧内容

回滚触发判据(量化,任一达标即回滚):

  • health_check 未通过(报「未达期望内容」);
  • 任一节点 sha256sum /usr/local/slurm/epilog_eda.sh ≠ 期望值;
  • journalctl -u slurmd 出现 epilog failed: rc:<非 0>,或节点转为 drain;
  • 出现误删正在使用的 X socket / clsbd(表现为在跑作业显示中断)。

回滚后验证:三台 sha 恢复 9af06fee…b6a1f;sinfo 全部 idle;提交一个测试作业确认 Epilog 正常结束。

8. 风险说明

风险 缓解
误删在用 X 显示(并发作业正在用 :80) 判定为「有进程占用即保留」(fuser 优先、ss 兜底);两个工具都没有则保守保留,宁可漏清
Epilog 返回非 0 → 节点 DRAIN 整段包成函数并以 || true 调用;set -e 下的 cmd && continue 隐患已消除;判定语句全用 if;保持禁用 set -u
清理范围越界 只处理 /tmp/.X11-unix/X[0-9]*、/tmp/.X[0-9]*-lock、/tmp/clsbd.* 三类明确模式,rm -f -- 逐项显式删除,无通配删除
PID 复用导致漏清(非误删) kill -0 只判存活;方向安全(漏清而非误删),下次作业结束会再尝试
控制节点持有陈旧副本 本变更只下发 3 台计算节点;Epilog 只在执行节点生效,控制节点副本不参与运行

附录 A:交付物清单

文件 说明
SOP.md 本文件
backup/README.md 备份台账
backup/slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh.bak.20261005-231956 源模板变更前副本

改动清单(1 个文件):

  • slurm_ins/slurm_conf_old/pro_epilog/epilog_eda.sh(sha 9af06fee…b6a1f → 844715fc…fea8af)
  • 同步更新:docs/RUNBOOK.md(新增场景 J + 修订记录)、docs/BASELINE.md(第 3 节两条机制说明)、docs/HANDOFF.md(#14 结案)

附录 B:决策经过

  • 为什么不改 slurm.conf:Epilog= 已指向 /usr/local/slurm/epilog_eda.sh,改脚本内容即生效;改 conf 会触发 scontrol reconfigure,无必要。
  • 为什么走 --scratch-gc 而非全量部署:该模式只分发钩子脚本 + 老化脚本,不碰 daemon、不重启服务;全量部署会牵动 6 台与服务重启,收益不成比例。
  • 为什么判据用 fuser 而非 lock:实测残留只有 socket、没有 lock(X server 异常退出时清掉了 lock),仅靠 lock 无法判定,必须以「有无进程占用 socket」为主判据。
  • 为什么不限定作业用户:残留文件名不带 jobid、也无法可靠关联到发起作业的用户(实测 clsbd 用户名字段是 unknownUser);限定用户会漏掉无法关联的孤儿。安全由「无进程占用」判据保证,而非用户名过滤。

来源:/home/rain/Documents/slurm/slurm_ins/epilog_xdisplay_cleanup_20261005/SOP.md | 最后更新:2026-10-05 23:23(由 build/sync.py 同步,请勿在站点侧修改)