Files
pikasTech-HWLAB/docs/reference/g14-gitops-cicd.md
T
2026-06-05 01:35:35 +08:00

257 lines
40 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.
# G14 GitOps CI/CD
G14 是 HWLAB 当前 DEV/PROD 原生 k8s 与 GitOps 运行面目标。G14 CI/CD 必须只作用于 G14 k3s,不接管 D601 legacy 运行面,不使用 UniDesk Code Queue 作为调度器,也不把 UniDesk backend、provider-gateway 或 microservice proxy 当作 HWLAB runtime。
## 目标模型
- Source of truth:业务版本以 Git source commit 为唯一身份;镜像 tag、OCI labels、runtime annotation 和 Argo CD desired state 都必须记录同一个 source commit。
- CITekton 在 G14 k3s 内运行 `hwlab-g14-ci-image-publish` Pipeline,由 `scripts/g14-gitops-render.mjs` 直接生成原生 task;最小校验固定为 `repo-reports-guard``g14-contract-check``codex-api-forwarder-check`,随后按 component plan 做 per-service BuildKit publish 与 GitOps promote;没有 `CI.json` runner、DIND 单任务发布或 Docker fallback。
- Artifact:镜像使用 commit tag,例如 `127.0.0.1:5000/hwlab/hwlab-cloud-api:<shortCommit>`digest 由 registry 返回,CI report 只作为审计证据,不作为 CD 真相。发布态 artifact catalog 由 Tekton 写入 `G14-gitops:deploy/artifact-catalog.dev.json`
- Branch split`G14` 是源码监控分支,只保存人写源码、声明和 seed contract`G14-gitops` 是 Tekton promotion 写入的生成分支,保存 `deploy/artifact-catalog.dev.json``deploy/gitops/g14/**` desired state。CI/CD 不再把 catalog promotion commit 写回 `G14`
- v0.2 扩容线:`v0.2` 必须从当前 `G14` fork 出来,并以 `G14:/root/hwlab-v02` 作为固定开发 workspace、`G14:/root/hwlab-v02-cicd.git` 作为固定 CI/CD source repo、`hwlab-v02` 作为固定 runtime namespace。`v0.2` CI/CD 只能作为新增 lane 接入现有 G14 Tekton/Argo 体系,不得改写、删除、暂停或重定向现有 `G14` poller、`G14-gitops` DEV/PROD desired state、`hwlab-dev``hwlab-prod`;详细规格见 [spec-v02-cicd.md](spec-v02-cicd.md)。
- CDArgo CD 只消费 `G14-gitops:deploy/gitops/g14/runtime-dev``deploy/gitops/g14/runtime-prod` 的 Git desired state,不重新构建镜像,不读取 D601 状态,不获取 legacy DEV CD Lease。
- FRPG14 DEV 通过 `hwlab-dev/hwlab-g14-frpc` 暴露 `17666/17667`G14 PROD 通过 `hwlab-prod/hwlab-g14-prod-frpc` 暴露 `18666/18667``v0.2` 规划通过 `hwlab-v02` 内独立 frpc 暴露 `19666/19667`。master frps 的 `deploy/frp/frps.dev.toml` 与实际 `/etc/frp/frps.toml` 必须放行对应端口,但 `v0.2` 放行只能新增 19xxx 入口,不能复用或覆盖 DEV/PROD 入口。
- 并行性:不同 source commit 的 CI build 不共享发布锁;并行安全由 immutable commit tag/digest 和 Git desired state 保证。最终运行版本由 Argo CD 当前同步的 Git revision 决定。
## 门禁最小化与扩容治理
不要滑向不必要的复杂门禁是 HWLAB G14 CI/CD 和 `v0.2` 扩容的通用原则。架构迁移、分支扩容和运行面治理应优先靠固定边界、清晰命名、唯一真相源、标准入口和长期参考文档收敛;不要把每个设计约定、运行策略、观测项或回滚手册都做成新的 preflight、guard、gate 或报告生成器。
旧 DEV/D601/main 门禁如果阻碍当前 G14 或 `v0.2` 路径,默认处理是从当前调用链删除,而不是做兼容性迁移、fallback、legacy mode、双路径绕行或在旧门禁上叠加例外。新增门禁只能覆盖明确高价值风险,且必须最小、低噪声、容易删除;资源配额、RBAC 命名、清理策略、回滚顺序、人工同步策略等默认是设计约定或 runbook,不是 CI/CD 通过条件。
`v0.2` 的硬边界只包括:source branch 必须是 `v0.2`CI/CD source repo 必须是 `/root/hwlab-v02-cicd.git`GitOps branch 必须是 `v0.2-gitops`Git mirror/relay 必须来自 `devops-infra`runtime namespace 必须是 `hwlab-v02`runtime path 必须是 `deploy/gitops/g14/runtime-v02`Argo Application 必须指向 `v0.2` GitOps lane,公网入口只能是 `19666/19667``v0.2` source branch 不跟踪生成物,旧 DEV/D601/main 门禁不进入 `v0.2` 调用链。其他事项先写成决策表或 runbook;只有被证明无法靠上述边界和标准入口自然收敛时,才允许新增最小检查。
## 生成入口
`scripts/g14-gitops-render.mjs` 是 G14 专用转换器:
- 架构迁移时,过时的自检、预检、guard、gate 优先删除,不在旧门禁上叠加例外或复杂度;新门禁只允许覆盖明确高价值风险,必须保持最小、低噪声、易迁移。
- 直接声明 Tekton Pipeline、最小原语校验 task 和 PipelineRun 样板;不再读取 `CI.json` 或生成 `ci-json` step。
- 读取 `deploy/deploy.json``deploy/k8s/*`,生成 Argo CD 可消费的 G14 runtime Kustomize path。
- G14 Tekton 的镜像构建发布入口必须是 `scripts/g14-artifact-publish.mjs`;它只是集群内 Task 的 build/push helper,不做 rollout、不写 D601、不获取 legacy DEV CD Lease。旧 `scripts/dev-artifact-publish.mjs` 入口已删除;`dev-cd-apply``ci-publish` 和旧 `main` JS CD 入口禁止出现在 G14 Pipeline 生成脚本和 G14 验收证据中。
- `g14-contract-check` 在 fresh source clone 内取当前 `HEAD`,把 GitOps 产物渲染到 `mktemp -d` 临时目录,并立刻用同一个 `--source-revision` 执行 `--check`;它校验 render 代码、原生 Tekton 产物生成合同和 forbidden-fragment 护栏,不依赖 source branch 预先存在 `deploy/gitops/g14/source.json`。面向已发布 runtime 的 `source.json` 只存在于 `G14-gitops` 生成分支,作为 promotion evidence。
- 生成 `hwlab-g14-branch-poller` CronJob:它使用 G14 集群内的 Git SSH Secret 轮询 `G14` 分支,按 source commit 创建确定命名的 Tekton PipelineRun。
- 生成 `hwlab-g14-control-plane-reconciler` CronJob:它使用同一个 Git SSH Secret 轮询 `G14`,运行 repo 内 `scripts/g14-gitops-render.mjs`,并 server-side apply 生成的 Tekton RBAC、Pipeline、Poller 和 Reconciler manifests;因此 CI 控制面变化应自动进入 G14 k3s,不需要人工长期执行 render/apply。
- 默认输出到 `deploy/gitops/g14/`
- 默认 GitOps 生成分支是 `G14-gitops`Pipeline 成功后把本次 source commit 对应的 `deploy/artifact-catalog.dev.json``deploy/gitops/g14/**` 推送到该分支,避免把生成提交继续写回 `G14`
- `v0.2` GitOps lane 必须使用独立生成身份,例如 `v0.2-gitops``deploy/artifact-catalog.v02.json``deploy/gitops/g14/runtime-v02`,并由独立 Argo CD Application 指向 `hwlab-v02`。若后续实现选择复用某个脚本入口,也必须通过显式参数区分 source branch、catalog、runtime path、Application 和 namespace,不能靠修改默认值让 `G14` DEV/PROD 行为漂移。
- 默认 registry prefix 是 `127.0.0.1:5000/hwlab`,用于 G14 单节点 k3s 的 node-local registry。
- 默认 CI/CD proxy 是 G14 本机 `http://127.0.0.1:10808` / `socks5h://127.0.0.1:10808`。Tekton CI step、BuildKit sidecar 和 publish step 都注入 proxy/no_proxy;服务镜像构建只允许通过 Pod 内 BuildKit Unix socket 直接 push 到 G14 本地 registry,不能回退到 Docker daemon、DIND 或 host Docker。
- `prepare-source` 和最小原语校验 task 不允许每次运行时重新 `apk add``apt-get install` 或临时下载 browser/runtime 依赖。当前固定工具镜像是 `127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1`,包含 Node 22、npm、Bun、Git、OpenSSH、curl、Python3 和 Docker CLI。镜像内容由 `deploy/ci/hwlab-ci-node-tools.Dockerfile` 声明;脚本启动时必须输出工具与 proxy preflight 结构化日志。缺少工具时应先在 G14 构建并推送新的工具镜像,再修改 `HWLAB_G14_CI_TOOLS_IMAGE`/render 默认值,不能回退到 runtime 安装。
- G14 host 只用于 source workspace、GitOps render、k3s 控制和轻量语法/静态合同检查;不要把 host 当成浏览器执行面。低频 browser smoke 已不再属于默认 primitive CI。若确实需要一次性布局、移动端或交互验证,必须显式在 G14 k3s/Tekton 的专用 Playwright 镜像内运行,不能回退成 host 上强装 browser 的长期方案。
- Poller、control-plane reconciler、image publish 和 GitOps promote step 都不允许每次运行时 `apk add` / `apt-get install`。当前 G14 registry 固定工具镜像是 `127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1`,包含 Node 22、npm、Bun、Git、OpenSSH、curl、Python3 和 Docker CLI;生成的脚本只做 proxy preflight 与工具存在性检查。若需要升级工具,先在 G14 构建/推送新的工具镜像,再修改 `HWLAB_G14_CI_TOOLS_IMAGE`/render 默认值并由 reconciler apply。
- 服务镜像构建的默认 parent/base image 不得从 Docker Hub 反复拉取。当前 `node:20-bookworm-slim` 已镜像到 G14 registry 的 allowlist 名称:`127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim`render 默认 `base-image` 和 poller `BASE_IMAGE` 都指向这个本地镜像。需要升级 parent image 时,先通过 G14 proxy 拉取并推送到 G14 registry,再修改 `HWLAB_G14_DEV_BASE_IMAGE`/render 默认值;image publish step 只允许从本地 registry pull base image,且 tag 必须符合 publish gate 的 `hwlab-node20-base`/`hwlab-dev-base`/`hwlab-node-runtime-base` allowlist。
- 任何依赖下载阶段都必须有可观测诊断。生成的 Tekton 脚本在 npm、BuildKit base image/local registry probe 和 GitOps promote 之前输出结构化 `dependency-proxy-probe``dependency-curl-probe``dependency-download-*` 日志,至少包含 phase、目标 URL/镜像、脱敏 proxy、首包耗时、总耗时、下载字节数和速度。CI/CD 卡在下载时,先用这些日志判断是 proxy 不可达、目标源慢、DNS/首包慢还是下载吞吐低,再决定是否切换 G14 代理节点或预热镜像;不能只凭 PipelineRun Running 时长判断业务测试失败。
常用命令:
```sh
npm run g14:gitops:render -- --source-revision <sourceCommit>
```
人工 source workspace 不再对 `deploy/gitops/g14/**` 做生成物对比;这些文件在 source 分支下是忽略的生成物,旧文件和 `source.json` 不是真相。需要看渲染计划时使用 `npm run g14:gitops:render -- --no-write`;正式发布态只看 `G14-gitops`、Argo Application 和 live runtime。
## 原生 k8s Tekton + Argo 配置面 vs 已废弃的 CI.json runner
当前 G14 已完全删除 `CI.json``ci-json` step。下面的对比只保留为迁移理由:Tekton + Argo 是唯一发布控制面;repo-local 校验已经收敛为 render 内建的少量原语 task,而不是再保留一条 shell runner 路线。
| 维度 | 原生 k8s Tekton + Argo 配置面 | 已废弃的 `CI.json` runner 路线 |
| --- | --- | --- |
| 权威职责 | 构建、发布、GitOps promotion、Argo sync、runtime rollout | repo-local 静态合同、schema、focused smoke、预检命令 |
| 真相来源 | PipelineRun、TaskRun result、`G14-gitops` revision、Argo Application、live workload | 仓库内 `CI.json` 命令、runner 镜像、单个 `ci-json` TaskRun 日志 |
| 优点 | 控制面声明式、k8s 可观测、并行 fan-out、TaskRun result 可复用、CD 与 runtime 验收边界清楚 | 改 repo 内检查成本低、命令贴近源码、适合 source-only 合同和 focused preflight、无需每加一条检查就手写一套新 Tekton Task |
| 缺点 | render/generated/cluster 模板三层更复杂,控制面改动要等 reconciler apply 后才能验证,source 与 generated 漂移会制造噪声 | shell 包装、命令链和日志语义更脆弱;多个检查挤在一个 step 内时失败隔离差,天然不提供 GitOps desired state、Argo rollout 或 live runtime 证据 |
| 适用场景 | 需要发布真相、并发构建、artifact identity、GitOps promotion、CD rollout、live runtime 判定 | 需要 repo-local 源码检查、静态合同、快速 smoke、focused preflight,但不需要声明 runtime 期望状态 |
| 不该承担的职责 | 不应回退成 D601 legacy JS CD 或 target-side build | 不应被提升为独立 CD 路线,也不应用来替代 Argo sync、live workload ready 或公网 health |
选择规则:
- 只要问题涉及 release truth、rollout、runtime desired state、namespace workload 健康、public health 或 artifact provenance,就必须回到 Tekton + Argo 配置面。
- 只要问题仍停留在 repo-local 静态合同或 focused preflight,就把它实现成独立的原语 Tekton task;不值得长期保留的检查直接删除,而不是重新引入 `CI.json` runner。
- 不要再创建第二套 shell 命令发布面;如果一项检查开始依赖 rollout 顺序、TaskRun results、GitOps branch、Argo revision 或 live runtime,它就必须属于 Tekton/Argo 标准路径。
## Monorepo 组件计划与兼容 render
HWLAB 是 monorepoG14 CI/CD 加速必须按组件输入判断构建和滚动,并直接依赖内建 component model、`deploy/deploy.json` 与 per-service artifact catalog`CI.json` 已删除,不再作为任何 planner 输入。
- `scripts/g14-ci-plan.mjs` 是只读 planner,默认读取当前 workspace 的 `deploy/deploy.json``deploy/artifact-catalog.dev.json``scripts/src/g14-ci-plan-lib.mjs` 内建 component model,输出 `affectedServices``reusedServices``componentCommitId``componentInputHash``dockerfileHash``baseImageDigest``buildArgsHash` 和原因;它不得修改 deploy、catalog 或 GitOps 文件。Tekton `prepare-source` 会先从 `G14-gitops` 注入上一轮发布态 catalogsource 分支里的 catalog 只作为 seed contract。
- 服务清单兼容顺序固定为:显式 `--services``deploy.services[]``deploy.k3s.serviceMappings[]``internal/protocol.SERVICE_IDS`。因此旧 `deploy/deploy.json` 形态和当前完整 `deploy.services[]` 形态都必须能被 planner 识别。
- 组件边界固定由 `scripts/src/g14-ci-plan-lib.mjs` 的内建 service-path model 定义;如需新增或调整 `componentPaths``sharedPaths``runtimeDeps``buildSystemPaths`,直接修改 planner 库和对应测试,不再额外维护 repo-local commands/forbidden skeleton。
- `hwpod`/`device-pod-cli` 是 runner 和 runtime 镜像内的稳定工具入口,不是纯本地 `hwlab-cli` 源码工具;`tools/device-pod-cli.mjs``tools/device-pod-cli.ts``tools/src/device-pod-cli-lib.ts``skills/device-pod-cli/` 必须作为 shared runtime input 进入 component model。修改这些路径时,G14/v0.2 CI 必须至少触发携带 `/usr/local/bin/hwpod` 的 runtime 服务重新构建或 env-reuse rollout,不能全量复用旧 artifact 后只报告 PipelineRun 成功。
- `scripts/g14-artifact-publish.mjs` 默认启用组件级 lazy build:先运行 planner,再只构建/推送 `affectedServices``reusedServices``deploy/artifact-catalog.dev.json` 或 lane catalog 复用已有 sha256 digest。非 env-reuse 服务复用前必须满足 artifact provenance 自证:catalog 有可验证 digest、catalog 的 `sourceCommitId` 在当前 repo 可解析、用该 `sourceCommitId` 的 source tree 重新计算出的 `componentInputHash` 等于 catalog 记录、该 hash 再等于本轮 planner 计算值,且 `dockerfileHash`/`buildArgsHash` 没有不一致。catalog 缺 digest、缺 provenance、source tree 无法解析、catalog hash 与 catalog source tree 不一致、或 catalog hash 与本轮 input 不一致时,planner 必须把该服务列为 affected 并重新发布,不能用旧 guard 阻塞,也不能退回 Docker 或 legacy full-build 路线。
- Artifact catalog 的 per-service provenance 是镜像 digest 的身份证明,不是本轮 planner 状态缓存。reuse 路径只能保留旧 artifact 自身的 `sourceCommitId``componentCommitId``componentInputHash``dockerfileHash``baseImage*``buildArgsHash`;禁止把当前 planner 的 component/build 元数据写入复用的旧 digest,否则下一轮 planner 会把旧镜像误判成已包含新输入,造成 CI/CD false-green。
- `scripts/g14-artifact-publish.mjs` 的 publish report 必须携带 planner 的 per-service 元数据;`scripts/refresh-artifact-catalog.mjs` 默认只预览,只有 G14 Tekton promotion 显式传 `--write` 时,才把生成的 `commitId``image``imageTag``digest``publishState` 和 component provenance 字段写进当前 workspace 的 `deploy/artifact-catalog.dev.json`,随后只提交到 `G14-gitops``deploy/deploy.json` 是人写的 runtime config 真相源,不得被 promotion、refresh 脚本或人工发布流程回写镜像身份字段。
- `scripts/g14-gitops-render.mjs` 只支持混合 desired stateworkload 的 container image、`HWLAB_IMAGE``HWLAB_IMAGE_TAG` 和 pod template `source-commit` 来自 `deploy/artifact-catalog.dev.json` 的 per-service artifact identity;普通 env、replica、healthPath、profile 等配置来自 `deploy/deploy.json`。全局 GitOps metadata 仍记录本次 source commit。`--legacy-source-images``HWLAB_G14_USE_DEPLOY_IMAGES=0` 已废弃,不得把所有 workload image 回退渲染为同一个 source commit tag。
- G14 Tekton promotion 在推送 `G14-gitops` 前,必须用 publish report 显式 `--write` 刷新 workspace 内的 `deploy/artifact-catalog.dev.json`,然后把刷新后的 catalog 和 rendered GitOps desired state 一起提交到 `G14-gitops`。promotion 不得自动修改或推送 `G14` source branch,也不得自动修改 `deploy/deploy.json``deploy/k8s/base/workloads.yaml`,这样人写配置不会和 CI 生成身份反复冲突。
- Tekton 并发化只能以 planner 输出作为输入;每个 service 都有独立 TaskRunchanged service 启动 BuildKitunchanged service 只写 reuse result 并复用 catalog digest,且不能改 pod template,避免无意义 rollout。没有完整 per-service desired state 证据时,必须修复 planner/catalog 证据,不能使用 `--full-build``--legacy-source-images`、DIND 或 Docker fallback 回退。
## 加速判定与当前瓶颈
G14 CI/CD 加速的第一判定标准不是 PipelineRun 总耗时单点变短,而是 monorepo 组件粒度的“少构建、少滚动、可并发”是否成立。
- 触发延迟:`hwlab-g14-branch-poller` 固定每 1 分钟轮询 `G14`。相对 5 分钟轮询,平均触发等待应从约 2.5 分钟降到约 0.5 分钟,最坏等待从 5 分钟降到 1 分钟。发现 `kubectl -n hwlab-ci get cronjob hwlab-g14-branch-poller -o jsonpath='{.spec.schedule}'` 不是 `* * * * *` 时,应先修 poller/reconciler,而不是手工长期创建 PipelineRun。
- 组件懒构建:docs-only、GitOps-only、poller/reconciler manifest-only 等不影响服务输入的变更,planner 应输出 `affectedServices=[]``buildSkippedCount=<全部服务数>`,各 service TaskRun 应写出 `status=reused``build-backend=reused-catalog`。这类变更不应重新构建任何服务镜像,也不应把 workload image 改成当前 source commit tag。
- 并发 fan-outper-service TaskRun 应由 Tekton/k8s scheduler 同时调度,多个 service 的 build/reuse 窗口应接近“最慢单个服务任务耗时”,而不是所有服务串行相加。reuse-only 场景的 fan-out 窗口当前基线约为十几秒;真实组件构建场景仍需按 changed service 单独记录 BuildKit 耗时和 cache hit 情况。
- CD rolloutunchanged service 必须复用 `deploy/artifact-catalog.dev.json` 的 image/digestpod template 不应因全局 source commit 改变而无意义滚动。手写 manifest(例如 `deepseek-proxy`、device-agent 类辅助 workload)只要复用某个已发布服务镜像,也必须走同一个 catalog image 选择逻辑,不能直接用当前 source commit tag。
- 运行态验证:GitOps promote 成功只说明 `G14-gitops` 分支更新;最终通过必须看 Argo Application revision、sync 状态、目标 namespace Deployment/StatefulSet ready、公网 health。Argo 还停在旧 revision 时,优先做 `argocd.argoproj.io/refresh=hard` 刷新;不要把已经推送的 `G14-gitops` 分支误判成运行面已滚动。
- Argo health 判定:v0.2 不再生成 HWLAB 自有 agent worker Job templateAgentRun v0.1 作为外部共享执行基础设施接入。`hwlab-cli` 不创建镜像、Service 或 Job template,只在固定 repo 内短连接执行。真正发布验收仍以长驻 workload ready、公网 health 和失败 Pod 清单为准。
当前仍然慢的主要位置:
- `prepare-source` 与最小原语校验 task 仍是前段主要开销。它们包含 Git clone、checkout、必要的 `npm ci``repo-reports-guard``g14-contract-check``codex-api-forwarder-check`。继续加速应优先减少 workspace cold start 与依赖安装成本,而不是重新塞回一条 shell runner。
- `gitops-promote` 仍是大头,当前量级约 100 秒。它包含 source branch freshness check、artifact catalog refresh、render、clone `G14-gitops`、复制 catalog 与 generated desired state、commit 和 push。继续加速应优先减少 git clone/push 成本,例如使用浅 clone、持久 workspace、server-side patch 或把 GitOps repo 操作单独缓存。
- per-service TaskRun 数量增加后会有 k8s 调度和 sidecar 生命周期开销。reuse-only 任务很快,但如果每个 unchanged service 都启动完整 buildkit sidecar,调度开销会抵消部分收益;长期目标是让 unchanged service 走更轻量的 reuse Taskchanged service 才启动 BuildKit。
- per-service BuildKit 必须使用 Pod 内 Unix socket 或其他 Pod-scoped 端点,禁止在 hostNetwork 下绑定固定 TCP 端口。多个 service TaskRun 并发时,如果 sidecar 统一监听 `0.0.0.0:1234`,会抢同一个宿主端口,导致主 step 结果成功但 Pod phase 显示 Failed,严重污染 CI 观测。
- Argo sync/health 不是 instant。`Sync=Synced` 后 health 仍可能因为 replicas=0 的模板 Deployment、未排除 health 的模板 Job 或 StatefulSet 旧 revision 显示 `Suspended`/`Progressing`。这时应先看实际长驻 workload ready 与具体 unhealthy resource,不要只看 Application 总 health;如果 suspended Job 只是模板资源,应修 render 注解而不是把 DEV rollout 判成失败。
- StatefulSet 旧 ordinal pod 可能长期卡在历史缺失镜像上,即使 StatefulSet 当前 template 已经修正。DEV 可按运行面热修流程删除旧 Pending pod 让 StatefulSet 按当前 template 重建;PROD namespace 需要明确 maintenance 授权后再做同类删除。
每次优化 CI/CD 都必须保留可比测量:记录 PipelineRun 总耗时、`prepare-source``repo-reports-guard``g14-contract-check``codex-api-forwarder-check``plan-artifacts`、per-service fan-out 起止窗口、`collect-artifacts``gitops-promote`、Argo sync 到 workload ready 的时间,并标明本轮是 reuse-only、单组件 build 还是多组件 build。没有这些分段数据,不要只用“感觉变快/变慢”判断优化成败。
## Code Agent Provider Profiles
G14 Code Agent 通过同一个 repo-owned Codex app-server stdio runner 承载多个 OpenAI-compatible Responses profile。Cloud Web 每次请求都发送 `providerProfile`,后端按 profile 生成本次请求的 env overlay;不得把某一个模型通道硬写死到全局运行态而删除另一个通道。
| Profile | 默认 | Model | Base URL | 说明 |
| --- | --- | --- | --- | --- |
| `deepseek` | 是 | `HWLAB_CODE_AGENT_DEEPSEEK_MODEL`,默认 `deepseek-chat` | `HWLAB_CODE_AGENT_DEEPSEEK_BASE_URL`,默认 `http://hwlab-deepseek-proxy.<namespace>.svc.cluster.local:4000/v1/responses` | G14 集群内 DeepSeek Responses bridgeService 4000 先进入 `hwlab-deepseek-responses-bridge`,再转发到同 Pod 内 Moon Bridge 4001。 |
| `codex-api` | 否 | `HWLAB_CODE_AGENT_CODEX_API_MODEL`,默认 `gpt-5.5` | `HWLAB_CODE_AGENT_CODEX_API_BASE_URL`G14 默认应指向同 Pod `127.0.0.1` loopback forwarderforwarder upstream 为 hyueapi | 独立 Codex/OpenAI-compatible Responses API 通道;不得依赖 DeepSeek bridge。 |
| `minimax-m3` | 否 | `HWLAB_CODE_AGENT_MINIMAX_M3_MODEL`,默认 `MiniMax-M3` | 不由 HWLAB 配置;由 AgentRun `backendProfile=minimax-m3` 的 Secret/profile 承接 | HWLAB 只把该 profile 委托给 AgentRun v0.1,不在 HWLAB 新增 MiniMax API key、base URL、proxy 或 provider 实现。 |
`hwlab-cloud-api` 仍以 `HWLAB_CODE_AGENT_PROVIDER=codex-stdio` 运行。`HWLAB_CODE_AGENT_MODEL``HWLAB_CODE_AGENT_OPENAI_BASE_URL` 可作为 runtime-default 兜底,但前端默认 profile 是 `deepseek`,用户可以切到 `codex-api``minimax-m3`。Codex app-server 启动参数必须同时设置 provider base URL、provider `name``model``review_model`,否则 Codex/DeepSeek 链路可能在模型目录或 `/v1/responses` 阶段退化成 `model=None``minimax-m3` 是 AgentRun 委托 profileHWLAB 只负责把用户选择透传为 AgentRun `backendProfile=minimax-m3`,不得为了该 profile 在 HWLAB 里重新读取 MiniMax Secret 或维护第二套 provider bridge。HWLAB 对外的 conversation/session/thread 合同不随 AgentRun backend 切换而改变;adapter 内部传给 AgentRun 的 `sessionRef.sessionId` 必须按 `backendProfile` 分域,`accessController` 持久化记录仍以 HWLAB 原始 session 归属为准,嵌套 `agentRun.sessionId` 保存 AgentRun scoped sessionRef。G14 cloud-api 的 `NO_PROXY/no_proxy` 必须包含 `hyueapi.com``.hyueapi.com``codex-api` 的 hyueapi upstream 由同 Pod loopback forwarder 直连,不能被 proxy 注入污染。
G14 `codex-api` 不得把 `http://172.26.26.227:17680/v1/responses` 作为默认 base URL;该地址只保留为 D601 legacy Code Queue runner 或历史 egress 对照线索。G14 上的 `codex-api` 应先进入同 Pod `127.0.0.1` loopback forwarder,再由 forwarder 直连 `hyueapi.com` / `.hyueapi.com`;这两个域名必须同时进入 `NO_PROXY``no_proxy`。DeepSeek profile 可以通过集群内 bridge/Moon Bridge 转换,`codex-api` 不能用 DeepSeek bridge 伪装通过,也不能因为默认 profile 切到 DeepSeek 而删除或覆盖 `codex-api` 的独立模型、base URL、auth 和最小闭环验证。
Provider/profile 变更必须按 [Code Agent Chat Readiness Runbook](code-agent-chat-readiness.md) 的分层方法先做目标 pod 最小闭环:env/profile overlay、SecretRef/auth 结构、hyueapi direct NO_PROXY、裸 Responses API、D601 Code Queue runner 对照、loopback forwarder 对照、`codex exec --json`、最终 app-server stdio `completed` + 非空 reply。裸 HTTP/SSE 请求通过只能证明 upstream、认证和模型可用;没有 Codex CLI/app-server 通过前,不得进入正式 GitOps/CI/CD 发布,也不得把 Workbench 状态标成 DEV-LIVE reply pass。
Codex app-server 当前要求 provider `wire_api="responses"`,不得把 DeepSeek profile 切到旧 `chat` wire API。DeepSeek profile 的真实 Responses 转换层固定使用 Moon Bridge;不要在 HWLAB 里手写完整 Responses-to-Chat/Anthropic 转换器,因为这会破坏 Moon Bridge 对 prompt cache、tool-result 顺序和模型目录的成熟处理。Codex 发往 `/v1/responses` 的请求体可能带 `Content-Encoding: zstd`,而 Moon Bridge 不负责解压 Codex zstd body。因此 GitOps 中的 `hwlab-deepseek-proxy` Pod 必须包含 repo-owned `hwlab-deepseek-responses-bridge` sidecarService 端口 4000 指向 bridgebridge 只做 zstd request body 解压、删除 `Content-Encoding`/重写 `Content-Length`、丢弃非 `function` tool 类型和 Codex-style `GET /v1/models` 目录适配,再把请求转发给 4001 的 Moon Bridge。DeepSeek API 只接受 `function` toolsbridge 必须在转发前丢弃 Codex Responses 请求里的非 `function` tool 类型(例如 `web_search``image_generation`),但不得移除 shell/apply-patch 等 function tools。模型、cache、tool 调用顺序、密钥和真实推理仍由 Moon Bridge 管理;bridge 不新增业务 gate,也不得把兼容失败伪装成 SOURCE/legacy blocker。
DeepSeek proxy manifest 是 GitOps desired state 的一部分,DEV/PROD 分别生成在 `runtime-dev/deepseek-proxy.yaml``runtime-prod/deepseek-proxy.yaml`。Moon Bridge 镜像由 `deploy/moonbridge/Dockerfile` 从上游 `ZhiYi-R/moon-bridge` 固定 commit 构建,默认镜像为 G14 本地 registry 的 `moonbridge:<上游短 commit>`GitHub、Google 或 Docker base image 下载必须优先使用 G14 节点本地 proxy。DeepSeek key 仍通过 `hwlab-code-agent-provider/openai-api-key` Secret 注入到 init container 并写入 Pod 内 `emptyDir` 配置文件;ConfigMap、文档、trace、health、issue 和日志不得打印 Secret 值。
## Polling 触发
G14 不要求 GitHub webhook 或 GitHub Actions 配置。`hwlab-ci/hwlab-g14-branch-poller` CronJob 每 1 分钟通过 Git SSH 拉取 `G14` HEAD,并用 source commit 的前 12 位生成 PipelineRun 名称 `hwlab-g14-ci-poll-<short12>`
`v0.2` 接入不再新增 HWLAB 发布触发 CronJob;标准入口是 UniDesk CLI `bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02 --confirm`,由该入口自动 fetch `/root/hwlab-v02-cicd.git`、解析当前 `origin/v0.2` HEAD,并直接创建可区分的 commit-pinned PipelineRun,例如 `hwlab-v02-ci-poll-<short12>`。该 lane 的 GitOps promotion 只允许写入 `v0.2` 专属 catalog、runtime path 和 GitOps branch;不得向 `G14` 或现有 DEV/PROD runtime path 写入生成物,也不得从 `/root/hwlab-v02` 工作树状态选择待发布 commit。
G14 lane 中如果同名 PipelineRun 已存在,poller 直接跳过;如果不存在,则创建新的 PipelineRun。这样可以用 Kubernetes 原生 CronJob、ServiceAccount、RBAC 和 Tekton API 实现无 GitHub webhook 的分支监控,同时避免同一个 commit 被反复派单。历史上的 `chore: promote G14 GitOps source ...` source 分支生成提交已经废弃;G14 poller 只应该看到人写 source commit。
Pipeline 的标准路径是:`G14` source commit -> Tekton 原语校验 task -> commit-tagged image push 到 G14 registry -> refresh artifact catalog -> render `deploy/gitops/g14/**` -> push catalog 与 GitOps desired state 到 `G14-gitops` -> Argo CD 同步 runtime。CD 只消费已经构建好的镜像和 Git desired state,不在 Argo CD 内构建镜像。
GitOps promotion 成功只证明 `G14-gitops` 分支已经写入新 desired state;如果 render 改变了 Argo Application、AppProject、runtime path 或 DEV/PROD 拆分目录,必须同步检查并应用 `deploy/gitops/g14/argocd/project.yaml``application-dev.yaml``application-prod.yaml``hwlab-g14-dev` 必须指向 `deploy/gitops/g14/runtime-dev``hwlab-g14-prod` 必须指向 `deploy/gitops/g14/runtime-prod`;如果集群里 Application 仍指向旧 `deploy/gitops/g14/runtime`Argo 会停在旧 revision,即使 CI/publish/promote 全部成功也不会滚动新镜像。
观察 PipelineRun、Argo 和 rollout 时使用短连接轮询:一次 `kubectl get` 或有限 `logs --tail` 后返回,由指挥侧间隔重试。不要用长时间 `kubectl wait --timeout=900s` 占住 `tran G14:k3s` 透传锁;如果误用长 wait,只能杀掉本地等待客户端并清理 stale tran lock,不能把这个等待过程当成 CD 失败。
下载阶段排障命令:
```sh
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n hwlab-ci logs pod/<pipelinerun-task-pod> --all-containers --tail=400 \
| grep -E 'dependency-(proxy|curl|download)'
```
如果 probe 显示 `proxy-env-missing``proxy-connect-failed``timeout` 或速度长期接近 0,应先按 `/root/docs/vpn-proxy-ops.md` 检查 v2rayN/Hysteria,再重跑 polling。若 probe 正常但后续校验失败,按对应 Tekton task 日志和业务检查排障。
手动 render/apply 只允许作为 bootstrap 或 reconciler 故障抢修;正常路径必须由 `hwlab-g14-control-plane-reconciler` 自动完成 CI 控制面 manifest 更新。
修改 `scripts/g14-gitops-render.mjs`、Tekton Pipeline、RBAC、poller 或 reconciler 这类 CI 控制面后,不能立刻用旧集群 PipelineRun 作为新模板验证。必须先确认 `hwlab-g14-control-plane-reconciler` 已完成并且集群内 Pipeline manifest 包含新字段,再触发手动 poller 或等待下一轮 source commit;否则 PipelineRun 会在旧 Pipeline 下创建,容易把已经修复的 sidecar、proxy 或 TaskRun 行为误判为仍然失败。
## 集群资源
- `hwlab-ci`registry、Tekton runner RBAC、Polling CronJob、Pipeline/PipelineRun 所在 namespace。
- `hwlab-ci/hwlab-g14-control-plane-reconciler`:自动 render/apply G14 CI 控制面 manifest 的 CronJob。
- `argocd`Argo CD 控制面和 `hwlab-g14-dev` / `hwlab-g14-prod` Application 所在 namespace。
- `hwlab-dev`HWLAB DEV runtime namespace,由 Argo CD 应用 `deploy/gitops/g14/runtime-dev`
- `hwlab-prod`HWLAB PROD runtime namespace,由 Argo CD 应用 `deploy/gitops/g14/runtime-prod`;通过 `hwlab-g14-prod-frpc` 映射到 `18666/18667`,正式验收必须同时检查 Argo sync、Deployment ready、FRP public health 与 source commit。
- `hwlab-v02`HWLAB `v0.2` runtime namespace,只能由 `v0.2` 专属 Argo CD Application 和 GitOps runtime path 管理;规划通过独立 frpc 映射到 `19666/19667`。创建 `hwlab-v02` 不得删除、重命名或改写 `hwlab-dev`/`hwlab-prod`
G14 registry 由 Kubernetes Deployment `hwlab-ci/hwlab-registry` 承载,使用 host network 暴露 `127.0.0.1:5000` 给 k3s/containerd 和 host-network Tekton build pod。旧 host Docker registry 不能与该 Deployment 同时占用 5000 端口。
G14 k3s/containerd 的 Pod 镜像拉取也必须长期使用 G14 本机代理。主机配置记录在 `/root/docs/kubernetes-ops.md`,当前 systemd env 文件是 `/etc/systemd/system/k3s.service.env`。修改代理后需要 `systemctl daemon-reload && systemctl restart k3s`,并确认 `k3s-server` 进程环境中存在 `HTTP_PROXY` / `NO_PROXY`
## 凭证边界
HWLAB repo 是私有仓库时,Tekton 和 Argo CD 需要各自的 Git SSH Secret。Secret 只能从 G14 本机已有 SSH key 创建,不能写入 Git,不能打印 key 内容。
Tekton 约定 Secret 名称:`hwlab-ci/hwlab-git-ssh`
Argo CD 约定 repository Secret`argocd/hwlab-git-ssh`,并带 label `argocd.argoproj.io/secret-type=repository`
## D601 边界
G14 GitOps manifests 不包含 D601 kubeconfig、D601 node guard、D601 FRP 公网入口或 UniDesk Code Queue 调度入口。D601 仍由既有生产路径维护;G14 GitOps 的安装、PipelineRun、Argo sync 和 registry 操作都不能对 D601 执行 kubectl、docker 或流量切换动作。
## 真相源与常见误判
G14 PR、CI、CD 的判断应按以下顺序收敛真相,越靠前越接近最终运行面:
1. live runtime:目标 namespace 的 Deployment/StatefulSet template、Pod ready、`describe`、容器日志和公网 health。
2. Argo desired state`hwlab-g14-dev` / `hwlab-g14-prod` 的 Application revision、sync、health 和实际 runtime path`v0.2` 接入后还要看 `hwlab-g14-v02` 或等价专属 Application 是否指向 `v0.2` GitOps lane 和 `hwlab-v02`
3. Tekton 执行证据:branch-poller 日志、PipelineRun、TaskRun results、`gitops-promote` 终态。
4. 干净 source workspace`origin/G14` 当前内容,以及 `npm run g14:gitops:render -- --no-write` 和 planner 输出。
5. 对照线索:旧 commit 记忆、坏 worktree、D601 legacy 路径、脚本旧默认值,只能当线索,不能当真相。
常见误判与纠正:
- 不要把 `/root/hwlab` 当前 checkout、任意 `/tmp` worktree 或带 conflict marker 的目录当 source truth;只有跟踪 `origin/G14` 的干净 worktree 才能作为当前发布依据。
- 不要按“某个预期 commit 是否出现在 ancestry 中”判断功能是否已经合入;当前 `origin/G14` 实际内容比历史 commit 轨迹更重要。
- 改了 source 但没有触发 Tekton promotion 时,`G14-gitops` 仍停在旧 generated state 是正常现象,不等于 Tekton 或 Argo 本身坏掉。
- 不要把 `build-*` TaskRun 名称直接当作镜像重建证据;reuse-only 变更也会扇出同名 TaskRun,必须看 Tekton result 的 `status=reused``build-backend=reused-catalog`
- 不要把 `G14-gitops` 分支已更新误判成 DEV 或 PROD 已滚动;只有 Argo Application revision、目标 workload ready 和 live manifest 生效,才算 CD 真实通过。
- 不要把 health payload 中的镜像 commit 当成 runtime manifest commitruntime-only 修复可能复用旧镜像,只改变 probe、env、annotation 或 sidecar 行为。
- sidecar 监听 `127.0.0.1` 时,Pod-IP `httpGet` probe 失败不等于 sidecar 自身 crash;必须同时对照 sidecar listen 日志和 probe target 语义。
- 真实 live smoke 超时要先排除脚本入口和 timeout 误报;健康的 Codex stdio 冷启动首 token 可能需要数十秒,10 秒级 transport timeout 不能直接判服务故障。
- 不要重新引入把多条检查挤成顶层 shell 链的 runner。旧 `CI.json``a && b && c` 一旦直接内联到顶层 shell,就会出现前半失败但 TaskRun 继续成功的假绿;原语化 CI 的原则是每个检查保持独立 task/result,失败语义直接由 Tekton 负责。
## PR -> CI -> CD 最短零误判 SOP
1. 工作区与路由预检
```sh
bun scripts/cli.ts ssh G14:/root/hwlab shell 'git status --short --branch && git remote -v | sed -n "1,4p"'
```
- `/root/hwlab` 是固定 source workspace 和 worktree 管理入口;完成预检后,在 `/root/hwlab/.worktree/<task>` 从最新 `origin/G14` 创建任务专属 worktree,再开始本轮代码、文档、测试和提交修改。
- 只有独立 worktree 跟踪正确 base 且 `git status` 只包含本任务文件时才继续;不要在 `/root/hwlab` 根目录直接堆叠并行开发改动,也不要复用其他任务遗留 worktree。
- k3s 只走 `G14:k3s`;不要混用 D601 kubeconfig、master server 执行面或旧 SSH route 语法。
2. Source / PR 预检
```sh
npm run g14:gitops:render -- --no-write
node scripts/g14-ci-plan.mjs --base-ref origin/G14 --target-ref HEAD --pretty
```
- 先看当前内容和长期参考,不按旧 commit 记忆判断“功能是否已合入”。
- GitOps、manifest、poller/reconciler、provider profile 相关改动先跑 render no-writeplanner 用于判断本轮是 docs-only、reuse-only 还是需要真实构建。不要在 source 分支对 ignored generated output 跑 drift check。
- provider/profile 变更在进入 PR 或 CI 之前,先按 `code-agent-chat-readiness.md` 做目标 Pod 最小环;不要把完整 CI/CD 当成 transport 试错工具。
3. 合并或推送到 `G14`
- `G14` 是 poller 的唯一 source branch。只有 source 侧检查通过后,才进入 merge 或 push。
- push 被 fast-forward 拒绝时,先 `fetch``rebase` 到最新 `origin/G14`;不要为了抢跑 poller 强推覆盖别人的 GitOps promote 提交。
4. 确认 CI 已接单
- 先看最新 branch-poller job 或日志,确认它是否识别到新的 `G14` HEAD。
- 再看 `hwlab-ci` 里的 PipelineRun 是否出现 `hwlab-g14-ci-poll-<short12>`。poller 还没创建 PipelineRun 时,不要先把问题归类为 Tekton 故障。
- 如果本轮改的是 poller、Pipeline、RBAC、reconciler 或 render 模板,先确认 `hwlab-g14-control-plane-reconciler` 已经把新控制面 apply 进去,再用新 commit 验证。
5. 确认 CI 真实通过
- 最低通过条件是:`prepare-source``repo-reports-guard``g14-contract-check``codex-api-forwarder-check``plan-artifacts`、per-service fan-out、`collect-artifacts``gitops-promote` 全部成功。
- `build-*` fan-out 要按 result 判断 reused 还是 rebuilt;不要只看 TaskRun 名称。
- reuse-only 变更的目标是 `affectedServices=[]`、全部 service `status=reused`,且不触发无意义 rollout。
6. 确认 CD 真实通过
- 先确认 `G14-gitops` 头 revision 已更新,再看 Argo Application 当前 revision 是否追上该 GitOps revision。
- 然后检查目标 namespace 的 Deployment、StatefulSet、ReplicaSet、Pod ready 和失败事件。
- runtime-only 修复要直接检查 live Deployment template 是否带上新的 probe、env 或 annotation;不要只盯公网 health 里的镜像 commit。
7. 做最终运行态验证
- 先看公网 `/health/live`,再跑 focused live smoke;业务 smoke 必须在目标运行面已经 Healthy 后进行。
- Code Agent 对话链路用 `node scripts/code-agent-chat-smoke.mjs --live` 或显式 `--url http://74.48.78.17:17667/ --timeout-ms 45000`;通过标准是“真实 DEV 路由 + `completed` + 非空 assistant reply”,不是单纯 HTTP 200、非 JSON chunk 或 10 秒内首包。