From 2629984139cf23a084a090c34270a0feb02d8d4c Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 12 May 2026 21:07:16 +0800 Subject: [PATCH 1/2] docs: add vcluster API entry preflight + K8s API timeout evidence layering guides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two engine-neutral methodology docs for environment-layer API access first-blocker classification. docs/vcluster/addon-vcluster-api-entry-preflight-guide.md (new) — vcluster API entry preflight gate applied before creating any business resource (KB Cluster / OpsRequest / Backup) inside a vcluster. Covers 4 read-only probes + 1 install-layer deep check, first-blocker 4-types (A control-plane / Loft proxy TLS termination / B kubeconfig missing / C backend kube-apiserver / etcd pressure / D install-layer config drift), artifact 4-block format, controlled escalation pattern with 5-item closeout per phase, broad-grep + API timeout anti-pattern, and candidate ≠ confirmed gate. Type D distinguished from workload-layer ImagePullBackOff via cross-link to the IDC image registry mirror guide. Appendix D marked in-progress: install-fix positive control not yet attained, with explicit language preventing the case from being read as install-fix already confirmed. docs/addon-k8s-api-timeout-evidence-layering-guide.md (new) — kubectl / client-go Client.Timeout exceeded while awaiting headers layered evidence gathering. 5 candidate layers (L1 network path / L2 apiserver / L3 etcd backend / L4 shared API endpoint / L5 non-API noise), N>=5 round readyz/curl/kubectl comparison table, apiserver / etcd slow-path keyword set, exact-timeout-window evidence gap framing, exclusion triplet (not product / not a specific patch / not controller logic), candidate to confirmed gate (same-window >=2 evidence sources + N>=2 reproductions + owner verify), owner-routed handoff 4-block format. Both docs add Affected by version skew intro field per addon-docs intro convention. Cross-link the two docs as front-end (vcluster control-plane TLS / proxy) vs back-end (apiserver / etcd) layers of the same surface. docs/SKILL-INDEX.md updated with both docs: - section 3 (env-ready-pre) for vcluster API entry preflight, - section 4 (runtime / troubleshooting) for K8s API timeout, - full descriptions in the document master list. Placement of addon-vcluster-api-entry-preflight-guide.md under docs/vcluster/ aligns with the IDC vcluster grouping introduced in the previous main branch refactor (#117). --- docs/SKILL-INDEX.md | 4 + ...k8s-api-timeout-evidence-layering-guide.md | 318 +++++++++++++++++ ...ddon-vcluster-api-entry-preflight-guide.md | 333 ++++++++++++++++++ 3 files changed, 655 insertions(+) create mode 100644 docs/addon-k8s-api-timeout-evidence-layering-guide.md create mode 100644 docs/vcluster/addon-vcluster-api-entry-preflight-guide.md diff --git a/docs/SKILL-INDEX.md b/docs/SKILL-INDEX.md index abe82a1..52b1346 100644 --- a/docs/SKILL-INDEX.md +++ b/docs/SKILL-INDEX.md @@ -55,6 +55,7 @@ - [`vcluster/`](vcluster/README.md) — idc / idc1 / idc2 / idc4 中围绕 vcluster 运行 KB / KBE / addon tests 的文档集合入口;host k8s 只承载 runner / helper / bootstrap,KBE / KB runtime 以 vcluster 为 SUT - [`addon-test-environment-gate-hygiene-guide.md`](addon-test-environment-gate-hygiene-guide.md) — 环境就绪逐项坐实清单(API 路由 / 控制器身份 / CSI / 镜像分发 / staged anchors / fresh slot / capability),post-restart 禁止复用 pre-restart 事实 - [`addon-test-script-preflight-guide.md`](addon-test-script-preflight-guide.md) — **跨 line / 多 tenant / shared mutable state 视角**:把 `~/.kube/config` current-context、HTTPS_PROXY、`/tmp/kubeconfig-*.yaml` 残留等共享 client state 当 protected invariant;trigger-event-driven preflight;①程序锁 + ②fingerprint + ③fail-fast 三层防御;N=4 cross-line evidence pool(Valkey/SQL Server/Oracle/MySQL);voice-commitment-not-invariant 实证。是 `addon-test-environment-gate-hygiene-guide.md` 的 cross-line 补集 +- [`addon-vcluster-api-entry-preflight-guide.md`](vcluster/addon-vcluster-api-entry-preflight-guide.md) — vcluster 内创建任何业务资源(KB Cluster / OpsRequest / Backup)之前的 **API 入口 preflight gate**:4 项基础探测(readyz round / get-rounds / apiserver log keywords / cleanup-state)+ 1 项 install-layer 深检(vcluster Helm `sync.toHost.volumeSnapshots*` / `sync.fromHost.volumeSnapshotClasses`),first-blocker 4 分类(A control-plane / Loft proxy TLS termination / B kubeconfig / SAN / 路由 missing / C backend kube-apiserver / etcd pressure / D install-layer 配置缺漏),artifact 4-block 取证格式,controlled escalation pattern(read-only → controlled write-op → deeper read-only → install-layer fix,每步 5-item closeout),broad-grep + API timeout 假阴性反模式(exact-name probe,不是 broad grep + filter),candidate ≠ confirmed 升格门槛。比 `addon-vcluster-kb-install-preflight-guide.md` 更早一层(API 入口本身没通时 chart install 都谈不上) - [`addon-vcluster-kb-install-preflight-guide.md`](vcluster/addon-vcluster-kb-install-preflight-guide.md) — vcluster 内安装 KB / addon dependency / chaos tooling 前的 bootstrap harness preflight:CRD server-side apply、runner Helm HOME / HELM_* 可写目录、远程 chart 拉取 EOF 后的 artifact integrity、大 CRD bundle 跨 pod / runner 传输的 source sha + chunk sha + 重组 sha 校验 - [`addon-k3d-kubeconfig-loopback-fix-guide.md`](addon-k3d-kubeconfig-loopback-fix-guide.md) — k3d 默认 kubeconfig server 写成 `0.0.0.0` 在 macOS 报 EOF;统一改 `127.0.0.1` - [`addon-k3d-image-import-multiarch-workaround-guide.md`](addon-k3d-image-import-multiarch-workaround-guide.md) — k3d 节点拉 docker.io 超时 + `k3d image import` 在 multi-arch manifest 上的静默 bug;走 host `docker save` + 节点 `ctr import` @@ -71,6 +72,7 @@ 集群已经跑起来后撞到的问题,分流到对应方法论: +- [`addon-k8s-api-timeout-evidence-layering-guide.md`](addon-k8s-api-timeout-evidence-layering-guide.md) — 测试跑起来后 `kubectl` / client-go 偶发 `Client.Timeout exceeded while awaiting headers` 不要立刻判产品 fail:只读分层取证(5 层候选 L1 网络路径 / L2 apiserver / L3 etcd 后端 / L4 共享 API endpoint / L5 非 API 噪声)+ N≥5 轮 readyz/curl/kubectl 对照表 + apiserver/etcd 慢路径 keyword 集 + exact-timeout-window evidence gap 表达 + exclusion triplet(不是产品 / 不是某条 patch / 不是 controller 逻辑)+ candidate → confirmed 升格门槛(同窗 ≥2 段证据 + N≥2 复现 + owner verify)+ owner-routed handoff 小包 4 块格式 - [`addon-ops-restart-troubleshooting-guide.md`](addon-ops-restart-troubleshooting-guide.md) — Restart / RollingUpdate "卡住"时先分 `queue 入口未放行` vs `执行体内部`,再用"冻结样本 + clean restart"两段式验证 - [`addon-manual-reconcile-trigger-diagnosis-guide.md`](addon-manual-reconcile-trigger-diagnosis-guide.md) — Cluster / Component / Workload 长时间不收敛时,先留前后快照,再用无业务含义 annotation 手工触发一次 reconcile,区分"事件没推到 controller"与"controller 处理后仍判定条件不满足";强调触发会改现场,触发后不能再把现场叫纯自然现场 - [`addon-chart-vs-kb-schema-skew-diagnosis-guide.md`](addon-chart-vs-kb-schema-skew-diagnosis-guide.md) — `helm install` 报 `field not declared in schema` 时区分「chart 局部 bug」/「整代代差」/「chart 跟 KB main 但 API 未发布」三种根因 @@ -131,6 +133,7 @@ - [`docs/addon-k3d-kubeconfig-loopback-fix-guide.md`](addon-k3d-kubeconfig-loopback-fix-guide.md) — k3d 默认把 kubeconfig server 写成 `https://0.0.0.0:`,macOS / 部分 Linux 报 EOF;统一改 `127.0.0.1`(含一次性、脚本、集群创建时三种修法) - [`docs/addon-k3d-image-import-multiarch-workaround-guide.md`](addon-k3d-image-import-multiarch-workaround-guide.md) — k3d 节点拉 docker.io 超时(host 拉得动)的 host-side `docker save` + 节点 `ctr import` 注入路径;同时绕开 `k3d image import` 在 multi-arch manifest 上的静默 bug - [`docs/addon-k3d-backup-restore-prereqs-guide.md`](addon-k3d-backup-restore-prereqs-guide.md) — k3d 上跑 KB Backup/Restore 的两层环境前置:装 VolumeSnapshot CRD(让 dataprotection controller 起来)+ 建默认 BackupRepo(让 Backup CR 不再 NoDefaultBackupRepo),引擎无关 +- [`docs/vcluster/addon-vcluster-api-entry-preflight-guide.md`](vcluster/addon-vcluster-api-entry-preflight-guide.md) — IDC / remote vcluster 内创建任何业务资源(KB Cluster / OpsRequest / Backup)之前的 **API 入口 preflight gate**。引擎中立方法论:(1) **4 项基础探测 + 1 项 install-layer 深检**(readyz round / 3 轮 get-rounds / apiserver log keyword 扫描 / cleanup-state / vcluster Helm sync 配置审计);(2) **first-blocker 4 分类**(A control-plane / Loft proxy TLS termination — rc=35 + time_appconnect=0;B kubeconfig / NodePort SAN / 路由 missing;C backend kube-apiserver / etcd pressure — rc=28 timeout;D install-layer 配置缺漏 — vcluster Helm `sync.toHost.volumeSnapshots*` / `sync.fromHost.volumeSnapshotClasses` 关闭导致 mirror controller `if kind is a CRD, it should be installed before calling Start`);(3) **artifact 4-block 取证格式**(preflight evidence / install-layer config / mirror controller logs / summary);(4) **controlled escalation pattern**(read-only → controlled write-op → deeper read-only → install-layer fix,每步 5-item closeout:plan / mutation_count / impact / rollback / verify;mutation_count 即使 net-zero apply 也算 +1);(5) **broad-grep + API timeout 假阴性反模式**(API 短窗 timeout 下 broad grep + filter 会假阴性,必须用 exact-name probe;自纠也是产出);(6) candidate ≠ confirmed 升格门槛(read-only 取证可写 candidate;positive control 通过才升 confirmed)。Case D appendix 标 in-progress,install-fix positive control 仍 follow-up(写作时点未达成,引用本附录不得当 "install-fix 已确认"使用);ImagePullBackOff 不是 Type D(image source path mismatch,pod-level health BAD),按 `addon-idc-image-registry-mirror-guide.md` 走 mirror / sideload。比 [`addon-vcluster-kb-install-preflight-guide.md`](vcluster/addon-vcluster-kb-install-preflight-guide.md) 更早一层(API 入口本身没通时 chart install 都谈不上);是 [`addon-k8s-api-timeout-evidence-layering-guide.md`](addon-k8s-api-timeout-evidence-layering-guide.md) 在 vcluster 前端 control-plane / Loft 代理 layer 的兄弟篇(前者后端 etcd / apiserver,本篇前端 TLS / proxy) - [`docs/vcluster/addon-vcluster-kb-install-preflight-guide.md`](vcluster/addon-vcluster-kb-install-preflight-guide.md) — IDC / remote vcluster 内安装 KubeBlocks、addon dependency、chaos-mesh 等 Helm chart 前的 bootstrap harness preflight。聚焦四类 engine-neutral first blocker:大 CRD bundle client-side apply annotation limit(默认 server-side apply + group count)、runner pod Helm HOME/HELM_* 不可写(显式 `/tmp` writable dirs)、远程 chart pull EOF(artifact-first:外部 pull / cache + sha256 或 digest + runner 内复验后 install)、多 MB artifact 跨 pod / runner transit corruption(pod-to-pod HTTP / artifact store + chunk sha + reassemble sha + fresh-slot 清理)。与 IDC 镜像供给 doc、host-runner Job pattern、test-script preflight、first-blocker / evidence discipline 文档形成环境层闭环 - [`docs/addon-multi-ns-registry-scan-preflight-guide.md`](addon-multi-ns-registry-scan-preflight-guide.md) — 多 namespace / 多 topology 并发测试或 chaos suite 启动前的测试 scope preflight。核心 framing:"verified scope vs scan-only future-gate" 二分——本轮跑过 + 通过的 topology 写 verified 结论;未跑但 pre-flight scan 命中 docker.io 漏点的 topology 写 future-gate precondition,不混进本轮 product 结论。具体 application 是审计 live `ComponentVersion` + `ParametersDefinition.spec.reloadAction.shellTrigger.toolsSetup.toolConfigs[].image` 的 image source 一致性;reload sidecar 镜像源不在 ComponentVersion 范围内是常见盲点。N=2 MySQL grounded(task #5 functional 多 ns 并发:4 ns 跑通后 scan 出 mysql-orc / mysql-proxysql 仍含 docker.io 列入 future-gate;task #6 chaos vcluster pre-patch 三个 CR 后 pod-kill smoke 通过)。是 [`addon-evidence-discipline-guide.md`](addon-evidence-discipline-guide.md) 三规则在 multi-topology 测试场景的 layer-aware 应用 - [`docs/vcluster/addon-idc-image-registry-mirror-guide.md`](vcluster/addon-idc-image-registry-mirror-guide.md) — IDC vcluster 测试环境的镜像供给三档决策树(mirror 主路径 / ACR 直拉 / sideload 兜底)+ chart audit checklist(避免 ghcr.io / docker.io 主依赖)+ PR-built controller / DataProtection controller / syncer / addon image 的本地 build + helper pod sideload 规则(目标 commit / unique local tag / per-node import / `imagePullPolicy: Never` / pod imageID / build info)+ vcluster 内 chaos-mesh 的 host k8s ↔ vcluster 双层 syncer 注入路径;含 6 line × 3 IDC(idc / idc2 / idc4)行的 ground-truth cross-engine reuse 表(Valkey / MariaDB / SQL Server / Oracle / OceanBase / KBE 各自的 image inventory + 路径选择 + 实测 evidence);§7 syncer auto-sync open question 待 Phase 2 PoC 验证;配套 skills `idc-image-pull-fallback` / `local-build-sideload-test-image` @@ -154,6 +157,7 @@ - [`docs/addon-slock-thread-hygiene-guide.md`](addon-slock-thread-hygiene-guide.md) — Slock agent 收到消息后的注意力边界规则:先判断是否与自己职责 / DM / @mention / task / reminder 相关;无关 thread 立即 `slock thread unfollow --target ""`,#all 和 #all thread 不发言;配套 skill `slock-thread-hygiene` - [`docs/addon-pr-reviewer-routing-guide.md`](addon-pr-reviewer-routing-guide.md) — addon 代码 PR reviewer routing 方法论:先查当前 PR touched files、初版 PR、addon 目录主要作者、最近相关功能作者,再按"最近功能维护者 → 初版 / 主要作者 → addon owner → fallback reviewer → 带证据问 Weston"选择 reviewer。包含 @Musk3 安全转发格式、Kevin 汇总表、Oracle PR #1276 / PR #764 反例,配套 callable skill `addon-pr-reviewer-routing` - [`docs/addon-github-submission-discipline-guide.md`](addon-github-submission-discipline-guide.md) — 多 agent 协作 + GitHub 公开仓库的边界纪律:(1) AI provenance trailer(`Co-Authored-By: Claude` / `🤖 Generated with` / `noreply@anthropic.com`)不外漏的硬规则与兜底命令链(heredoc + `git commit --amend -m "$(... | sed)"` strip / push 前 grep 自检);(2) 多 agent 并发推同一 PR branch 的 cascade 事故响应 playbook(force-with-lease lemma:lease 锚 last-fetched remote tip 不防 fetch 后并发 push / 双向 `git log --oneline` ritual / dropped-commit owner self-recover / single-owner-execute 收口)。5 条 doctrine(A force-with-lease / B per-commit grep / C cascade single-owner-execute / D forensic 自查 / E content-delta verify)+ §5 cross-cutting rules(forensic self-review / Doctrine E shorthand / evidence-post obligation / 递归 self-application) +- [`docs/addon-k8s-api-timeout-evidence-layering-guide.md`](addon-k8s-api-timeout-evidence-layering-guide.md) — 测试跑业务样本(创建 Cluster / 跑 OpsRequest / 看 Backup 等)时偶发 `kubectl` / client-go 报 `Client.Timeout exceeded while awaiting headers` 或 `context deadline exceeded` 时的引擎中立分层取证方法。**核心 framing**:不要立刻判产品 fail,先做 read-only layered 取证。(1) **5 层候选**(L1 网络路径 / L2 apiserver / L3 etcd 后端 / L4 共享 API endpoint / L5 非 API 噪声);(2) **N≥5 轮 readyz / curl / kubectl 对照表**(采样矩阵:rc + elapsed + 关键 keyword);(3) **apiserver / etcd 慢路径 keyword 集**(`apply request took too long` / `slow read-only range request` / `etcdserver: request timed out` / `leader changed` / `TLS handshake error` / `cache sync timeout` 等);(4) **exact-timeout-window evidence gap 表达**("我没拿到 X 段的 log / metric" 也是证据,不要静默掩盖);(5) **exclusion triplet**(明确写"不是产品 / 不是某条 patch / 不是 controller 逻辑",每条排除依据各自带证据);(6) **candidate → confirmed 升格门槛**(同窗 ≥2 段证据 + N≥2 复现 + owner 拿同窗证据自己 verify,不是 owner "拍 OK");(7) **owner-routed handoff 4 块格式**(human-readable,no raw evidence dump)。是 [`addon-vcluster-api-entry-preflight-guide.md`](vcluster/addon-vcluster-api-entry-preflight-guide.md) 的后端兄弟篇(前者前端 vcluster control-plane / Loft 代理 layer / TLS 挂断,本篇后端 etcd / apiserver pressure / shared endpoint) - [`docs/addon-soak-test-result-classification-guide.md`](addon-soak-test-result-classification-guide.md) — 长跑型测试(24h+ soak / chaos / fault-injection)出结果之后的结果分类方法论。**核心 framing**:fault total / PASS-FAIL 计数无法回答"是否 ACCEPTED",必须把每条 fault 注入按"哪一层先失败"落到 4-state schema:(1) `invariant-break`(不变量破坏 → ROLLBACK)/ (2) `product-path-failure`(产品恢复路径失败)/ (3) `harness-race`(测试工具时序竞争)/ (4) `external-environmental-cascade`(外部环境级联)。**判据**:Q1(bad_ack > 0)→ Q2(cluster 终态)→ Q3(OpsRequest Failed + N≥2 自验证)→ Q4(duration 超 mean+3σ + 外部事件关联 + 对照样本)→ product-pass(mermaid 流程图)。**N≥2 自验证最小证据门槛**:harness-race 需同类 Succeed 对照、cascade 需 baseline ±1σ 对照样本;单 sample 不能下"非产品"结论。**ACCEPTED 判据**:`invariant-break = 0 AND product-path-failure = 0`,harness-race / cascade 不阻塞但触发对应修复 ticket。grounded N=3 CH30 harness-race 对照(fault-026 vs 029/033)+ N=2 CH20 cascade negative-control(fault-028 21min outlier vs 031 95s 1σ 内)+ AG quorum non-sticky 3-transition 行为附注。与 [`addon-test-acceptance-and-first-blocker-guide.md`](addon-test-acceptance-and-first-blocker-guide.md) 单次 fail first-blocker 分层方法论形成互补对子(前者聚合维度,后者单次维度) ## 案例材料 diff --git a/docs/addon-k8s-api-timeout-evidence-layering-guide.md b/docs/addon-k8s-api-timeout-evidence-layering-guide.md new file mode 100644 index 0000000..1615429 --- /dev/null +++ b/docs/addon-k8s-api-timeout-evidence-layering-guide.md @@ -0,0 +1,318 @@ +# Addon K8s API Timeout 分层取证指南 — 网络路径 vs apiserver/etcd 后端压力 vs 共享 API endpoint + +> **Audience**: addon dev / test / TL +> **Status**: draft +> **Applies to**: any KB addon +> **Applies to KB version**: any(methodology, version-agnostic) +> **Affected by version skew**: no(本文是 kubectl / client-go API timeout 分层取证的方法论,与 KB / addon / Kubernetes 版本无关;具体 keyword 集和 apiserver slow-path 行为可能随上游 K8s 版本演进,但分类逻辑不变) + +属于:方法论主题文档(不绑定单一引擎、不绑定具体 PR / patch)。 + +## 先用白话理解这篇文档 + +### 这篇文档解决什么问题 + +测试跑业务样本(创建 Cluster、跑 OpsRequest、看 Backup、看 ConfigMap 等)时,**偶发**会出现 `kubectl` / `client-go` 请求 8s/10s 内拿不到响应头,报 `Client.Timeout exceeded while awaiting headers` 或 `context deadline exceeded`。这一类失败的危险在于: + +- 看起来像产品 fail(业务 case RED / timeout),但**底层是 API 通道不响应**; +- 同一个采样窗口里现象会一时复现、一时又不复现; +- 历史日志里能看到 etcd / apiserver 后端慢路径痕迹,但当前 readyz 又是 200。 + +如果不分层归因,最常踩的错误是**把环境层的 API 超时升格为产品层 fail**(标到自家 addon / 一条具体 PR / DataProtection controller 等),然后开始改无辜代码。 + +→ 本文给出**只读分层取证**的方法论,把"网络路径 / apiserver / etcd 后端 / 共享 API endpoint / 非 API 噪声"几层先拆清,再决定该把锅交给环境 owner 还是继续在产品层调查。 + +### 读完你能做什么决策 + +- **拿到一个业务样本 RED/timeout 报告时**:能在 5 分钟内判断"先停业务样本、转入 API timeout 分层取证",还是"API 通道健康、是产品 fail"。 +- **判 first-blocker 时**:能写出 `runner/env API connectivity` 这一层,并进一步细到 `etcd/apiserver backend pressure candidate` vs `network/forwarding path candidate` vs `shared API endpoint candidate`,而不是停在"环境不稳定"这种模糊说法。 +- **要找环境 owner 时**:知道**只**送 human-readable 小包,不外发完整证据大包;知道**只**要 owner 决策/路由,不让人类替你看 raw 日志。 +- **写结论时**:知道什么时候只能写 `candidate`,什么时候才能升格为 `confirmed`;知道 N=1 单次复现 + 历史日志拼接**不足以**写 confirmed。 + +### 为什么独立成篇 + +与相邻方法论的边界: + +- [`addon-test-acceptance-and-first-blocker-guide.md`](addon-test-acceptance-and-first-blocker-guide.md) 讲的是"哪一层算 first blocker、不要混层归因"的通用框架;本文是其在 **K8s API 不响应** 这一具体场景下的展开。 +- [`addon-evidence-discipline-guide.md`](addon-evidence-discipline-guide.md) 讲的是"证据强度匹配描述强度、N=1 不写 average / confirmed";本文把它套到 API 超时的 candidate → confirmed 升格判定上。 +- [`addon-bounded-eventual-convergence-guide.md`](addon-bounded-eventual-convergence-guide.md) 讲的是"对外部状态用 bounded retry";本文借用其精神做 N≥5 轮只读对照采样。 +- [`addon-test-environment-gate-hygiene-guide.md`](addon-test-environment-gate-hygiene-guide.md) 讲的是"开跑前的环境就绪清单";本文是其反向版:跑起来之后才暴露的环境层 API 不响应如何取证。 + +--- + +## 1. 何时本文方法论 apply + +满足下面任一条件就先按本文跑分层取证,**不要**把样本直接判为产品 fail: + +1. 业务样本里出现 `kubectl get` / `kubectl logs` / client-go 请求 8s+ 没拿到响应头。 +2. 同一个 kubeconfig 下,`/readyz` 偶尔 rc=1,偶尔 rc=0;或同一条命令重跑结果不稳定。 +3. 业务样本的 RED 时间点正好落在 controller / apiserver / etcd pod 历史日志里有 `ReadIndex too long` / `apply request took too long` / `slow fdatasync` 的窗口。 +4. 跨业务样本(不同 addon、不同 PR、不同 Ops 类别)都出现同一类"等响应头超时"。 +5. 看上去像 controller 没工作(finalizer 不动、Job 不出现等),但 controller pod 本身是 Running、最近无 restart、log 拉取却偶尔卡住。 + +**判反例**(不要套本文): + +- 业务样本里 Pod 本身有 fatal assert / dump / OOM 等 pod-level 直接现象 — 走 addon / 引擎调查路径。 +- 业务样本里仅个别 namespace 内一类资源行为异常,跨 namespace 同操作正常 — 更像产品 / scope-bound 问题。 +- 单纯的 client 端 DNS 解析失败 / 本机网络断 — 先排自家测试机网络。 + +## 2. 分层模型(5 层) + +把 "API 超时" 这一现象拆成 5 个潜在 first-blocker 候选层: + +| 层 | 候选含义 | 典型证据形态 | +|---|---|---| +| L1 网络路径 | client 到 apiserver 的 TCP connect / TLS 握手慢或失败 | `curl -k -w` 的 `connect`、`tls` 段;`kubectl -v=8` 的 dial 阶段失败 | +| L2 apiserver 自身 | apiserver process 忙 / admission webhook 阻塞 / etcd client 占用线程 | apiserver 日志 `Trace[…]` 长 trace、`webhook ... took ...s`、`waited too long for ...` | +| L3 etcd 后端 | etcd 慢提交 / 慢 fsync / leader 抖 / ReadIndex 等待 | etcd 日志 `slow fdatasync`、`apply request took too long`、`waiting for ReadIndex response took too long`、commit/apply 指标延迟 | +| L4 共享 API endpoint | vcluster → host apiserver 转发、proxy、API gateway 抖动 | vcluster syncer 日志、转发路径耗时;host curl OK 但 vcluster 内 kubectl 超时 | +| L5 非 API 噪声 | failing-open webhook、控制器频繁刷 finalizer、API server reachable 但具体 path 慢 | 单类 GET/PATCH 路径慢、其他路径正常 | + +**判定口径**:一次取证不需要把 5 层全部走完,但**结论必须打 layer tag**,至少写到 `Lx candidate`,不要泛泛 "环境不稳定"。 + +## 3. 只读分层取证 baseline + +整个取证全程**只读**:不创建业务资源、不删 finalizer、不动 retained scene。 + +### 3.1 N≥5 轮 readyz / curl / kubectl 对照采样 + +用同一 kubeconfig 连续打 N≥5 轮 `/readyz`,串行 + 至少 1 轮并行;同步记录: + +- `curl http_code`(200 / 5xx / 0) +- `curl rc` +- 各段耗时:`time_namelookup`(DNS)、`time_connect`、`time_appconnect`(TLS)、`time_starttransfer`、`time_total` +- 同窗口内 `kubectl get --raw=/readyz -v=8` 的 rc 与总耗时 + +输出格式建议(每篇 owner-handoff 小包都用这张表): + +| 轮 | curl http | curl rc | DNS | connect | TLS | 首字节 | total | kubectl rc | kubectl ms | + +判定: + +- 全 200 + connect/TLS 都正常 → **L1 网络路径本轮采样健康**,不能仅凭这一轮把锅给网络;但也不能反过来说"网络无嫌疑",因为采样窗口可能恰好健康。 +- 有 connect/TLS 段明显慢(>200ms 异常持续)→ L1 候选强化,先继续拆 connect vs TLS。 +- 有任意一轮 rc≠0 + 报 timeout-awaiting-headers → L2/L3/L4 候选强化,下一步看 apiserver/etcd 慢日志。 + +### 3.2 apiserver 慢路径 keyword 集(通用) + +对当前测试环境能拿到的 apiserver 日志(host kube-apiserver 或 vcluster apiserver)做只读 grep,**只数行 + 拿示例时间点**,不要直接外发整个日志: + +- `Trace\[`:长 trace; +- `apply request took too long`:写路径慢; +- `waited too long for`:apiserver 自身 backpressure; +- `webhook` + `took`:admission webhook 拖慢; +- `failing open` / `failing close` + webhook name:失效模式; +- `Client.Timeout exceeded while awaiting headers`(自家 client 端); +- `dial tcp ... i/o timeout`(罕见,偏 L1)。 + +### 3.3 etcd 慢路径 keyword 集(通用) + +对应能拿到的 etcd 日志: + +- `waiting for ReadIndex response took too long`:读链路慢; +- `apply request took too long`:写链路慢; +- `slow fdatasync` 或 `took ... to fdatasync`:磁盘 fsync 抖; +- `leader changed` / `lost the TCP streaming connection`:leader 抖; +- `/registry/...` GET + `context deadline exceeded` / `context canceled`:health 查询本身被 etcd 慢搞挂。 + +判定: + +- 单类 keyword 计数 + 集中时间点(UTC)一起记,**不**单凭一行硬下结论。 +- 如果几条信号集中在某几分钟窗口,记录这些窗口;后续 owner 取证窗口对齐时复用。 + +### 3.4 host 节点资源 baseline + +只读拿三类信号,作为辅助: + +- 三节点 CPU / mem / disk / network pressure 同窗口快照; +- 各节点的 kubelet / etcd / apiserver pod 的 Ready / restart count; +- nodes 的 Memory / Disk / PID Pressure conditions。 + +注:**不**单凭节点快照健康就排除压力候选。压力可能只发生在短时窗,被采样错过。 + +## 4. exact-timeout-window evidence gap 表达 + +只读分层走完后,常态结论是: + +- 历史日志里**有**慢路径信号; +- 当前采样窗口里**没有**复现 timeout; +- **缺**原 timeout 同一时间窗的 host / vcluster apiserver + etcd metrics/logs + 网络/转发路径证据。 + +这种"两段拼接 + 中间缺口"是典型 candidate 形态,**严禁**升格为 confirmed。给环境 owner 时必须明写: + +1. 现在的结论强度(`Lx candidate`,不写 confirmed); +2. 已经覆盖了哪些只读证据(指向自己拿到的 keyword 计数 + 时间点 + readyz 对照表); +3. 还缺哪些**只读**信息: + - 原 timeout 同一时间窗(精确到分钟)的 host/vcluster apiserver 日志; + - 同窗口 etcd metrics/logs(ReadIndex、request duration、fdatasync、leader/commit/apply); + - 同窗口 host 网络 / 转发路径指标(连接、TLS、转发、等响应头各段); +4. 拿不到的项**明确写权限/路径缺口**,不补猜(参考 [`addon-evidence-discipline-guide.md`](addon-evidence-discipline-guide.md) 规则 B)。 + +## 5. Exclusion triplet — 把 noise 排掉再下结论 + +写"这不是产品/PR/controller 的锅"必须有依据,不能只是"觉得不像"。最少把三件套过一遍: + +| 排除哪一类 | 凭什么排除 | +|---|---| +| 不是当前 addon / 引擎产品问题 | timeout 落在 API 读取 / 日志 / readyz / 共享 CR 路径,不是某个 Pod 内部行为;其他 addon 同集群同窗口也会撞到 | +| 不是某条 patch / PR | 该 PR 已经在另一条 lane(如 same-path A)拿到 patch-version GREEN;当前样本卡在 PR 逻辑触发**之前**(apply / read / watch 阶段) | +| 不是 controller 逻辑 | controller pod Running / Ready / restart=0;同窗口的 reconcile 频率 / 错误率没异常;卡点在 client → apiserver 通道,不在 controller worker queue | + +任何一条排不掉,就**保留**该候选,不要写"已排除"。 + +## 6. Candidate → confirmed 升格判定 + +把 candidate 升格为 confirmed 需要的最低门槛: + +1. **同时间窗证据闭合**:原 timeout 同一时间窗的 apiserver / etcd / 网络/转发路径三段证据**至少 2 段**与目标层信号一致(例:apiserver 同窗口 `waited too long for` 计数尖峰 + etcd 同窗口 `slow fdatasync ≥ Ns`,则 etcd 后端压力可升 confirmed)。 +2. **可复现**:至少**两次**独立时间窗里看到同层信号 + 同层失败现象;single-window correlation 不算。 +3. **排除 triplet 全部成立**:§5 三件套都有具体证据。 +4. **owner 侧 acknowledgment**:环境 owner 看过同窗证据并同意层级归属(不是 owner "拍 OK 就行",而是 owner 拿同窗证据自己 verify)。 + +任何一条不满足,结论只能停在 `Lx candidate`。 + +> 这条对应 [`addon-evidence-discipline-guide.md`](addon-evidence-discipline-guide.md) 规则 A 的口径对应表:N=1 只能写 "出现过一次",不能写 "通常";间接旁证只能写 "提示某方向值得查",不能写 "已证明"。 + +## 7. Owner-routed evidence handoff format + +给环境 owner(不是普通 reviewer)的小包**只有**这四块,超出范围的内容压到自家 evidence root,不外发: + +1. **5 轮 readyz / curl / kubectl 对照表**(§3.1 格式); +2. **apiserver / etcd 慢日志关键词与集中时间点**(行数 + 示例 UTC 时间点); +3. **exact timeout 时窗证据缺口**(§4 精确写明,**精确到分钟**); +4. **排除 triplet**(§5),明写每一条排除依据。 + +不放进小包的内容: + +- raw 日志大段(grep 摘要为主); +- secret / token / kubeconfig 内容; +- 集群拓扑、namespace 列表等可推断的运营信息; +- 自己未验证的 narrative("可能是 etcd OOM" 等)。 + +诉求只写:「请帮忙指定/路由环境 owner 提供同窗 host/vcluster apiserver、etcd、网络/转发路径只读证据;拿不到的项明确写权限/路径缺口」。不让人类替你看大包。 + +### 7.1 多 agent 安全审核链(route → review → send) + +当组织里 owner-routing 不是直发,而是要经过中间 agent / PM / 安全审核(典型形态:route agent 收集 ask → safety agent 二次审 → send agent 一次性发到外部 IM 群),TL 这边要遵守的几条规则: + +1. **保留 ask 主导权**:route agent 可以 pause、可以提"太脚本化 / 太长 / 不脱敏"等格式异议;但**不要让中间 agent 自行裁剪 ask 的技术逻辑**(步骤数量、时窗、缺口写法)。被否决时,TL 自己改写格式、保留同样的覆盖面,再走一次审核。 +2. **格式冲突要向上提**:下游 owner 要"具体步骤和命令"、safety agent 要"不要长脚本不要落本地文件"——这两件事是直接冲突的格式约束,不是 TL 一个人能拍的。把冲突点拎清楚交 HRBP / management 拍一次"prose+1-2 例 vs bash 长清单"。 +3. **示例命令的纪律**:如果必须给命令,**每类只给 1–2 条示例级命令**,加一句"按已有运维方式等价执行也可以";命令里不写 token / kubeconfig 路径 / 入口 IP / 机器登录信息 / 任何写/删/重启/patch/exec 改的操作。 +4. **返回口径要预先锁**:让 owner 回**单一摘要表**(有/无命中 / 最大耗时 / 是否突变 / 无权限 / 无路径),**不要附件回传**、**不要群里贴 raw logs / pod yaml / describe 原文 / 凭据**。"无权限/无路径"也算证据,原样收。 +5. **回执路径要单一**:所有 owner 回执回 route agent → TL 一处;TL 再 sync 给原 routing owner(即使原 owner 静默也要 sync)。"并行 routing 不是越级"——这话要在 sync 里明写一次,保留原 owner 决策权。 + +写成检查清单: + +``` +□ ask 主导权由 TL 行使(不让 PM trim 技术逻辑) +□ 格式冲突 (prose vs script) 由 HRBP/management 拍 +□ 命令只给 1-2 例示例 + "等价执行也可以" +□ 回执只收摘要表,无 raw logs / 凭据 / 附件 +□ 缺口("无权限"/"无路径") 也算证据 +□ 回执路径单一,TL 再 sync 给原 routing owner +``` + +## 8. 反模式(不要做) + +- ❌ 一次采样健康就写"网络无问题"。 +- ❌ 历史日志里看到 fdatasync 慢 → 直接写 "etcd 压力 confirmed"。 +- ❌ 给环境 owner 转完整 evidence root(让人类替你读大日志)。 +- ❌ 在 API 超时未闭口前继续刷业务样本,把环境层 RED 计入产品 N=...。 +- ❌ 在 chat / evidence 里贴 token、密码、license、secret value。 +- ❌ 拿不到的证据补猜("应该是 etcd 慢")。 +- ❌ 把 candidate 写到 release-readiness 判定里:candidate / setup-only / live-gate-only 都不是 release-ready。 +- ❌ 给环境 owner 发 **bash 长脚本式 ask**(一整块 100+ 行可粘贴执行脚本、落本地文件、附件回传流程)。即使全部只读,**外发环节的安全审核也会驳回**——脚本化形态本身就被视为"把 owner 当成执行排查脚本的人"。改写成 prose 步骤 + 每类 1–2 例命令 + "等价执行也可以" 即可。 +- ❌ 强切并行 routing 而不同步原 routing owner,让原 owner 觉得"问了等于没问"。任何并行 routing 都要在原 owner 的 DM 里写一次"不是越级、随时可接回"。 +- ❌ 连发 nudge(同一 owner < 2h 内 ≥3 次)。packet + 1 次 nudge + reminder snooze 是上限;继续没回执时升级为 HRBP/management 决策外扩,而不是再纠缠原 owner。 + +## 9. 与相邻 doc 的协作 + +- 用 [`addon-test-acceptance-and-first-blocker-guide.md`](addon-test-acceptance-and-first-blocker-guide.md) 做层级 tag; +- 用 [`addon-evidence-discipline-guide.md`](addon-evidence-discipline-guide.md) 把 N、强度、描述对齐; +- 用 [`addon-bounded-eventual-convergence-guide.md`](addon-bounded-eventual-convergence-guide.md) 做 N≥5 轮采样; +- 真要发布层判定,参考 [`addon-ship-readiness-multi-phase-validation-guide.md`](addon-ship-readiness-multi-phase-validation-guide.md):candidate 不能写 release-ready。 + +--- + +## 附录 A:案例(draft — owner 证据未到,结论仍只到 candidate) + +> 本附录是**单一案例**抽象示意,不写引擎特化指令、不挂任何具体 PR 的 confirmed 结论。当 owner-routed 同窗证据回来后,再决定是否补 `已升格 confirmed` 章节。 + +**场景**:一条业务 lane(数据库 addon 的 backup 删除 TTL 验证)在跑业务样本时拿到 `Client.Timeout exceeded while awaiting headers`。runner 在 BackupPolicy / Backup CR 读取阶段就 RED,原本要验的产品行为(TTL + buffer 后 Job/Pod NotFound)根本没跑到。 + +**已做的只读取证(§3)**: + +- N=5 readyz/curl/kubectl 对照表:本轮全 200,connect/TLS/starttransfer 都健康,`kubectl readyz` 全 rc=0。→ L1 本轮采样健康。 +- apiserver 慢日志:观察到 `waited too long for`、长 Trace、failing-open admission webhook 噪声若干。 +- etcd 慢日志:`waiting for ReadIndex response took too long` 41 行;`apply request took too long` 126 行;`slow fdatasync` 9 行,单次最高 3.553s;`/registry/health` GET 出现约 2s 后 `context deadline exceeded / canceled`。集中在历史几个分钟窗口。 +- 三节点 CPU/mem/disk 在当前采样窗口内 pressure False。 + +**已做的 exclusion triplet(§5)**: + +- 不是产品:timeout 落在 API 共享读取路径(BackupPolicy / BackupRepo / readyz / logs 都撞过),不是数据库 pod 内部。 +- 不是某条 patch:同一 patch 在 same-path A lane 已拿到 patch-version GREEN,业务 case 卡在 apply / watch 阶段之前。 +- 不是 controller 逻辑:controller pod Running / Ready / restart=0;reconcile 行为正常;卡点在 client → apiserver 通道。 + +**当前结论强度**:`L3 etcd/apiserver backend pressure candidate`,**未** confirmed。 + +**缺什么才能升格**:原 timeout 同窗(精确到分钟)的 host/vcluster apiserver 日志 + etcd metrics/logs(ReadIndex、request duration、fdatasync、commit/apply)+ host 网络/转发路径指标;任意 ≥2 段同窗证据与 L3 信号一致才能升格 confirmed。 + +**保留状态**:业务 lane 暂停;不刷新业务样本;retained scene 已留存 finalizer/ownerRef/events;patch-version GREEN 维持不写 release-ready。 + +--- + +## 附录 B:常见 keyword 速查(grep-friendly) + +apiserver(kube-apiserver / vcluster apiserver): + +``` +"Trace\[" +"apply request took too long" +"waited too long for" +"webhook" + "took" +"failing open" +"Client.Timeout exceeded while awaiting headers" +``` + +etcd: + +``` +"waiting for ReadIndex response took too long" +"apply request took too long" +"slow fdatasync" +"took .* to fdatasync" +"leader changed" +"/registry/.*context deadline exceeded" +"/registry/.*context canceled" +``` + +client 侧(kubectl / client-go): + +``` +"Client.Timeout exceeded while awaiting headers" +"context deadline exceeded" +"dial tcp .* i/o timeout" +"connection refused" +``` + +注:keyword 只用于 grep 计数 + 取示例时间点,不外发整段。 + +--- + +## 附录 C:owner-routed cadence-and-escalation timeline(占位 — 待 owner 证据回来填充) + +> 本附录为 §7 / §8 抽取的一次实际 cadence 时间线骨架,**用于沉淀升级路径模式**,不写引擎 / PR / 主机名 / 凭据等具体身份。 +> 当 owner 证据或缺口表回来时,按下表填空,作为方法论的实证案例;**回执前不填**。 + +| Phase | 触发条件 | 行动 | 时间间隔上限 | +|---|---|---|---| +| P0 packet | 业务样本 RED + 已完成 §3 N≥5 + exclusion triplet | 给原 routing owner 4-block 小包(§7) | 同小时内 | +| P1 nudge | packet 后 owner 静默 | 1 次 nudge,引用同一 packet | ≥30 min after P0 | +| P2 reminder snooze | nudge 后仍静默 | 设 reminder,不连发 nudge | ≥30 min after P1 | +| P3 binary to HRBP | reminder fire 仍静默 | 给 HRBP 提交"继续等 vs 切并行" 二选一 | ≥1h after P2 | +| P4 parallel routing | HRBP 拍并行 | 通过 PM agent 异步路由备选 owner,**同步原 owner**"不是越级、可接回" | HRBP 决定即刻 | +| P5 多 agent 安全审核链 | 备选 owner 要求"具体命令"但 safety agent 不允长脚本 | TL 主导改写为 prose+1–2 例命令,走 PM → safety review → send | 同段内闭环 | +| P6 owner 回执 | 备选 owner 给摘要表或缺口写法 | TL 按"证据补充"处理,**不直接升 confirmed**;同步原 owner | owner 回执到 1h 内 | +| P7 升格判定 | §6 升格门 ≥2 项满足 | 才在 doc / 结论里写 confirmed;任何缺口仍标"无权限/无路径" | 仅在 P6 之后 | + +— 实证案例(具体时间点、关键词命中数、缺口分布)待 P6 owner 回执后填入;在此之前**不**写具体身份与数字。 diff --git a/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md b/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md new file mode 100644 index 0000000..73bc28a --- /dev/null +++ b/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md @@ -0,0 +1,333 @@ +# vcluster API 入口 Preflight Blocker 分类指南 + +> **Audience**: addon dev / test / TL — 任何在 vcluster 里跑 KB Cluster / Backup / Ops 测试的 addon line +> **Status**: draft — 主方法论稳定;附录 D install-fix positive control 仍 in-progress(写作时点未达成,详见附录 D Status 字段) +> **Applies to**: 任何 KB addon(引擎中立) +> **Applies to KB version**: any(preflight 方法论与 KB / addon 版本无关) +> **Affected by version skew**: no(本文是 vcluster API 入口的方法论 gate,不依赖具体 KB / addon / vcluster Helm 版本;附录 D 案例里 `sync.toHost.volumeSnapshots*` 等具体 flag 名称随 vcluster 版本可能变化,但分类逻辑不变) +> **Sibling docs**: +> - 同目录(`docs/vcluster/`):[`addon-vcluster-kb-install-preflight-guide.md`](addon-vcluster-kb-install-preflight-guide.md)、[`addon-idc-image-registry-mirror-guide.md`](addon-idc-image-registry-mirror-guide.md) +> - 父目录(`docs/`):[`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md)、[`../addon-evidence-discipline-guide.md`](../addon-evidence-discipline-guide.md) + +属于:方法论主题文档(不绑定单一引擎、不绑定具体 PR / patch)。 + +## 先用白话理解这篇文档 + +当一个测试 runner 要去 vcluster 里操作(创建 Cluster / 跑 OpsRequest / Backup 等)之前,**第一件事不是创建业务资源,是验证 vcluster API 入口本身还能用**。 + +如果 vcluster API 入口已经坏了(kubeconfig 缺 / TLS 挂断 / apiserver 卡死 / install-layer 缺组件),这时候硬上业务资源只会得到两种结果: + +1. **快错**:runner 在第一步就 FAIL,根因在环境层不在产品层 → first blocker 错配,容易写出错误的"产品 fail"结论 +2. **慢错**:业务资源勉强建出来,controller 后续 reconcile 撞到同一个 API 不稳,retained scene 留得乱七八糟,清理也撞同样的问题 + +正确做法:**业务资源创建之前,强制一道 vcluster API 入口 preflight gate**。preflight 不过,不创建任何业务资源,把 first blocker 锁在 API 入口层。 + +读完你能: +- 拿到一个 vcluster 失败现象时,5 分钟内判断 first-blocker 落在 4 种 candidate 中的哪一种(TLS / kubeconfig / backend / install-layer) +- 写出有 layer-tag 的 candidate 结论(如 `vcluster control-plane TLS termination candidate, not confirmed`),而不是"环境不稳定" +- 知道什么时候只能写 `candidate`,什么时候才能升格为 `confirmed` +- 知道 pod-level mitigation(rollout restart)失败本身就是证据,不要继续重启 + +## 与现有 docs 的关系 + +| 现有 doc | 它讲什么 | 本文不重复处 | +|---|---|---| +| [`../addon-test-acceptance-and-first-blocker-guide.md`](../addon-test-acceptance-and-first-blocker-guide.md) | first-blocker 5 层分层方法论(理论) | 本文是它"层 1 环境门禁"的一个具体子门:vcluster API entry 这道门 | +| [`addon-vcluster-kb-install-preflight-guide.md`](addon-vcluster-kb-install-preflight-guide.md) | vcluster 内**安装 KB / chaos 等 chart** 前的 preflight | 本文管的是**比那更早一层** — vcluster API 入口本身(KB 还没装就卡 API 通) | +| [`../addon-test-environment-gate-hygiene-guide.md`](../addon-test-environment-gate-hygiene-guide.md) | post-restart / 路由 / 控制器身份 gate | 本文 anchor 在 vcluster control-plane / Loft 代理这条 path | +| [`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md) | 后端(etcd / apiserver)压力候选 + owner-routed 取证 | 本文专门讲**前端 vcluster control-plane layer** 失败(time_appconnect=0 → TLS 都没握上);两文互为前/后端 layer | + +## 1. preflight gate 应该测什么 + +> 方法论是引擎中立的,不绑 SQL Server / Oracle / 任何具体 addon。 + +**4 项基础探测 + 1 项 install-layer 深检**,全部只读,不创建任何资源。 + +### 1.1 readyz round(API 通道健康) + +``` +对 vcluster apiserver $HOST/readyz curl, 3 轮(间隔 5-10s) +观察: +- rc=0 + HTTP 200 + total_time < 2s = 健康 +- rc=35 + SSL_ERROR_SYSCALL + time_appconnect=0 = control-plane / 代理层 TLS 挂断(Type A) +- rc=28 + total_time ≈ 你的 curl --max-time = backend / kube-apiserver pod 慢(Type C) +``` + +### 1.2 get-rounds(API 真实可用) + +``` +3 轮,每轮 3 个 kubectl 命令:get nodes / get ns / get deploy -A +观察: +- rc=0 elapsed<5s = 健康 +- rc=124 timeout + context deadline = 真后端 backend pressure(Type C) +- rc=1 + Unable to connect / TLS handshake = control-plane / 代理层(Type A) +- rc=0 但 list 缺预期项 = 可能是 API access gap(见 §5 假阴性 lesson) +``` + +### 1.3 vcluster apiserver log 关键词扫描(只有当你能拿到 log 时) + +``` +grep 关键词(引擎中立): +- "context deadline exceeded" +- "apply request took too long" +- "TLS handshake error" +- "leader changed" +- "etcdserver: request timed out" +- "connect: operation not permitted" # install-layer 的典型信号 + +如果拿不到 log(host SSH 禁 / vcluster log 路径无权限)→ 写 "permission/path gap",不是猜 +``` + +### 1.4 cleanup-state(确认没有残留干扰) + +``` +kubectl get all -n → 应该 "No resources" +不创建任何资源,这一项只是 sanity 确认 +``` + +### 1.5 install-layer 深检(Type D 候选触发时) + +当 §1.1-1.4 走完仍无法定位,且观察到下列任一信号,触发深检: +- pod-level health 看起来 OK(容器 Ready / restart 不一定为 0 但能跑起来),但 external readyz 仍失败 +- 关联 controller 持续 CrashLoopBackOff(如 KB-DP / addon controller),tail log 反复报 missing capability +- vcluster internal API service(如 `https://10.96.X.X:443`)报 `connect: operation not permitted` + +深检方法: +- 列 host k8s + vcluster 期望的 CRD 清单,对照 `kubectl get crd` **精确名**查找(**不要** broad grep — 见 §5 lesson) +- 列 install-time 期望生成的 ConfigMap 清单(来自 helm template / addon 安装文档),对照 `kubectl get cm ` 找 missing +- 扫 KB / addon controller pod 状态 — CrashLoopBackOff = install-layer 信号 +- 扫 controller log 关键词:`couldn't load capability`, `failed to get RESTMapping`, `connect: operation not permitted`, `cache sync timeout`, `if kind is a CRD, it should be installed before calling Start` +- 检查 vcluster Helm values `.sync.*.enabled` 矩阵 — 缺失的 sync flag 会让对应 CRD/资源在 vcluster view 不可见 + +## 2. first-blocker 分类(拿到 preflight artifact 之后下结论) + +### 2.1 四种首要类型 + +**A. vcluster control-plane / 代理层 TLS 挂断** +- 特征:`rc=35 SSL_ERROR_SYSCALL`, `time_connect` 很小(TCP 通), `time_appconnect=0`(TLS 没起来), `total_time` 接近你的 timeout +- 根因候选层:Loft 代理 / vcluster syncer / 入口 LB / vcluster control-plane pod / 中间证书链 +- ⚠️ **不要**直接归为 "etcd/apiserver backend pressure" — TLS 都没握上,后端压力还根本没传过来 +- **可写的 candidate wording**:`vcluster control-plane / proxy-layer TLS termination candidate, not confirmed` +- 升 N:同一 kubeconfig 同型重现 ≥2 次 → `same-type persistent`,但仍 not confirmed + +**B. kubeconfig 缺失 / provisioning gap** +- 特征:文件不存在 / kubeconfig 里指向的 cluster 没有 / 证书已过期 +- 根因层:env provisioning 没做完,runner 这台机器从来没拿到过这条入口 +- ⚠️ **不要**和 A 混 — 这不是 TLS 挂断,是入口都没建好 +- **可写的 candidate wording**:`runner/env - kubeconfig missing or not provisioned` +- 升 N:不适用(是 binary 存在/不存在,不是统计概率事件) + +**C. backend pressure(后端 apiserver / etcd 真慢)** +- 特征:`rc=124 timeout` + `context-deadline-exceeded` + TLS 握上了(`time_appconnect>0`) +- 根因层:kube-apiserver pod / etcd 后端 +- 详细方法论见 [`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md),本文不重复 + +**D. install-layer incomplete(vcluster / addon 安装层缺组件)** +- 特征: + - pod-level health OK 但 external readyz 仍失败 — pod-level mitigation 无法恢复入口 + - vcluster internal API service 报 `connect: operation not permitted` — 不是 TCP 不通也不是 TLS 挂断,像 NetworkPolicy / 配置策略阻断 + - vcluster view 缺 install-time expected CRD(如 `VolumeSnapshot.snapshot.storage.k8s.io/v1` 等 addon 依赖的 CRD) + - 关联 controller 持续 CrashLoopBackOff,restart 高,tail log 反复报 `if kind is a CRD, it should be installed before calling Start` +- 根因层:install 流程没跑完 / 跑错版本 / 跑漏关键步骤 / vcluster sync config 显式 disable 某些资源 sync;**不是产品逻辑 bug,也不是 backend pressure** +- ⚠️ **不要**直接归为 Type A — Type D 的 pod 本身能 Ready,TLS 握手层不一定挂;也**不要**归为 Type C — 表现不是后端慢而是后端被某条路径策略拒 +- **可写的 candidate wording**: + - `install-layer incomplete candidate, not confirmed`(主候选) + - 子项:`missing CRD ` / `missing ConfigMap ` / `vcluster-internal API access denied (NetworkPolicy candidate)` / `vcluster sync config disabled for ` +- 升 N:Type D 一般是 binary 状态(装了 / 没装);但**多个 addon controller 同时报同一缺失** = 跨 controller 同型,升强度 +- post-failure 处理:**install-layer 修复属于高于 pod-level mitigation 一级的风险动作**(重装 / 加 CRD / helm upgrade / 改 NetworkPolicy),必须 owner / tech-authority 拍,**不**私自扩授权 +- ⚠️ **不要**把 ImagePullBackOff 误归 Type D:preflight 全绿之后业务 workload 撞 `ImagePullBackOff` 是另一条 path —— vcluster syncer 把 pod 调度到 host shadow,host containerd 拉不到某些 image。pod-level health 这里**是坏的**(pod Pending / ContainerCreating),信号也不是 `connect: operation not permitted`,而是 `ErrImagePull` / `manifest unknown` / `i/o timeout to registry`。这属于 **image source 路径 mismatch**(不是 install-layer incomplete),按 [`addon-idc-image-registry-mirror-guide.md`](addon-idc-image-registry-mirror-guide.md) 的 per-source decision matrix 选 mirror / sideload 路径,不要走 §4 的 install-layer 修复升格。 + +### 2.2 cross-pin discipline(跨 line 升 N 规则) + +| 现象组合 | 怎么写 | +|---|---| +| 一条 line N=1 同型(一个 sample) | candidate not confirmed,**不**外推到其他 line | +| 一条 line N=2 同型 persistent(≥21min interval 再现) | `same-type persistent`,仍 not confirmed,**不**外推 | +| 两条 line 第一 blocker 不同型 | **不**算 N=2 of any kind,各自分别归类,cross-pin 写两条 | +| 两条 line 第一 blocker 同型 | 才升 `cross-line same-type N=2`,此时**才**有理由 routing 整 owner 路径 | + +### 2.3 反模式 + +- ❌ 写 "环境不稳定" — 太模糊,没有 layer +- ❌ 把 TLS 挂断写成 backend pressure(它们是不同 layer) +- ❌ 一条 line 同型 N=2 就跨 line 外推 — owner 路径会被错误升级 +- ❌ 写 "owner declined" 当噪音吞掉 — owner declined 也是证据 +- ❌ preflight 没过就创建业务资源然后 retained scene 留一堆要清 + +## 3. artifact 包结构(4-block format) + +给 owner / 团队 / TL review 看的,必须固定结构: + +``` +artifacts/-vcluster-preflight--/ + 00-context.txt # KUBECONFIG, target vcluster name, observation window, agent boundary 声明 + 01-readyz-rounds/ # 3 轮 curl /readyz,每轮独立 rc + time + 02-get-rounds/ # 3 轮 kubectl get nodes / ns / deploy-A,每轮独立 rc + elapsed + 03-vcluster-apiserver/ # log 整盘(如果有权限),或 "permission/path gap" 声明 + 04-grep-summary.tsv # 关键词 hit count,表头: keyword|count|first_hit_ts|last_hit_ts|sample_line_200c + 05-cleanup-state/ # kubectl get all -n ,应该 No resources + 06-install-layer/ # 触发 §1.5 时:CRD exact-name probe / ConfigMap probe / controller pod logs / vcluster Helm sync values + manifest.txt # 文件清单 + 单 sha256 + SHA256SUMS # 全部文件单独 sha256 行 +``` + +artifact 必须 `tar -cf -` 然后 sha256,用 attachment id 提交,不贴 raw bundle 内容,不贴 token/kubeconfig 内容。 + +## 4. controlled escalation pattern(4-5 阶段渐进式深检) + +当 preflight gate 报红,但无法立刻定位是 A/B/C/D 哪一种时,按下列 phase 模式渐进式深入。这是 evidence-discipline 的**核心结构** — 每 phase 单独 closeout,不混合。 + +| Phase | 类型 | 目的 | 边界 | +|---|---|---|---| +| 1 | read-only host inventory | 拿 vcluster sts / mirror pods / svc / events 基线 | `mutation_count=0`,host kubeconfig 范围,no host SSH,no secret data read | +| 2 | read-only deeper | 探 controller logs / ConfigMap / event 噪声分布 / pod uptime / restart count | 同 Phase 1 | +| 3 | controlled write-op(pod-level mitigation) | 1 次 rollout restart 试 — 低 risk 写操作,验证是否纯 pod 内问题 | `mutation_count=1`,单一对象,rollback = 等 controller 自然恢复或再做反向 rollout;失败本身就是证据,不要重复试 | +| 4 | read-only install-layer deep check | Phase 3 失败 → 进 install-layer 深检(§1.5) | `mutation_count=0` | +| 5+ | install-layer 修复 | helm upgrade / 补 CRD / 补 ConfigMap / 改 NetworkPolicy | **每个 sub-step 单独 plan + mutation_count + impact + rollback + verify**,启动前书面 green light | + +每个 phase 输出独立 artifact + closeout 5 项: + +1. **artifact + sha**:dir / tar sha / attachment id / mutation_count / redaction check +2. **候选(refresh)**:当前 phase 后哪些 candidate 强化、哪些弱化、哪些 disproved +3. **排除项(累积)**:明确写"不是 X",每条排除依据 +4. **是否需要下一 phase write-op**:yes / no +5. **下一 phase plan + rollback**:写完整动作 + 回滚步骤 + +### 4.1 mutation_count discipline + +- 每次 write-op 严格 `+1`;net-zero(apply 实际是 `configured` 而非 `created`)也算 1 次 mutation 入账 +- `kubectl apply` 输出区分 `configured` / `created` / `unchanged` — 记录到 artifact,是 rollback 设计的关键 +- pre-existing 资源**不允许** rollback 时删除(会破坏 idempotent 状态),这条写进 rollback contract + +### 4.2 pre-state / post-state 必须配对 + +每次 controlled write-op 必须: + +``` +[pre-state] → 取当前状态快照(exact-name probe,§5 lesson) +[执行命令] → 完整命令 + 输出 rc +[post-state] → 取执行后状态快照 +[verify] → 目标 invariant 是否达到 +[rollback] → 如何回到 pre-state(写成可执行步骤,不写"如有问题再说") +``` + +pre-state 是**最后一道防线** — 如果发现 pre-state 与 candidate 假设不符(如"缺 X"其实"X 存在"),**立即终止当前 phase**,self-correct,重新归类候选,**不要继续执行命令**。 + +## 5. 重点 lesson:broad-grep + API timeout = 假阴性 + +### 5.1 现象 + +某 phase 用 `kubectl get crd | grep ` 报告 "host 缺 N 个 CRD"。下一 phase 在 apply 前先 `kubectl get crd ` 取 pre-state,发现**目标 CRD 全部 pre-existed**。 + +假阴性根因:host API 当时 `context deadline exceeded` 导致 `kubectl get crd` 返回**不完整 list**,再 grep 自然 miss。 + +### 5.2 discipline + +- 验证 "缺 X" 类候选**必须**用 `kubectl get -o yaml`(或 `-o name`)精确查,**不可** broad list + grep +- 任何 `kubectl get` 出现 `context deadline exceeded` / `connection refused` / `i/o timeout` 都不能视为 "不存在",应标 "**API access gap, evidence inconclusive**" +- API 当时不稳定时,broad grep / `wc -l` / `count != expected` 都**不能**作为 "缺失" 证据 — 必须 evidence layered:**exact-name probe** + **post-condition observe** + **idempotency check** +- controlled write-op 的 pre-state 是兜底防线 — 一旦发现"缺 X"假设站不住,**立即** self-correct,把 apply rc 区分(`configured` vs `created`)记录到 artifact + +### 5.3 self-correction culture + +当一个 phase 主动 surface 自己上一 phase 的误读 + 拒绝盲做 rollback delete + 把误读路径写进 closeout,而不是默默盖过: + +- 这是 evidence-discipline 团队的 critical signal +- 不能为了"保持节奏"让 false-positive / false-negative 留在 audit trail 里 +- closeout 5 项格式里 "候选(refresh)" 这一栏专门用于公开 disproved 子候选 + +## 6. 边界守(agent self-discipline) + +- agent-local only(不切 host kubeconfig 越权 / 不 SSH 宿主 / 不越级问 owner) +- 不创建任何业务资源(preflight 没过的前提下) +- 不强删 finalizer,不动 product 代码,不 nudge open PR review +- 拿不到的 log / metric / owner 回执 → 全部按 "permission/path gap" 字面记录,不猜 +- 升 `confirmed` 需要 backend 层证据(host SSH 拿 etcd metrics / vcluster control-plane log / Loft 代理 log 等)— 这部分按 [`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md) §7 owner-routed 路径走 + +## 7. candidate → confirmed 升格 + +将 candidate 升 confirmed 需要: + +1. **同窗口证据闭合**:至少 2 段同时间窗证据与 layer 候选一致 +2. **可复现**:≥2 次独立时间窗看到同型现象 +3. **排除 triplet 全部成立**(产品 / patch / controller 逻辑都明确排除) +4. **owner 侧 verify**:环境 owner 拿同窗证据自己 verify(不是 owner "拍 OK 就行") +5. **install-fix positive control**(Type D 专属):install-fix 后 vcluster internal API access 恢复 + 关联 controller exit CrashLoop + external readyz 恢复 200 + +任何一条不满足,结论只能停在 `Lx candidate`。 + +--- + +## 附录 A:Type A — vcluster control-plane TLS termination case + +> **Status**: candidate not confirmed(缺 owner-side Loft proxy / vcluster control-plane pod log) + +- **现象**:curl /readyz rc=35 SSL_ERROR_SYSCALL,`time_appconnect=0`,total~5s,3/3 轮一致 +- **get-rounds**:rc=124 timeout 9/9 行 elapsed=12s 全 context-deadline +- **分类**:Type A 控制面 / 代理层 TLS 挂断(vcluster syncer / Loft proxy 层候选) +- **处理**:业务资源 0 创建,lane → paused-on-owner-routing +- **没升 confirmed 因为**:缺 host 侧 vcluster control-plane pod log + Loft proxy 上游证据 — 由 owner 拿 + +## 附录 B:Type B — kubeconfig missing case + +> **Status**: binary 现象,confirmed by file inspection + +- **现象**:`~/.kube/config---` 不存在;本机只有 host kubeconfig 和跨 engine 不可用的旧 kubeconfig +- **分类**:Type B kubeconfig missing / provisioning gap +- **处理**:**不**升 N=2 of Type A(是不同型 blocker),cross-pin 单独记录 +- **没自动 fallback 到 host kubeconfig 因为**:agent boundary 禁 host SSH + +## 附录 C:Type C — backend pressure case + +> **Status**: candidate not confirmed(historical-window only,无 fresh-window 同窗证据) + +- **现象**:vcluster apiserver log `context deadline exceeded`,host apiserver 同型,`time_appconnect>0`(TLS 握上了,是后端慢) +- **分类**:Type C backend pressure 候选 +- **处理**:详见 [`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md) 附录 A(owner declined,permission/path gap closeout) + +## 附录 D:Type D — install-layer incomplete case(in-progress) + +> **Status**: 写作时点 candidate strongest, **not confirmed**。candidate ≠ confirmed — install-fix positive control(§7 升格门 5)**尚未达成**。本附录记录截至 Phase 5.3 的取证编年,**不**作为"已修复"或"install-fix 已验证"的证据使用;任何引用本附录得出"install-fix 成立"的结论都不成立,必须等本附录后续 revision 显式更新到"已升 confirmed"段时才采信。 + +**取证编年**(controlled escalation Phase 1-5,host kubeconfig 范围,无 host SSH,边界严守): + +| Phase | 类型 | mutation_count | 关键发现 | +|---|---|---|---| +| 1 | read-only host inventory | 0 | vcluster syncer pod uptime 7d,addon controller CrashLoopBackOff 7d1h | +| 2 | read-only deeper | 0 | controller 缺一个 ConfigMap;~36000 BackOff events;候选 refine 到 "syncer watch 慢" + "controller cascading" | +| 3 | controlled write-op (rollout restart 1 次) | 1 | pod 重建 Ready,**但 external readyz 仍 rc=28 SSL connection timeout**;**pod-level mitigation 不解决问题** — "syncer overload" less supported | +| 4 | read-only install-layer deep check | 0 | vcluster internal API `connect: operation not permitted`;vcluster view 缺 `VolumeSnapshot.snapshot.storage.k8s.io/v1` CRD | +| 5.1 | install-fix preflight (broad grep) | 0 | **[后被纠正]** broad grep 输出 "host 缺 3 CRD" — 实际是 host API timeout 期间不完整 list 的假阴性 | +| 5.2 | controlled write-op apply CRDs (rollback 不删 pre-existing) | 1 (net=0) | **self-correction**:3 CRD pre-existed;`kubectl apply` rc=`configured` not `created`;候选 "host 缺 CRD" **disproved**;mirror controller 仍 CrashLoop | +| 5.3 | read-only vcluster sync baseline | 0 | vcluster Helm values 显式 disable 3 个 snapshot sync flags(`sync.fromHost.volumeSnapshotClasses.enabled=false` + `sync.toHost.volumeSnapshots.enabled=false` + `sync.toHost.volumeSnapshotContents.enabled=false`);mirror controller log `if kind is a CRD, it should be installed before calling Start {"kind": "VolumeSnapshot.snapshot.storage.k8s.io"}` — clean root cause chain | + +**分类(截至 Phase 5.3,候选状态)**: +- Type D install-layer incomplete 候选(主,evidence 强但未 confirmed)— 子候选收敛为 **vcluster install-time sync config drift** +- Type A TLS termination 候选保留(external readyz rc=28 SSL timeout — 与 D 解耦,单独 lane) +- Type C backend pressure 候选保留(host API 偶发不稳 → 5.1 假阴性的成因,伴随 noise) + +**排除项**(累积): +- NOT addon 产品 RED(失败层不在 addon 容器内) +- NOT pod-level health(Phase 3 验证) +- NOT syncer overload(Phase 3 less supported) +- NOT controller 逻辑 bug(CrashLoop 是 install state 导致,不是逻辑错) + +**install-fix 升 confirmed 的条件(未达成)**: + +下列三条同时成立时,**才**可以把"vcluster install-time sync config drift"从 candidate 升 confirmed;任何一条不成立或缺证据,结论必须停在 candidate: + +- 若 install-fix 执行(如 `helm upgrade` enable 3 个 snapshot sync flags),**且** post-state vcluster view 出现对应 CRD 精确名 match,**且** +- 关联 controller 在 post-state 退出 CrashLoopBackOff(restart 计数稳定,最后一次 restart 时间停在 install-fix apply 之前),**且** +- external readyz 在同时间窗恢复 200(或与 Type A 单独 lane 解耦后该 lane 自身闭环) + +未达成上述条件之前,本附录所述 Phase 5.3 candidate **不**等同于已修复事实;引用本附录写"install-fix 已验证"是越界。post-state evidence 待后续 phase 的 5-item closeout(plan / mutation_count / impact / rollback / verify)落 artifact 后,再由本附录追加一个 "已升 confirmed" 段落。 + +--- + +**Reference docs**: +- [`../addon-test-acceptance-and-first-blocker-guide.md`](../addon-test-acceptance-and-first-blocker-guide.md) +- [`addon-vcluster-kb-install-preflight-guide.md`](addon-vcluster-kb-install-preflight-guide.md) +- [`addon-idc-image-registry-mirror-guide.md`](addon-idc-image-registry-mirror-guide.md) +- [`../addon-test-environment-gate-hygiene-guide.md`](../addon-test-environment-gate-hygiene-guide.md) +- [`../addon-k8s-api-timeout-evidence-layering-guide.md`](../addon-k8s-api-timeout-evidence-layering-guide.md) +- [`../addon-evidence-discipline-guide.md`](../addon-evidence-discipline-guide.md) From 6239314488acc53b2cc86cf66dcea2a0007346ad Mon Sep 17 00:00:00 2001 From: Ava Date: Tue, 12 May 2026 23:19:05 +0800 Subject: [PATCH 2/2] docs(vcluster): backfill Appendix D.2 Phase 5.5-5.7 in-progress case + D.3 dispatch-design-flaw lesson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D.2 records the Phase 5.5 install-layer change execute (candidate fix scope) and the Phase 5.6/5.7 read-only + Phase B v2 precondition snapshot observations. Status remains Case D NOT confirmed: install_fix_confirmed=false, write_path_exercised=false, §7 gate 5 (positive control) NOT met. Current evidence only supports no regression signal observed; full write-path E2E was not exercised due to environmental ceiling (idc2 vcluster host - real K8s 1.27 cluster - has no VolumeSnapshotClass instance, which is outside install-fix scope). D.3 records the dispatch-design-flaw self-correction lesson from Phase 5.7 Phase B v1 (host shadow ns vs vcluster-internal ns confusion) as a parallel to the §5 probe design flaw, kept in appendix only. Main §1-§7 and Appendix D.1 (Phase 5.3 chronicle) are untouched; Type D is not elevated in the main methodology. Both sections are explicitly in-progress case material, not method final recommendations. --- ...ddon-vcluster-api-entry-preflight-guide.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md b/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md index 73bc28a..60a26c1 100644 --- a/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md +++ b/docs/vcluster/addon-vcluster-api-entry-preflight-guide.md @@ -322,6 +322,75 @@ pre-state 是**最后一道防线** — 如果发现 pre-state 与 candidate 假 未达成上述条件之前,本附录所述 Phase 5.3 candidate **不**等同于已修复事实;引用本附录写"install-fix 已验证"是越界。post-state evidence 待后续 phase 的 5-item closeout(plan / mutation_count / impact / rollback / verify)落 artifact 后,再由本附录追加一个 "已升 confirmed" 段落。 +### 附录 D.2:Phase 5.5–5.7 取证编年续(install-fix execution + read/write probes) + +> **Status update**: 写作时点 Case D 整体仍 **NOT confirmed**。Phase 5.5 helm change 已执行(enable 3 个 snapshot sync flags + RBAC,host kubeconfig 范围);Phase 5.6/5.7 read-only probes and Phase B v2 precondition snapshot showed **no regression signal observed**; full write-path E2E **was not exercised**(idc2 vcluster host (real K8s 1.27 cluster) 无 VolumeSnapshotClass instance — environmental ceiling)。本续段记录 in-progress case material,**不**作为 install-fix verified / Case D fixed / release-ready 任一结论的证据。 + +| Phase | 类型 | mutation_count | 关键发现 | +|---|---|---|---| +| 5.5 | install-layer change execute (candidate fix scope) | per-step 单独计入(属 install-layer 修复 risk 动作,TL+owner 拍 + 5-item closeout) | 启用 `sync.fromHost.volumeSnapshotClasses.enabled=true` + `sync.toHost.volumeSnapshots.enabled=true` + `sync.toHost.volumeSnapshotContents.enabled=true` + 对应 RBAC;post-state mirror DP 关联 controller **post-state no longer CrashLoopBackOff**,restartCount 收敛 baseline=1472(因果待 §7 升 confirmed 5 门表门 5 验证,本行只记现象,不下因果结论) | +| 5.6 | read-only probe (post install-fix, sustained convergence sample N=5 fresh-window) | 0 | mirror DP restartCount 5 sample 全 1472;snapshot 3 CRDs LIST rc=0;syncer 15min delta error keyword grep 无 hits | +| 5.7 Phase A | read-only sustained convergence (refresh sample N=5 + business observation) | 0 | restartCount 5 sample 全 1472;3 CRDs rc=0;syncer 15min delta 无 error keyword;backups/restores/volumesnapshots LIST rc=0 | +| 5.7 Phase B v2 | write-op precondition probe / fail-closed matrix | 0 | Stage 1 read-only topology snapshot 显示 `host_has_sc=true / host_has_vsc=false` → 决策矩阵命中 Path C,B.2.0 dry-run gate 未进入;无 PVC/VS create,cleanup_state=not_applicable;mirror DP baseline 仍 1472;syncer 15min delta snapshot/forbidden/RBAC/volume keyword 全 0 | + +**Phase 5.7 Phase B v2 Reader Snapshot 字段(8 字段前置 discipline)** + +``` +install_fix_confirmed=false +write_path_exercised=false +dry_run_pvc_rc=N/A +dry_run_vs_rc=N/A +chosen_path=C +cleanup_state=not_applicable +verdict=fail-closed: precondition not met +verdict_description=no regression signal observed (read paths rc=0; + no forbidden/RBAC keyword in syncer 15min delta); + full write-path E2E not exercised in this run; + environmental ceiling +``` + +**当前证据净结论(严措辞)**: + +- 仅支持:"current evidence only supports **no regression signal observed**" 在 install-fix scope(RBAC + 3 sync flag)范围内 +- **不**支持:confirmed / install-fix verified / Issue#1 fixed / release-ready / Type D 升 confirmed 任一 +- write-path E2E 未 exercise 的原因:**environmental ceiling** — idc2 vcluster host (real K8s 1.27 cluster) 视图无任何 VolumeSnapshotClass instance。VSC instance 安装**不在** install-fix scope,**也不在** Phase 5.5 helm change scope;本次迭代**不**触发该方向工作 + +**§7 升 confirmed 5 条门(截至 Phase 5.7 v2 状态)**: + +| 门 | 状态 | +|---|---| +| 1. 同窗证据闭合 | partial — Phase A sustained + Phase B v2 read paths 同窗 no regression signal;write-path E2E 缺 | +| 2. 可复现(≥2 次独立时间窗) | partial — sustained sample N=5 单窗内连续;跨独立时间窗未达 ≥2 | +| 3. 排除 triplet 全部成立 | partial — **未观察到新增回退信号**;但 write-path E2E **未** exercise,triplet **仍未闭合** | +| 4. owner 侧 verify | not started | +| 5. install-fix positive control (Type D 专属) | **NOT met** — write-path E2E 未 exercise(environmental ceiling,非 install-fix scope) | + +任一门不满足 → 结论停在 candidate,Case D 整体保持 NOT confirmed。 + +**doc backlog(写作时点未 PR)**: +- `idc-vcluster-host-vsc-instance-gap.md` — idc2 vcluster host (real K8s 1.27 cluster) 无 VSC instance 的环境形态记录 +- `env-ceiling-vs-install-fix-scope.md` — 方法论:environmental ceiling 与 install-fix scope 的 dichotomy(本附录 D.2 作为 case material,**不**作为 method final recommendation) +- `fail-closed-precondition-not-met-template.md` — verdict + Reader Snapshot wording 模板,含 dispatch-design-flaw discipline 前置段 + +### 附录 D.3:dispatch-design-flaw discipline(self-correction lesson) + +> 与 §5 probe design flaw 平行,本节是 **dispatch design flaw** 的自纠条款。本节也是 in-progress lesson,不作为 method final recommendation。 + +**lesson 现象(Phase 5.7 Phase B v1)**: + +dispatch 设计中假设 PVC 创建在 vcluster 内的 `sqlserver-test` ns。该假设**错** — `sqlserver-test` 是 **host 侧 vcluster shadow ns**(vcluster pod / mirror DP / mirror manager pod live 在此处),**不是** vcluster 内部 ns;vcluster 内部 k3s 完全独立 ns 集合(默认含 `default` / `kube-system` / `kube-public` / `kube-node-lease` / `kb-system` / 业务 soak ns),通过命名 `-x-...-x-` 与 host 互通。Phase 5.7 Phase B v1 因此 `Error from server (NotFound): namespaces "sqlserver-test" not found`,write-path 未触发实际 mutation。 + +**root cause**:dispatch 设计时**凭记忆假设** ns 在哪一侧,**未**先做 read-only topology snapshot 证伪。 + +**discipline**:TL 在写涉及"vcluster ns / host ns / shadow ns" 任一对的 dispatch 时,**必须**先用 read-only `kubectl get ns` 在两边各跑一次取 capability snapshot 作为前置 Stage 0;**禁止**凭记忆假设 ns/SC/VSC 在哪一侧。本次纠正后的 Phase B v2 已采用 probe-first + fail-closed decision matrix 结构(Stage 1 topology snapshot → Stage 2 decision matrix → B.2.0 dry-run gate)。 + +**与 §5 关系**:§5 lesson 是 broad-grep + API timeout 假阴性的 **probe design flaw**;本节是同性质但发生在 **dispatch design** 阶段。两者共享 evidence-discipline 核心: + +- 凭记忆假设是反 evidence-discipline 的 +- 任何 layer/拓扑/scope 类假设必须 read-only 证实在前,write-op 在后 + +artifact `97-v1-design-flaw-self-correction.txt`(Phase 5.7 Phase B v2 tar 内)保留 v1 错误现场 + 拓扑正解 + 自纠链条。 + --- **Reference docs**: