diff --git a/docs/reference/spec-v02-cicd.md b/docs/reference/spec-v02-cicd.md index a4f7c596..2f920434 100644 --- a/docs/reference/spec-v02-cicd.md +++ b/docs/reference/spec-v02-cicd.md @@ -116,6 +116,99 @@ CI/CD 内部由 UniDesk 手动触发入口、PipelineRun、component planner、B 写 mirror 的一致性模型是 local-first、manual-flush。promotion task 只能持有 mirror/relay 写凭证,不持有 GitHub deploy key;GitHub deploy key 只存在于 `devops-infra` mirror/relay sync/flush 边界。mirror/relay 必须在本地 receive 期间完成 object closure、目标 branch allowlist、non-fast-forward 拒绝和 changed-path 最小校验;receive 成功后本地 ref 即为 Argo 可消费事实。flush 失败不得回滚已经 rollout 的本地 GitOps revision,但必须保留 pending/outbox 状态,下一次手动 flush 可重试并输出 last error,不得静默丢弃。 +## 性能预算与回归判定 + +`v0.2` fast lane 的性能目标是让 code-only/env-reuse 场景主要受 G14 集群调度、PVC 和本地磁盘 I/O 限制,而不是受 GitHub 网络、重复依赖安装或无效 runtime 等待限制。性能预算只用于发现退化和指导排障,不新增发布门禁;发现退化时先按本节做现场热探测,再决定是否需要完整 CI/CD 复跑。 + +代表性 env-reuse 场景的对比基线如下。后续更换 runner、PVC、Tekton controller、Argo repo server 或 mirror 存储后,必须重新测量并更新本表的预算口径。 + +| 场景 | 关键路径总耗时 | 主要阶段表现 | 判定口径 | +| --- | ---: | --- | --- | +| env-reuse + mirror-write 初始路径 | 约 108s | `prepare-source` 约 56s;包含无效 `npm ci` 和多余探针;runtime wait 仍执行 | 只作为旧基线,不应回归。 | +| 移除 `prepare-source` 中的 `npm ci` | 约 76s | `prepare-source` 降到约 21s;其余路径仍有多余探针和 runtime wait | 不应再恢复依赖安装。 | +| 剪裁 fast-path 探针 | 约 50s | `prepare-source` 约 11s;`gitops-promote` 约 7s;`runtime-ready` 约 17s | runtime 有实际变化时的合理预算。 | +| P1 no-op runtime skip | 约 37s | `prepare-source` 约 10s;`plan-artifacts` 约 6s;`collect-artifacts` 约 7s;`gitops-promote` 约 8s;`runtime-ready` 跳过 | source-only 且 runtime identity-only 变化时的目标预算。 | + +当前预算判定:source-only、所有 service 都复用 artifact、GitOps runtime 只发生 source identity 变化时,总耗时应接近 40s;超过 50s 需要先查是否误触发 `runtime-ready`、是否发生 GitHub 直连、是否恢复了 `npm ci` 或无效 preflight。真正需要 rollout 的 code-only 变更允许约 50s,因为 `runtime-ready` 必须等待 Argo 与 workload 收敛。涉及 BuildKit publish、env image rebuild、registry push 或真实 runtime 滚动时,不适用 40s 预算,应按 affected service 的 build 耗时单独测量。 + +关键阶段预算如下: + +| 阶段 | 正常预算 | 退化信号 | +| --- | ---: | --- | +| `prepare-source` | 约 10-12s | 超过 20s、出现 `npm ci`、访问 GitHub canonical remote、catalog fetch 超过数秒。 | +| `source-clone` | 约 1-2s | 超过 5s 通常说明没有命中 `devops-infra` mirror 或 PVC/网络异常。 | +| `catalog-fetch` | 小于 1s | 超过 3s 先查 mirror refs、object closure 和 catalog branch。 | +| `plan-artifacts` | 约 5-7s | `buildServices` 或 `rolloutServices` 非空但本轮只改 CI/CD 文档/脚本时,先查 component input 与 catalog 是否加载。 | +| `collect-artifacts` | 约 6-8s | 未输出 `artifact_reuse` 或 `buildSkippedCount=9` 时,先查 catalog digest 与 service identity。 | +| `gitops-promote` no-op | 约 7-9s | 未输出 `skipped-runtime-unchanged`,或写入了 `v0.2-gitops`,说明 runtime 比对未命中。 | +| `runtime-ready` | no-op 应跳过;真实 rollout 约 15-20s | no-op 场景出现 TaskRun 即为 P1 退化;真实 rollout 超时则按 Argo/workload 排障。 | + +## 性能优化原理 + +`prepare-source` 不安装依赖。该 task 只负责 checkout source、读取上一版 catalog、输出 source identity 和基础文件,不运行 `npm ci`、不探测 npm registry、不做与后续 task 重复的工具链检查。需要依赖的语法检查、render test 或 build task 必须使用各自镜像内已有工具或在专属阶段处理,不能把全仓依赖安装塞回 source 准备阶段。 + +Git 读写都走 `devops-infra` 本地 mirror/relay。读路径用 HTTP mirror checkout `v0.2` source 和 `v0.2-gitops` catalog;写路径把 promotion 推到 mirror/relay 的 `v0.2-gitops`,Argo 也从 mirror/relay 读取。这样 source clone、catalog fetch、GitOps clone/push 都落在集群内网络和本地磁盘,GitHub 只通过手动 `sync`/`flush` 进入或离开本地 mirror,不在 CI 关键路径内。 + +env image 复用把系统依赖和业务代码身份分离。`environmentDigest` 表示可复用运行环境,`HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT` 和 `HWLAB_BOOT_SH` 表示本次代码启动身份。code-only 变更只更新 boot metadata 和 runtime identity,不发布新 env image;只有 `environmentInputHash` 变化时才进入 env rebuild。 + +P1 no-op runtime skip 只跳过无实际 runtime 变化的等待。planner 输出 `buildServices=[]` 且 `rolloutServices=[]` 后,`gitops-promote` 会在写入前对旧 `runtime-v02` 与新 render 结果做归一化比较:忽略 source commit、artifact source commit、boot commit 和等价 commit env/annotation 这类 identity-only 字段。如果归一化结果相同,promotion 输出 `skipped-runtime-unchanged`,把 Tekton result `runtime-ready-required=false` 写出,并跳过 GitOps commit、push、Argo hard refresh 和 `runtime-ready`。如果 workload spec、image digest、env image、SecretRef、Service、Ingress、FRP、ConfigMap 或 rollout service 有实际变化,必须保持 `runtime-ready-required=true` 并等待 runtime 收敛。 + +快速优化不能绕过 fail-closed 语义。mirror miss、commit ancestry 不合法、boot script 缺失、digest 缺失、Argo observer RBAC 不足、workload 未 ready 或公网 health 不一致,都不能为了追求耗时而降级为 warning。允许跳过的只有已经证明 runtime desired state 没有实际变化的等待。 + +## 快速排障 + +先看一次 PipelineRun 总览,确认是否是性能退化、构建退化还是 runtime 收敛失败: + +```bash +bun scripts/cli.ts ssh G14:k3s script -- 'set -eu +ns=hwlab-ci +pr= +kubectl get pipelinerun -n "$ns" "$pr" \ + -o jsonpath="status={.status.conditions[0].status}{\"\\n\"}reason={.status.conditions[0].reason}{\"\\n\"}message={.status.conditions[0].message}{\"\\n\"}start={.status.startTime}{\"\\n\"}done={.status.completionTime}{\"\\n\"}" +echo taskruns +kubectl get taskrun -n "$ns" -l tekton.dev/pipelineRun="$pr" \ + -o jsonpath="{range .items[*]}{.metadata.name}{\"\\t\"}{.metadata.labels.tekton\\.dev/pipelineTask}{\"\\t\"}{.status.conditions[0].status}{\"\\t\"}{.status.conditions[0].reason}{\"\\t\"}{.status.startTime}{\"\\t\"}{.status.completionTime}{\"\\n\"}{end}" | sort -k2,2 +echo skipped +kubectl get pipelinerun -n "$ns" "$pr" \ + -o jsonpath="{range .status.skippedTasks[*]}{.name}{\"\\n\"}{end}" | sort +' +``` + +再抓关键日志。正常 no-op fast lane 应看到 `source-clone` 约 1-2s、`catalog-fetch` 小于 1s、`buildSkippedCount=9`、九个 `artifact_reuse`、`skipped-runtime-unchanged`、`gitops-commit` 和 `gitops-push` 为 `skipped`,并且 `runtime-ready` 出现在 skipped tasks 中。 + +```bash +bun scripts/cli.ts ssh G14:k3s script -- 'set -eu +ns=hwlab-ci +pr= +for task in prepare-source plan-artifacts collect-artifacts gitops-promote runtime-ready; do + tr=$(kubectl get taskrun -n "$ns" \ + -l tekton.dev/pipelineRun="$pr",tekton.dev/pipelineTask="$task" \ + -o jsonpath="{.items[0].metadata.name}" 2>/dev/null || true) + [ -n "$tr" ] || { echo "--- $task skipped/no-taskrun ---"; continue; } + pod=$(kubectl get pod -n "$ns" -l tekton.dev/taskRun="$tr" \ + -o jsonpath="{.items[0].metadata.name}") + echo "--- $task $pod selected logs ---" + kubectl logs -n "$ns" "$pod" --all-containers=true | \ + grep -E "skipped-runtime-unchanged|runtime-ready-required|runtime-identity-only|g14-cicd-timing|git-operation|g14-ci-plan|artifact_reuse|buildSkippedCount|gitops-commit|gitops-push" || true +done +' +``` + +常见故障按以下顺序处理: + +| 现象 | 优先检查 | 处理原则 | +| --- | --- | --- | +| `prepare-source` 回到 20s 以上 | 日志是否出现 `npm ci`、npm registry probe、GitHub URL、SSH setup 或长时间 catalog fetch | 拆掉无效依赖安装和重复探针;读路径必须回到 mirror。 | +| `source-clone` 超过 5s | PipelineRun 参数 `git-read-url`、mirror service、PVC I/O、`git-mirror status` | 不要改回 GitHub 直连;先修 mirror/read service。 | +| catalog 缺失导致全量 build | `prepare-source` 是否从 `v0.2-gitops` 取到 `deploy/artifact-catalog.v02.json` | 先 `git-mirror sync --confirm`;必要时查 mirror refs/object closure。 | +| `buildSkippedCount` 不是 9 | `g14-ci-plan` 的 `affectedServices`、`buildServices`、`rolloutServices` 和 changed path summary | 真实业务变更可以 build;CI/CD 文档或 render-only 变更不应误触发全量 build。 | +| no-op 仍执行 `runtime-ready` | `gitops-promote` 是否输出 `runtime-ready-required=false` 和 `skipped-runtime-unchanged` | 查 runtime 归一化比较;不要直接删除 `runtime-ready`,只修 no-op 判定。 | +| mirror `pendingFlush=true` 长期存在 | `bun scripts/cli.ts hwlab g14 git-mirror status` 和最近一次 flush 错误 | 手动 `git-mirror flush --confirm`;flush 不影响已 rollout 本地 revision,但不能静默积压。 | +| Argo `Synced/Healthy` 但公网不通 | `hwlab-v02-frpc` 日志、master frps allowlist、`19666/19667` | 修 FRP allowlist 或 frpc;不要把 DEV/PROD 入口当作 v02 证据。 | +| `runtime-ready` 超时 | Argo app revision、workload Pod template source commit、Pod events、observer RBAC | 真实 rollout 必须 fail closed;先定位 Argo 或 workload,不要降低等待为 warning。 | + +性能退化排障结束后,只把可复用的预算、原理和入口更新到本文;一次性 PipelineRun 名称、Pod 名称、日志全文和临时证据放到 issue 或 PR,不写入长期规格。 + ## Env 容器复用与三变量启动 `v0.2` code-only fast lane 的 runtime desired state 由三类输入组成:可复用 env image digest、自动推导的 code boot metadata、以及既有 service runtime config。开发者仍按当前 DEV/OPS 流程提交 `v0.2` source commit 和维护 `deploy/deploy.json`;发布入口不得要求人工填写 repo、commitId 或 boot script 路径。 @@ -254,7 +347,8 @@ GitOps branch 已更新、source branch render 通过、PipelineRun 名称存在 | FRP `19666/19667` 入口 | 已实现 | 由 `hwlab-v02-frpc` 与 master frps allowlist 共同提供。 | | SecretRef 独立与 provider 验收 | 已实现/持续约束 | SecretRef 已独立;验收必须做真实短连接聊天。 | | env 容器复用三变量启动 | 已实现/持续约束 | device-pod fast lane 已由 CI/CD 自动推导 `HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT`、`HWLAB_BOOT_SH`;code-only rollout 复用 env image digest,只更新代码身份。 | -| `devops-infra` git mirror/relay 加速 | 部分实现/待迁移写路径 | source/catalog/runtime checkout 读路径已使用独立基础设施集群 mirror/cache;下一步必须把 GitOps promotion、Argo source 和 GitHub flush 收敛到真正写 mirror。runtime namespace 不持有 GitHub deploy key,registry 保持 G14 `hwlab-ci/hwlab-registry`;mirror 同步和 flush 由 UniDesk CLI 手动触发,不设置 CronJob。 | +| `devops-infra` git mirror/relay 加速 | 已实现/持续约束 | source/catalog/runtime checkout 读路径和 GitOps promotion 写路径均使用独立基础设施集群 mirror/relay;Argo source 指向本地 mirror,GitHub flush 由 UniDesk CLI 手动触发,不设置 CronJob。runtime namespace 不持有 GitHub deploy key,registry 保持 G14 `hwlab-ci/hwlab-registry`。 | +| CI/CD fast lane 性能预算 | 已实现/持续约束 | env-reuse no-op 目标约 40s;真实 runtime rollout 目标约 50s;不得恢复 `prepare-source` 依赖安装、GitHub 关键路径写入或 no-op runtime 等待。 | | 自动 registry GC | 未实现 | 初期不启用自动 GC,后续需 lane/profile 保护集。 | ## 平行 lane 运维边界