Files
pikasTech-HWLAB/docs/reference/dev-runtime-hotfix-runbook.md
T
2026-05-25 03:33:32 +00:00

12 KiB
Raw Blame History

HWLAB DEV Runtime Hotfix Runbook

本文定义 hwlab-dev 运行态热修的最低边界、审计字段、只读确认和回滚口径。它只适用于 DEV 能力边界或阻塞排障,不是发布验收手册,也不把运行态覆盖当作源码真相。

本规则承接 pikasTech/HWLAB#462pikasTech/HWLAB#465,服务 pikasTech/HWLAB#7 / pikasTech/HWLAB#239 的“用户工作台 + Code Agent 真实可用”主线。它不改变 DC-DCSN-P0-2026-003 / pikasTech/HWLAB#78 的 M3 上位约束:hotfix runbook、审计输出、SOURCE、LOCAL、DRY-RUN 或只读观察都不能被包装成 M3 DEV-LIVE 通过。

允许条件

DEV runtime hotfix 只能在以下条件同时满足时使用:

  • 目标是 hwlab-dev,不是 PROD、真实用户生产面或真实硬件生产控制面。
  • 目的是打通能力边界、定位阻塞层或恢复用户工作台关键路径;不能为了绕过源码、测试、artifact 或 CD 流程而热修。
  • 操作前已经明确该任务不会抢占 M3 虚拟硬件可信闭环主线,也不会把热修证据升级成 M3 验收。
  • 操作后必须创建或关联源码化 follow-up。#465 的持久化拆分是固定分工:#460 负责 Code Agent -> PC gateway shell.exec 能力路由,#461 负责 hotfix runner 源码化,#462 负责本 runbook 与只读审计,#463 负责 JSON-RPC serviceId 结构化诊断,#464 负责 DEV-LIVE smoke/harness。

D601 k3s 前置

D601 裸 kubectl 不可信。所有只读观测、热修、回滚和复核都必须显式指定原生 k3s kubeconfig,并先确认节点:

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodes -o jsonpath='{.items[*].metadata.name}'

结果必须包含 d601。出现 docker-desktopdesktop-control-plane127.0.0.1:11700 或第二套 hwlab-dev 控制面时,停止 hotfix、回滚和验收判断。此边界与 dev-runtime-boundary.mddeployment-publish.md 保持一致。

kubectl 热修命令形状

运行面热修应优先使用小补丁、短连接和远端临时文件,避免把大对象穿过 shell argv、CLI 输出截断或 kubectl apply 的 last-applied annotation。

推荐的 ConfigMap data 局部覆盖方式是:在本地生成只包含本次 key 的 merge patch,通过 D601 短连接写入远端临时文件,再让 kubectl 从真实文件读取:

node - <<'NODE' | bun scripts/cli.ts ssh D601 argv bash -lc '
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodes -o jsonpath="{.items[*].metadata.name}" | grep -w d601 >/dev/null
patch_file=/tmp/hwlab-hotfix.patch.json
cat > "$patch_file"
kubectl -n hwlab-dev patch configmap <hotfix-configmap> --type merge --patch-file "$patch_file"
rm -f "$patch_file"
kubectl -n hwlab-dev rollout restart deployment/<deployment>
kubectl -n hwlab-dev rollout status deployment/<deployment> --timeout=180s
'
const fs = require("node:fs");
process.stdout.write(JSON.stringify({
  metadata: { annotations: { "hwlab.pikastech.com/hotfix-reason": "<reason>" } },
  data: { "<key>": fs.readFileSync("<local-file>", "utf8") }
}));
NODE

