SOP — vncserver 只读探测批量化(拼 shell + 探测缓存 + 连接复用)¶
主题目录:
services/vncserver/vnc-server-deploy/probe_batch_20261008/|执行日期:2026-10-08|类型:C 类(改实现体) 触发:把「只读探测批量化」推广到其它工程时,vncserver 是三件套全缺的一个(无连接复用、无拼 shell、无缓存)
1. 背景¶
侦查结论(file:line 复核):
| 事实 | 证据 |
|---|---|
目标单主机 192.168.31.28(hpc-submit) |
config/site.yaml:5 |
自研 SSHRunner,每次 .run() 都 subprocess 新起一个 ssh 进程 |
utils/common.py:71-88 |
pre_check 逐条探测(os-release/uname/getenforce/uname -r/repo/cmds/sssd/free/df/ss…) |
deployer.py:96-197 |
sync_status 逐条 sha256 / 逐包 rpm -q / systemctl |
deployer.py:240-359 |
无连接池、无 (host,cmd) 缓存、无拼 shell |
全工程 grep 无命中 |
因是单主机,「主机间并发」不适用;收益来自 ① 同主机多条拼一段 shell ② 探测缓存 ③ ssh 连接复用。
2. 目标¶
| # | 目标 | 判据 |
|---|---|---|
| 1 | SSHRunner 支持「一条 shell 取回多条只读探测」且退出码不丢 |
真机冒烟:exit 3 → rc=3、test -f 不存在 → rc=1 |
| 2 | SSHRunner 增加 (命令原文) 探测缓存,变更后自动失效 |
离线自测:命中不再下发;invalidate 后重新下发 |
| 3 | ssh 连接复用(ControlMaster) | _ssh() 含 ControlMaster/ControlPath/ControlPersist |
| 4 | pre_check / sync_status 改走批量 + 缓存 |
上机 plan:墙钟下降、变更清单逐条一致 |
3. 前置条件¶
- 工程根
/home/rain/Documents/services/vncserver/vnc-server-deploy; - 到
192.168.31.28的 root 免密(实测ssh -o BatchMode=yes返回 OK); - 变更前完成 5.1 备份;验证全程只读(
plan/precheck/drift)。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 变更对象 | utils/common.py(SSHRunner)、deployer.py |
| 改的是什么 | 只读探测的打包方式(拼 shell / 缓存 / 连接复用);判定表达式、变更清单顺序与内容、变更类命令均未动 |
| 是否停服 | 否;验证只读 |
| 回滚风险 | 低:改动集中在 _pkg_installed / _xfce_ready / _ldap_endpoint / _ports_in_use + pre_check / sync_status / _step_packages |
5. 分步操作¶
5.1 备份¶
TOP=/home/rain/Documents/services/vncserver/vnc-server-deploy/probe_batch_20261008
mkdir -p "$TOP/backup"
cd /home/rain/Documents/services/vncserver/vnc-server-deploy
cp -a deployer.py utils/common.py "$TOP/backup/"
5.2 utils/common.py:SSHRunner 三件事¶
本节与 5.3 是实现体源码改动(改代码,不是在机器上跑的命令),故以「改动点 + 等价语义」呈现; 手工复现的唯一正确方式是 5.5 的「用
backup/旧版覆盖跑一轮」对照。 实现体:utils/common.py、deployer.py(实际执行以实现体为准)。
# ① 连接复用(_ssh 内)
# -o ControlMaster=auto -o ControlPath=/tmp/vncdeploy-ssh-<target>-<port> -o ControlPersist=120s
# ② 探测缓存
# self._cache: Dict[命令原文, Result];run_cached(cmd) 命中即不下发;invalidate() 清空
# ③ 批量探测
# probe({键: 只读命令}) -> {键: Result}:拼一段 shell 一次往返,并回填缓存
两个必须知道的坑(本主题实测踩到并修掉):
1. 退出码回传必须用独立整行标记 —— 初版写成 printf '@@rc_键 %s\n' "$?",与
_parse_kv 的「@@键 整行」约定不符,导致 rc 段被并入值段(真机表现为 exit 3 → rc=0)。
改为:命令 → __rc=$? → echo '@@rc_键' → echo "$__rc"。
2. 每条探测命令必须用子 shell ( … ) 包裹 —— 否则命令里的 exit 会终止整段 shell,
后续段与 rc 全部丢失。
3. 附带:探测命令若不输出换行(如 curl -w '%{http_code}'),会把 @@rc_ 标记粘进取值
(harbor 工程在同类改造中实际踩到)。
5.3 deployer.py:只读探测改走批量 + 缓存¶
# pre_check:环境契约 4 条 → 1 条 probe;包源/关键命令/SSSD/内存/磁盘 → 1 条 probe
# _pkg_installed / _xfce_ready / _ldap_endpoint / _ports_in_use → run_cached(同文本才命中)
# _installed_packages:一次 `rpm -q --qf '%{NAME}\n' p1 p2 …`(注意 %%{NAME} 转义)
# sync_status / _step_packages:包循环改 _installed_packages
# _maybe:变更后 self.r.invalidate();put_text 同理
5.4 离线自测¶
cd /home/rain/Documents/services/vncserver/vnc-server-deploy
python3 -m py_compile deployer.py utils/common.py && echo COMPILE_OK
python3 probe_batch_20261008/selftest.py # → VNC_PROBE_CACHE_SELFTEST_OK
5.5 上机新旧对照(只读 plan)¶
mkdir -p /tmp/vncnew && cp deployer.py /tmp/vncnew/ && cp utils/common.py /tmp/vncnew/ # 先存新版!
python3 deployer.py plan > /tmp/vnc_new.log 2>&1
cp probe_batch_20261008/backup/deployer.py . && cp probe_batch_20261008/backup/common.py utils/common.py
python3 deployer.py plan > /tmp/vnc_old.log 2>&1
cp /tmp/vncnew/deployer.py . && cp /tmp/vncnew/common.py utils/common.py
cmp -s deployer.py /tmp/vncnew/deployer.py && cmp -s utils/common.py /tmp/vncnew/common.py && echo RESTORE_OK
⚠️ 务必先 cp 新版到 /tmp 再覆盖 —— 本次首轮对照漏了这一步,导致新版被旧版覆盖、需按本章重放改动。
6. 验证¶
验证一:离线自测 —— VNC_PROBE_CACHE_SELFTEST_OK(12 项):probe 一条 shell、段值与 rc 正确、
非 0 退出码传递、缓存命中/失效/关闭、文本不一致不命中、_ssh 含三个 Control* 参数。
验证二:真机 probe 冒烟(.28)
| 键 | 命令 | 实测 rc |
|---|---|---|
| osrel | grep -E '^(ID\|VERSION_ID)=' /etc/os-release |
0(值 ID="rocky" / VERSION_ID="9.8") |
| sssd | systemctl is-active --quiet sssd |
0 |
| bad | exit 3 |
3 |
| missing | test -f /no/such/file |
1 |
验证三:上机 plan 新旧对照(2026-10-08)【已确认】
| 方案 | 墙钟 ms | rc | 待变更项 |
|---|---|---|---|
| 旧 | 16126 | 0 | 0 |
| 新 | 6011(-62.7%) | 0 | 0 |
输出 diff:仅「日志文件名 / 报告文件名」的时间戳不同,业务内容逐条一致。还原校验 RESTORE_OK。
7. 回滚¶
cd /home/rain/Documents/services/vncserver/vnc-server-deploy
BAK=probe_batch_20261008/backup
cp -a $BAK/deployer.py . && cp -a $BAK/common.py utils/common.py
回滚后验证:py_compile + plan(墙钟回到 ~16 s、清单不变)。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 拼 shell 后退出码丢失 → 三态判定失真 | 用独立整行 @@rc_键 回传;真机验证 rc=3/1/0 |
探测命令含 exit 终止整段 shell |
每段用子 shell ( … ) 包裹 |
| 探测命令不输出换行 → 标记粘连取值 | docstring 写明;harbor 已实测踩到并修 |
| 缓存陈旧(变更后仍读旧值) | _maybe / put_text 成功后自动 invalidate() |
| ControlPersist 残留 socket | ControlPersist=120s 自动回收;socket 在 /tmp,重启即清 |
| 对照时覆盖丢失新版 | SOP 5.5 强制「先 cp 新版到 /tmp」并 cmp 校验 |
附录 A 交付物清单¶
| 文件 | 说明 |
|---|---|
SOP.md |
本文件 |
selftest.py |
离线自测(12 项) |
backup/deployer.py、backup/common.py |
改造前快照 |
backup/README.md |
备份台账 |
| 变更的工程文件 | deployer.py、utils/common.py |
附录 B 与共享库的关系¶
本例是自研 SSH 层(subprocess 调系统 ssh),与共享库 base_module / ssh_executor 无关,
故三件套在本工程需自行实现;同类能力在共享库侧的对应物是
BaseModule.query_on_host / probe_per_host(本次同步新增)与 SSHExecutor.run_on_all。
附录 C RUNBOOK 判定¶
本工程已有 docs/RUNBOOK.md(覆盖 VNC 服务的日常使用与排障)。本次变更只改只读探测的打包方式
(拼 shell / 缓存 / 连接复用),服务的用法、交付物形态、配置文件与命令均未变,
故无需更新 RUNBOOK;若日后探测行为对使用者可见(如新增子命令或改变输出),须同步更新它。
来源:
/home/rain/Documents/services/vncserver/vnc-server-deploy/probe_batch_20261008/SOP.md| 最后更新:2026-10-08 11:44(由build/sync.py同步,请勿在站点侧修改)