Files
pikasTech-HWLAB/docs/reference/dev-runtime-hotfix-runbook.md
T

157 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HWLAB DEV Runtime Hotfix Runbook
本文定义 `hwlab-dev` 运行态热修的最低边界、审计字段、只读确认和回滚口径。它只适用于 DEV 能力边界或阻塞排障,不是发布验收手册,也不把运行态覆盖当作源码真相。
本规则承接 [pikasTech/HWLAB#462](https://github.com/pikasTech/HWLAB/issues/462) 和 [pikasTech/HWLAB#465](https://github.com/pikasTech/HWLAB/issues/465),服务 [pikasTech/HWLAB#7](https://github.com/pikasTech/HWLAB/issues/7) / [pikasTech/HWLAB#239](https://github.com/pikasTech/HWLAB/issues/239) 的“用户工作台 + Code Agent 真实可用”主线。它不改变 `DC-DCSN-P0-2026-003` / [pikasTech/HWLAB#78](https://github.com/pikasTech/HWLAB/issues/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。
## G14 k3s 前置
当前 HWLAB DEV 运行态热修目标是 G14 `hwlab-dev` namespace。所有只读观测、热修、回滚和复核必须通过 UniDesk route `G14:k3s` 进入 G14 原生 k3s,并先确认目标 namespace 与节点:
```sh
tran G14:k3s kubectl get nodes -o wide
tran G14:k3s kubectl get ns hwlab-dev hwlab-prod
```
结果必须显示 G14 原生 k3s 中存在 `hwlab-dev`;需要验证 PROD 发布面时也必须显示 `hwlab-prod`。出现 D601 kubeconfig、Docker Desktop Kubernetes、`desktop-control-plane``127.0.0.1:11700` 或第二套 `hwlab-dev` 控制面时,停止 hotfix、回滚和验收判断。此边界与 [dev-runtime-boundary.md](dev-runtime-boundary.md) 和 [g14-gitops-cicd.md](g14-gitops-cicd.md) 保持一致。
## kubectl 热修命令形状
运行面热修应优先使用小补丁、短连接和远端临时文件,避免把大对象穿过 shell argv、CLI 输出截断或 `kubectl apply` 的 last-applied annotation。
推荐的 ConfigMap data 局部覆盖方式是:在本地生成只包含本次 key 的 merge patch,通过 G14 短连接写入远端临时文件,再让 `kubectl` 从真实文件读取。示例形状必须保持短连接;具体目标 ConfigMap、Deployment 和 key 按当次 issue 填写:
```sh
tran G14:k3s script <<'SCRIPT'
set -e
kubectl get ns hwlab-dev >/dev/null
patch_file=/tmp/hwlab-hotfix.patch.json
cat > "$patch_file" <<'PATCH_JSON'
{
"metadata": {
"annotations": {
"hwlab.pikastech.com/hotfix-reason": "<reason>"
}
},
"data": {
"<key>": "<small-hotfix-content-or-pre-rendered-json-string>"
}
}
PATCH_JSON
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
SCRIPT
```
固定避坑规则:
- 不要用 `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 内验证同一份实验时,优先复用同一个脚本在 G14、pod 和 CI 中运行;不要把探测逻辑在每个通道手写一遍。若必须临时写脚本,文件名包含目标和 commit,issue 中记录脚本路径、commit、命令和输出摘要。
- pod 内部、G14 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:17667` / service mesh 中不被 bad-port 拦截的入口。
- 遇到 `fetch failed``bad port`、公网通而 pod loopback 不通时,先判断是不是实验工具栈限制,不要立即归因 cloud-api、FRP、k3s Service 或 gateway 离线。issue 复盘要记录具体 URL、调用库、错误字符串和替代入口。
- 清理临时进程时不要用会匹配到清理脚本自身 `cmdline` 的宽泛字符串,例如 `hwlab-gateway-hotfix``node -e` 本身会把该字符串放进 `/proc/<pid>/cmdline`。清理脚本应匹配真实入口和参数组合,并显式排除 `process.pid`,或由启动脚本写 pidfile 后按 pidfile 清理。
- 正式 smoke/check 不在 master server 上执行。master server 只负责源码编辑、Git、日志和指挥;仓库级 `check``node --test`、browser/layout smoke、`web/hwlab-cloud-web/scripts/check.mjs` 等正式验证必须改在 G14 `/root/hwlab`、G14 k3s/Tekton、repo-owned CI 或其他获批执行面运行。
- 验证 gateway 非阻塞或长命令并发时,不要把同一路 wrapper 的长轮询 `job-status`/状态查询当作唯一证据;忙碌 gateway 可能把后续短状态查询和只读调用一起排队,导致“状态检查卡住”掩盖真实根因。优先使用 repo-owned probe:保持一个慢命令在飞,再提交一个短命令测量 quick path 延迟;完成态判断优先读 job state/log/artifact 时间戳或 quick path 结果,而不是重复发同一路长轮询。
- 不要对已经很大的 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 --check``grep` 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` |
## 只读审计入口
仓库内只读入口是:
```sh
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 get``kubectl exec` 形态的读检查,用于判断当前 DEV 是否仍被 hotfix 覆盖;它不会执行 `apply``patch``rollout``restart``delete``create``set 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 `volumes``volumeMounts` 或 template annotations;正式 DEV CD apply 应先识别这种 unmanaged hotfix 覆盖,删除对应 desired Deployment,再由同一次 apply 从源码重新创建。
只读确认口径是:
```sh
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.mjs``containers[].volumeMounts[]`
- `hwlab.pikastech.local/pc-gateway-shell-hotfix` 等 hotfix annotation
- 不再需要的 `hwlab-cloud-api-code-agent-hotfix` ConfigMap。
直接 `kubectl patch``kubectl delete configmap``kubectl rollout status` 或等价写操作必须由明确授权的 DEV 操作者执行,并且仍要使用 `KUBECONFIG=/etc/rancher/k3s/k3s.yaml` 与节点 `d601` 确认。本 runbook 的审计脚本不执行这些写操作。回滚后再次运行 `scripts/dev-runtime-hotfix-audit.mjs --collect-readonly`,期望分类包含 `no-hotfix-detected`,且不包含 `deployment-mounts-hotfix``pod-loads-hotfix-marker``rollback-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](m3-loop-rollout-runbook.md)。
- 不得读取或打印 Secret value、kubeconfig token、DB URL 密码、provider token。
- 不得新增或修改仓库 `reports/**`;过程证据写入 PR/issue/comment,临时 JSON 只能放 `/tmp``.state` 或 CI artifact。