固定避坑规则:

  • 不要用 kubectl patch --patch-file - 假设 stdin 可用;目标 kubectl 版本可能把 - 当普通文件名。用 /tmp/*.patch.json--patch-file
  • 不要把大 JSON patch 放进 kubectl patch -p "$patch" 或 shell argvConfigMap 覆盖源码文件时很容易触发 Argument list too long。用 stdin 写远端临时文件。
  • 不要把多层脚本内联穿过 ssh -> bash -lc -> kubectl exec -> sh -lc -> node/python/powershell。这种路径会让 heredoc、$!、反引号、JSON 字符串、PowerShell 管道和中文内容被不同 shell 多次解释,常见结果是实验命令还没进入 pod 就被截断或改写。
  • 可靠做法是把实验逻辑沉淀为 repo-owned 脚本,或先写入远端 /tmp/*.mjs|*.sh|*.ps1,再用 kubectl cp/ConfigMap 挂载到 pod,最后用 kubectl exec -- node /tmp/script.mjs --arg value 或等价 argv 形态执行。只允许在外层 shell 保留短命令和固定参数,不在命令行里嵌大段 JS/PowerShell/heredoc。
  • 需要从 pod 内验证同一份实验时,优先复用同一个脚本在 D601、pod 和 CI 中运行;不要把探测逻辑在每个通道手写一遍。若必须临时写脚本,文件名包含目标和 commit,issue 中记录脚本路径、commit、命令和输出摘要。
  • pod 内部、D601 host、FRP 公网入口可能使用不同端口语义。127.0.0.1:6667 是 HWLAB 内部 cloud-api 常用端口,但 6667 属于 WHATWG bad portNode/undici fetch 会在发包前直接报 bad port,包括临时 gateway 自己 poll http://127.0.0.1:6667 的场景。运行面实验脚本和 gateway 传输层必须用 Node http/https 原生 request,或改走公网 http://74.48.78.17:16667 / service mesh 中不被 bad-port 拦截的入口。
  • 遇到 fetch failedbad port、公网通而 pod loopback 不通时,先判断是不是实验工具栈限制,不要立即归因 cloud-api、FRP、k3s Service 或 gateway 离线。issue 复盘要记录具体 URL、调用库、错误字符串和替代入口。
  • 清理临时进程时不要用会匹配到清理脚本自身 cmdline 的宽泛字符串,例如 hwlab-gateway-hotfixnode -e 本身会把该字符串放进 /proc/<pid>/cmdline。清理脚本应匹配真实入口和参数组合,并显式排除 process.pid,或由启动脚本写 pidfile 后按 pidfile 清理。
  • 不要对已经很大的 hotfix ConfigMap 使用 kubectl apply -f -apply 会尝试写入 kubectl.kubernetes.io/last-applied-configuration,可能超过 Kubernetes annotation 256 KiB 限制。局部更新用 patch --type merge --patch-file
  • 多文件运行面覆盖第一次创建 ConfigMap 时也不要默认用 kubectl apply -f -;如果 data 可能超过 annotation 限制,先 kubectl delete configmap <name> --ignore-not-found,再 kubectl create configmap <name> --from-file=...,随后用 Deployment annotation 记录 hotfix 版本并 rollout。正式 CD 收口时必须删除这些 unmanaged ConfigMap/volumeMount,不让热修覆盖继续漂在运行面。
  • 不要通过会截断 stdout 的控制 CLI 把 kubectl get configmap -o json 回读到本地再 replace;大 data 可能被日志/截断污染成非法 JSON。只有在确认完整无截断的原生通道中,才允许对完整对象做 kubectl replace -f -
  • 每次 rollout restart 后立即执行 rollout status,再用 running pod 做最小只读验证,例如 node --checkgrep marker、curl health 或公开入口 trace/result 轮询。
  • 临时补丁文件必须放在远端 /tmp,命名包含 hotfix 目标,命令结束后删除;issue 里只记录 ConfigMap 名、key、mountPath、operationId/traceId 和验证方法,不粘贴完整 data。

必须记录

每次 DEV hotfix 至少记录以下字段,写入 PR/issue/comment,不写入仓库 reports/**

字段 说明
Deployment namespace、name、generation、template annotation、容器 image。
Pod 新旧 pod 名、phase、node、用于验证的 running pod。
ConfigMap 名称、key 名、用途;不得把 data 全量贴入 issue。
annotation 记录热修来源和目的的 Deployment template annotation。
mount/subPath volume 名、ConfigMap 名、mountPath、subPath、readOnly。
traceId / operationId 能力验证请求的 trace 与 operation 标识。
验证命令 grep marker、node --check、API 返回字段等只读或最小验证命令。
回滚方式 移除 volume/mount/annotation 的方法,或用源码 artifact/CD 覆盖的方法。
源码化 follow-up 关联 issue/PR,说明热修不会作为长期 runtime truth。

#465 的典型对象是:

Deployment hwlab-dev/hwlab-cloud-api
ConfigMap hwlab-cloud-api-code-agent-hotfix
覆盖文件 /app/internal/cloud/code-agent-chat.mjs
subPath code-agent-chat.mjs
marker runtime-hotfix-pc-gateway-shell
annotation hwlab.pikastech.local/pc-gateway-shell-hotfix
能力路径 /v1/agent/chat -> /v1/rpc/hardware.invoke.shell -> PC gateway -> Windows cmd

只读审计入口

仓库内只读入口是:

node scripts/dev-runtime-hotfix-audit.mjs --pretty
node scripts/dev-runtime-hotfix-audit.mjs --collect-readonly --pretty

默认 --plan 模式只输出 compact JSON 计划,不执行 kubectl--collect-readonly 只允许 kubectl getkubectl exec 形态的读检查,用于判断当前 DEV 是否仍被 hotfix 覆盖;它不会执行 applypatchrolloutrestartdeletecreateset image,也不会读取 Secret resources 或 secret values。

输出分类固定包含以下语义:

分类 含义
hotfix-configmap-present 目标 ConfigMap 仍存在。
deployment-mounts-hotfix Deployment template 仍通过 volumeMount/subPath 覆盖目标文件。
pod-loads-hotfix-marker running pod 内目标文件仍包含 hotfix marker。
source-artifact-expected 正式收口应回到 source/PR/artifact/CD。
rollback-required 当前仍检测到热修残留或覆盖,不能把运行态当源码真相。
no-hotfix-detected 只读审计未发现该 hotfix ConfigMap 或 mount 覆盖。
unknown-needs-manual-readonly-check 无法确认节点、对象或 pod 状态,需要人工继续只读核对。

回滚口径

优先回滚方式是用源码化 artifact/CD 覆盖 runtime hotfix#460/#461 合并并发布后,Deployment 应消费正式镜像,不再通过 ConfigMap 覆盖 /app/internal/cloud/code-agent-chat.mjs。注意 kubectl apply -k 可能保留运行面 patch 写入、且源码 desired-state 不拥有的 hotfix volumesvolumeMounts 或 template annotations;正式 DEV CD apply 应先识别这种 unmanaged hotfix 覆盖,删除对应 desired Deployment,再由同一次 apply 从源码重新创建。

只读确认口径是:

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodes -o jsonpath='{.items[*].metadata.name}'
kubectl -n hwlab-dev get deployment hwlab-cloud-api -o json
kubectl -n hwlab-dev get configmap hwlab-cloud-api-code-agent-hotfix -o json
node scripts/dev-runtime-hotfix-audit.mjs --collect-readonly --pretty

如果必须直接移除运行态覆盖,授权操作者应先从 kubectl -n hwlab-dev get deployment hwlab-cloud-api -o json 中定位精确 indexes,再移除:

  • 指向 hotfix ConfigMap 的 spec.template.spec.volumes[]
  • 指向 /app/internal/cloud/code-agent-chat.mjscontainers[].volumeMounts[]
  • hwlab.pikastech.local/pc-gateway-shell-hotfix 等 hotfix annotation
  • 不再需要的 hwlab-cloud-api-code-agent-hotfix ConfigMap。

直接 kubectl patchkubectl delete configmapkubectl rollout status 或等价写操作必须由明确授权的 DEV 操作者执行,并且仍要使用 KUBECONFIG=/etc/rancher/k3s/k3s.yaml 与节点 d601 确认。本 runbook 的审计脚本不执行这些写操作。回滚后再次运行 scripts/dev-runtime-hotfix-audit.mjs --collect-readonly,期望分类包含 no-hotfix-detected,且不包含 deployment-mounts-hotfixpod-loads-hotfix-markerrollback-required

禁止误用

  • 不得把 ConfigMap/pod 覆盖当作 source truth;正式修复必须回到 source、PR、artifact 和 repo-owned CD。
  • 不得把 #462 runbook 或只读审计包装成 #460/#461 源码化完成。
  • 不得把 #462 runbook 或只读审计包装成 #464 DEV-LIVE smoke/harness 完成。
  • 不得把热修期间的 stdout、trace 或 operation 扩大为 M3 trusted evidenceM3 判定仍按 m3-loop-rollout-runbook.md
  • 不得读取或打印 Secret value、kubeconfig token、DB URL 密码、provider token。
  • 不得新增或修改仓库 reports/**;过程证据写入 PR/issue/comment,临时 JSON 只能放 /tmp.state 或 CI artifact。