跳转至

部署工程通用能力上移共享库与预检查扩充 — 变更实施手册(SOP)

变更对象:/home/rain/Documents/lustre_ins(Python 部署工程)与 /home/rain/py_public_modules(共享库) 变更类型:交付物变更(代码 / 配置样例 / 文档),不涉及生产服务的安装与停服 实现体为准:本文 shell 代码块仅用于阅读、复核与手工复现,不承担幂等与错误处理 运行期使用手册见 ../docs/RUNBOOK.md

目录

  1. 背景
  2. 目标
  3. 前置条件
  4. 影响范围
  5. 分步操作
  6. 验证
  7. 回滚
  8. 风险说明
  9. 附录

1. 背景

上一轮复核(对象:developing-python-deploy-scripts 技能的通用性与重复可用性)结论是: 规范侧已生效,工程落地侧只部分闭环。两个未闭环项即本次变更的触发条件:

编号 问题 证据(变更前)
G1 预检查覆盖面不足:SELinux、执行身份、系统盘可用空间、运行内核版本、LNet NID 可达性五项均无检测;EnvCheckModule 方案提出后未落地 utils/precheck.py(无对应函数)、.trae/documents/lustre-env-check-plan.md
G2 通用能力未上移共享库:预检查、三态探测、文件备份/台账、SSH 执行封装仍留在工程 utils/ 内,共享库只有 4 个基础模块;第二个工程复用时必然重复实现 utils/precheck.py、utils/idempotent_utils.py、utils/file_utils.py、utils/ssh_executor.py;/home/rain/py_public_modules/ 仅 4 文件
G3 EnvCheckModule 方案与既有 precheck/_gate() 功能重叠,若按原样实施会形成两套环境检测 .trae/documents/lustre-env-check-plan.md 第 9、52–71 行

本次采取的方案(与复核建议一致):不新建 modules/env_check.py,改为 ① 在既有 PreChecker 内扩充检测项;② 把通用能力上移共享库,工程侧继承/调用。 原方案文档保留为决策背景,已加状态标注。

2. 目标

变更完成后应达到以下可验证状态:

  • 共享库 /home/rain/py_public_modules/ 新增 4 个模块:ssh_executor.py、idempotent_module.py、file_module.py、precheck_module.py(共 8 个模块)
  • 工程内不再有通用能力的副本:utils/ssh_executor.py 删除;utils/idempotent_utils.py 只留 Lustre 专有探测;utils/file_utils.py 只留 fstab 追加;utils/precheck.py 只留继承 PreCheckBase 的领域专有检查项
  • PreChecker 新增 5 项检测,默认分级:执行身份非 root → BLOCK;系统盘可用空间低于 min_disk_free_gb → BLOCK;SELinux Enforcing → WARN;LNet NID 不可达 → WARN;运行内核版本 → INFO
  • 新增检查项的阈值与跳过列表全部下沉到配置(precheck 段),并同步写入配置样例、加入配置合法性校验、被实现体真正引用
  • 全部变更类子命令的门禁行为不变:真实执行存在 BLOCK 即终止;--dry-run 降级为告警
  • 文档口径一致:SKILL.md(共享库模块表 4→8)、docs/SOP.md、docs/RUNBOOK.md 与本实现体一致
  • 验证通过:py_compile + 全模块导入 + 真机 precheck 实跑 + --dry-run 连续两次零变更

3. 前置条件

项 要求 检查命令
操作机 Python 3.9+,工程 .venv 可用 cd /home/rain/Documents/lustre_ins && .venv/bin/python --version
共享库挂载 .pth 已挂载 /home/rain/py_public_modules .venv/bin/python -c "import precheck_module; print(precheck_module.__file__)"
SSH 免密 操作机可免密登录 192.168.31.21、192.168.31.22 ssh root@192.168.31.21 hostname
备份可用 py-skill-hardening/backup/ 内备份完整(29 份) find py-skill-hardening/backup -type f \| wc -l
变更窗口 本次仅改工程与共享库代码、文档;节点仅被只读探测 —
在跑作业 验证用的 precheck / --dry-run 均为只读,不影响作业 —

4. 影响范围

维度 说明
主机 操作机(工程代码、共享库代码、文档);目标节点仅被只读探测(lsblk/df/getenforce/id/uname/lctl ping/curl 等)
组件 共享库 4 个新模块;工程 utils/*、modules/*、deployer.py、config/deploy.yaml.example、docs/SOP.md、docs/RUNBOOK.md;全局技能 SKILL.md
是否停服 否。未执行任何真实部署动作,未写任何节点配置
窗口期 无(代码变更即时生效;验证为只读)
在跑作业 变更窗口内不影响;若节点有 Slurm/LSF 作业,precheck 会报 BLOCK(既有行为)
数据影响 无。未执行 mkfs.lustre、未改任何节点文件
其它工程影响 共享库为多工程共用:本次仅新增 4 个文件,未修改既有 4 个模块,对其它工程无影响

5. 分步操作

所有命令在工程根 /home/rain/Documents/lustre_ins 下执行。以下序列与实现等价,以实现体为准。

5.1 备份(变更前,已执行)

# 工程非 git 仓库,逐文件带时间戳备份到本主题目录,并登记台账
TS=20260923-141431
DEST=py-skill-hardening/backup
for f in main.py deployer.py utils/*.py modules/*.py \
         config/deploy.yaml.example config/deploy-expand.yaml.example \
         docs/SOP.md docs/RUNBOOK.md; do
  mkdir -p "$DEST/$(dirname "$f")"
  cp -p "$f" "$DEST/$f.bak.$TS"                 # 逐文件原位备份
done
# 全局技能单独留档
mkdir -p "$DEST/skills/developing-python-deploy-scripts"
cp -p /home/rain/.trae-cn/skills/developing-python-deploy-scripts/SKILL.md \
      "$DEST/skills/developing-python-deploy-scripts/SKILL.md.bak.$TS"
# 台账见 py-skill-hardening/backup/README.md

5.2 共享库新增 4 个通用模块

对应实现文件:/home/rain/py_public_modules/{ssh_executor,idempotent_module,file_module,precheck_module}.py

# 1) ssh_executor.py:工程 utils/ssh_executor.py 整体上移(SSH 执行便捷层)
#    唯一差异:临时文件前缀 lustre_deploy_ → deploy_(去工程耦合)
# 2) idempotent_module.py:三态探测通用项
#    probe_state / ssh_available / compute_sha256 / compare_file_content /
#    rpm_installed_state / is_rpm_installed,ssh_exec 注解脱耦为 Any
# 3) file_module.py:备份与台账通用项
#    timestamp / ensure_dir / backup_with_timestamp / write_file_if_changed /
#    register_remote_backup
# 4) precheck_module.py:预检查骨架
#    LEVEL_* / finding / summarize / has_block / render_report /
#    parse_lsblk_pairs / derive_system_disks / PreCheckBase(通用检查项)
.venv/bin/python -c "import ssh_executor, idempotent_module, file_module, precheck_module; print('ok')"

5.3 工程侧预检查重构与检测项扩充

对应实现文件:utils/precheck.py、utils/config_validate.py、config/deploy.yaml.example

# PreChecker 改为继承共享库 PreCheckBase:
#   通用项(操作机/SSH/OS/时钟/防火墙/调度器/执行身份/SELinux/磁盘空间/内核)由基类提供
#   工程侧只保留 Lustre 专有项:LNet 网卡、LNet ping、target 设备、仓库、版本矩阵
# 新增 5 项检测的默认分级(保守):
#   check_root        非 root        → BLOCK(precheck.require_root 可关)
#   check_disk_space  可用空间不足    → BLOCK(precheck.min_disk_free_gb,默认 10G)
#   check_selinux     Enforcing      → WARN (precheck.required_selinux,默认 permissive)
#   check_lnet_ping   NID 不可达      → WARN (LNet 未配置时记 INFO 跳过,不误报)
#   check_kernel      记录 uname -r  → INFO
# 配置样例与校验同步:precheck 段新增 5 个字段 + skip_checks,并纳入 config_validate
.venv/bin/python -m py_compile utils/precheck.py utils/config_validate.py

5.4 工程侧裁剪与引用调整

对应实现文件:utils/{idempotent_utils,file_utils}.py(裁剪)、utils/ssh_executor.py(删除)、deployer.py 与 modules/*.py(import 调整)

# 工程内删除已上移的副本(明确文件名,不用通配删除)
rm -f utils/ssh_executor.py
# 引用改为直连共享库(不再经工程内转发):
#   from utils.ssh_executor import SSHExecutor   → from ssh_executor import SSHExecutor
#   from utils.idempotent_utils import is_rpm_installed → from idempotent_module import ...
#   from utils.file_utils import register_remote_backup → from file_module import ...
#   from utils.precheck import LEVEL_BLOCK/…     → from precheck_module import ...
# 注意:Lustre 专有项(is_lustre_target_formatted / mounted_state /
#       lnet_configured_state / append_to_fstab / config_validate / lustre_state /
#       stripe_utils)仍留在工程侧,不动

5.5 文档同步

对应实现文件:SKILL.md、docs/SOP.md、docs/RUNBOOK.md、.trae/documents/lustre-env-check-plan.md

# SKILL.md:共享库模块表 4 → 8;标准目录结构中 utils/ 与共享库清单同步;
#           落地要求第三条改为「通用能力一律从共享库导入」
# docs/SOP.md:第 3 章与 5.1 补新增检测项说明
# docs/RUNBOOK.md:4.1 节补检查项清单、处置建议与修订记录
# lustre-env-check-plan.md:顶部标注「已并入 precheck 体系,不再新建 env_check 模块」

6. 验证

6.1 静态验证(本地,已执行)

项 命令 实测结果
语法 .venv/bin/python -m py_compile main.py deployer.py modules/*.py utils/*.py 通过,无输出
共享库导入 .venv/bin/python -c "import ssh_executor, idempotent_module, file_module, precheck_module" 4 个模块均由 /home/rain/py_public_modules/ 解析
入口可用 .venv/bin/python main.py --help 正常,子命令列表含 precheck,无导入错误
继承关系 PreChecker.__mro__ PreChecker → PreCheckBase → object
检测项齐备 dir(PreChecker) 中以 check_ 开头的方法 18 项,含新增 check_root / check_selinux / check_disk_space / check_kernel / check_lnet_ping
残留引用 grep -rn "utils.ssh_executor" --include=*.py . 无输出(无残留、无副本)
配置校验 validate_config 跑 config/deploy.yaml 与样例 均为 0 findings(新字段默认值合法)
副本比对 AST 比对共享库与工程侧同名函数体 已上移函数与原文一致;工程侧无残留副本

6.2 真机验证(只读,已执行)

预检查实跑(只读)

  • 命令:.venv/bin/python main.py precheck --config config/deploy.yaml
  • 退出码非 0(存在 BLOCK 项);报告:reports/precheck-20260923-143259.md
  • 结论计数:BLOCK 1 / WARN 4 / INFO 35
检查项 节点 实测值 级别
root(新增) 21 / 22 执行身份 root INFO
disk_space(新增) 21 / 22 / 可用空间 17G(阈值 10G) INFO
kernel(新增) 21 / 22 运行内核 5.14.0-611.13.1_lustre.el9.x86_64 INFO
selinux(新增) 21 / 22 SELinux 状态 Enforcing,期望 permissive WARN
lnet_ping(新增) 21 → 22 NID 可达: 192.168.31.22@tcp INFO
lnet_ping(新增) 22 → 21 修复去重后:NID 可达: 192.168.31.21@tcp INFO
repo(既有) 22 仓库不可达(HTTP 000): .../lustre-2.17.0/el9.7/server/ BLOCK

两点说明:

  • SELinux WARN 是真实环境事实(两台均 Enforcing),按保守分级不阻断后续变更;处置方式见 RUNBOOK 4.1 节。
  • repo BLOCK 属节点到 whamcloud 的网络波动(同一仓库在 21 上返回 200,9/22 记录中 22 亦为 200),非本次引入;--dry-run 下门禁按设计降级为告警,故预演继续执行。

首轮真机暴露并修复的缺陷

缺陷 现象 修复
check_lnet_ping 的对端 NID 未去重 21 身兼 mgs_mds 与 clients 两个角色,NID 在候选列表出现两次 → lctl ping <nid> <nid> 超时,误报「NID 不可达」(截断输出 Timeout ... is to large) all_nids 改用 dict.fromkeys 去重;复测 21↔22 双向均报可达

门禁与幂等(bootstrap --dry-run,真机)

项 运行 1 运行 2(复跑)
退出码 0 0
模块状态 success 12 12
「将执行变更」明细 10 条 10 条
「已存在,跳过」 7 条 7 条
ERROR 日志 2 条 2 条
日志行数 1132 1132
  • 去时间戳后两次日志逐行完全一致(仅命令耗时数值波动),满足「连续执行两次,第二次零变更、全跳过」的幂等验收标准。
  • 两处 ERROR 均为 lctl get_param llite.*.statahead_xattr 返回码 2(该参数在 Lustre 2.17 已移除),属既有遗留项,与本次变更无关。
  • 两次 dry-run 的门禁日志中,新增 5 项检测均在位且分级与设计一致(root INFO、disk_space INFO、kernel INFO、selinux WARN)。

7. 回滚

备份位置

变更前已对 29 份文件做带时间戳备份,位置与台账:

  • 备份目录:py-skill-hardening/backup/(时间戳 20260923-141431)
  • 台账:backup/README.md(逐条登记备份文件、原路径、产生时间、因何产生、回滚什么、何时可删)

回滚步骤

cd /home/rain/Documents/lustre_ins
TS=20260923-141431
DEST=py-skill-hardening/backup
# 1) 逐文件恢复工程内被改文件(示例:全部)
for f in main.py deployer.py utils/*.py modules/*.py \
         config/deploy.yaml.example config/deploy-expand.yaml.example \
         docs/SOP.md docs/RUNBOOK.md; do
  cp -p "$DEST/$f.bak.$TS" "$f"                 # 覆盖回变更前内容
done
# 2) 恢复被删除的工程内模块
cp -p "$DEST/utils/ssh_executor.py.bak.$TS" utils/ssh_executor.py
# 3) 恢复全局技能
cp -p "$DEST/skills/developing-python-deploy-scripts/SKILL.md.bak.$TS" \
      /home/rain/.trae-cn/skills/developing-python-deploy-scripts/SKILL.md
# 4) 删除本次新增的共享库模块(明确文件名,不用通配)
rm -f /home/rain/py_public_modules/ssh_executor.py \
       /home/rain/py_public_modules/idempotent_module.py \
       /home/rain/py_public_modules/file_module.py \
       /home/rain/py_public_modules/precheck_module.py
# 5) 本次新增的报告为只读产出物,按需保留

回滚后验证

.venv/bin/python -m py_compile main.py deployer.py modules/*.py utils/*.py   # 预期:无输出
.venv/bin/python main.py precheck --config config/deploy.yaml                # 预期:回到单体 PreChecker,可正常运行
grep -c "PreCheckBase" utils/precheck.py                                     # 预期:0

回滚不影响任何目标节点:本次变更未在节点上留下配置或数据改动。

8. 风险说明

风险 等级 缓解措施
import 路径批量调整导致导入失败 高 全量 py_compile + 逐模块导入 + main.py --help + 残留引用 grep 四道验证;备份可一键还原
共享库被工程侧专有语义污染 中 上移时逐函数判定:Lustre 专有项(卷标判定、挂载状态、LNet 配置、fstab 文案、target 解析)一律不上移;共享库内不出现 Lustre 字样
同名模块导入歧义(utils/ssh_executor.py 与共享库同名) 中 工程内该文件已删除,from ssh_executor import ... 唯一解析到共享库;已实测确认
新增门禁项误阻断合法变更 中 保守分级:仅 root 权限与磁盘空间为 BLOCK;SELinux/LNet ping 为 WARN;各项均可经 precheck.skip_checks 关闭
lctl ping 在 LNet 未配置时误报 中 先用 lnetctl net show/lctl list_nids 做能力探测,未配置时记 INFO 跳过,不报故障
check_local 依赖的共享库模块清单随之扩容 低 清单改为共享库内常量 DEFAULT_SHARED_LIB_MODULES(8 项),工程侧可传参覆盖
共享库变更影响其它工程 低 只新增文件,未改既有 4 个模块;其它工程不受影响
每次 precheck 都新增一份报告 低 既有留痕设计,与 inspect 一致;按需清理旧报告

9. 附录

9.1 交付物清单

路径 内容
/home/rain/py_public_modules/ssh_executor.py 新增:SSH 执行便捷层
/home/rain/py_public_modules/idempotent_module.py 新增:幂等三态探测
/home/rain/py_public_modules/file_module.py 新增:文件备份与台账
/home/rain/py_public_modules/precheck_module.py 新增:预检查骨架 PreCheckBase
utils/precheck.py 重写:继承骨架 + 新增 5 项检测
utils/config_validate.py 新增 precheck 段合法性校验
utils/{idempotent_utils,file_utils}.py 裁剪:只留 Lustre 专有项
utils/ssh_executor.py 删除(已上移)
deployer.py、modules/*.py、utils/{lustre_state,stripe_utils}.py import 改直连共享库
config/deploy.yaml.example precheck 段新增 5 个字段与说明
docs/SOP.md、docs/RUNBOOK.md 口径同步
/home/rain/.trae-cn/skills/developing-python-deploy-scripts/SKILL.md 共享库模块表 4→8、目录结构同步
.trae/documents/lustre-env-check-plan.md 加状态标注(并入 precheck 体系)
py-skill-hardening/backup/、py-skill-hardening/SOP.md 备份台账与本文档

9.2 决策记录

  • 不新建 EnvCheckModule:既有 precheck 子命令 + _gate() 已是环境的唯一入口,再建模块级门禁会形成两套检测、两处维护。改为扩充 PreChecker,与本技能「预检查独立成只读模块,不得由各业务模块零散实现」一致。
  • 上移粒度按函数判定而非整文件:precheck.py/idempotent_utils.py/file_utils.py 均属「通用骨架 + 领域特化」混合体,整文件上移会让共享库带 Lustre 语义。故只上移通用函数,领域项留工程侧。
  • config_validate.py 不上移:其校验域整体绑定 Lustre schema(fsname、NID、target 类型、striping),属工程专有。
  • 新增检测项默认分级取保守方案:仅「执行身份非 root」与「系统盘空间不足」阻断变更——这两者必然导致变更失败;其余为可继续的告警,避免换环境时被频繁拦截。

9.3 遗留事项(需人工决策,本次未改动)

  1. docs/HANDOFF.md 不存在,本次结论未落档为交接快照;如需建立请另行说明。
  2. modules/ 下 13 个业务模块中仍有 7 个未覆盖 pre_check()(repo_setup、package_install、fstab_persist、mount_targets、stripe_tuning、lctl_tuning、inspector;已覆盖的为 target_format、lnet_config、kernel_strategy、firewall、ha_pacemaker、client_mount)——环境门禁由编排层 _gate() 统一承担,模块级 pre_check() 仅做本模块相关的补充校验;是否逐模块补齐属既有设计的未决项。
  3. 巡检遗留的 3 项 lctl 调优参数与 Lustre 2.17 不兼容(见 precheck-hardening/SOP.md 10.4 节),本次未处理。

来源:/home/rain/Documents/lustre_ins/py-skill-hardening/SOP.md | 最后更新:2026-09-23 14:40(由 build/sync.py 同步,请勿在站点侧修改)