120 lines
19 KiB
Markdown
120 lines
19 KiB
Markdown
# 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。
|
||
- CI:Tekton 在 G14 k3s 内运行 `hwlab-g14-ci-image-publish` Pipeline,读取 `CI.json` 命令,按 source commit 构建镜像并推送到 G14 registry。
|
||
- Artifact:镜像使用 commit tag,例如 `127.0.0.1:5000/hwlab/hwlab-cloud-api:<shortCommit>`;digest 由 registry 返回,CI report 只作为审计证据,不作为 CD 真相。
|
||
- Branch split:`G14` 是源码监控分支;`G14-gitops` 是 Tekton promotion 写入的生成分支,保存 `deploy/gitops/g14/**` desired state。
|
||
- CD:Argo CD 只消费 `G14-gitops:deploy/gitops/g14/runtime-dev` 与 `deploy/gitops/g14/runtime-prod` 的 Git desired state,不重新构建镜像,不读取 D601 状态,不获取 legacy DEV CD Lease。
|
||
- FRP:G14 DEV 通过 `hwlab-dev/hwlab-g14-frpc` 暴露 `17666/17667`,G14 PROD 通过 `hwlab-prod/hwlab-g14-prod-frpc` 暴露 `18666/18667`;master frps 的 `deploy/frp/frps.dev.toml` 与实际 `/etc/frp/frps.toml` 必须同时放行这四个 G14 端口。
|
||
- 并行性:不同 source commit 的 CI build 不共享发布锁;并行安全由 immutable commit tag/digest 和 Git desired state 保证。最终运行版本由 Argo CD 当前同步的 Git revision 决定。
|
||
|
||
## 生成入口
|
||
|
||
`scripts/g14-gitops-render.mjs` 是 G14 专用转换器:
|
||
|
||
- 读取 `CI.json`,生成 Tekton Pipeline 和 PipelineRun 样板。
|
||
- 读取 `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 验收证据中。
|
||
- 生成 `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/gitops/g14/**` 推送到该分支,避免把生成提交继续写回 `G14`。
|
||
- 默认 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、Docker-in-Docker sidecar 和 publish step 都注入 proxy/no_proxy;publish step 必须先在 DIND 内 `docker pull $HWLAB_DEV_BASE_IMAGE`,再进入 DEV base-image preflight、build 和 registry push,保证 npm、base image 拉取和 registry push 走 G14 本机代理边界。
|
||
- `ci-json` 不允许每次运行时重新 `apt-get install` 或 `npx playwright install --with-deps`。它必须使用已经预热的 CI 基础镜像,当前默认由 `scripts/g14-gitops-render.mjs` 固定为 `mcr.microsoft.com/playwright@sha256:b0ab6f3cb99aa7803adbc14d9027ec1785fc6e433b97e134e0f8fe61683b6b53`;该镜像在 G14 k3s/containerd 中已缓存,包含 Node、npm、Git、Python3、OpenSSH、curl、Playwright 系统依赖和 `/ms-playwright/chromium-1217`。脚本启动时必须输出 `ci-base-image` 和 `playwright-cache-check` 结构化日志;缺少工具或 Chromium cache 时直接失败并修基础镜像,不能回退到 runtime apt 下载。若以后需要 Node 22 固定版本,先构建并推送 G14 registry 内的 Node22+Playwright runner 镜像,再修改 `HWLAB_G14_CI_JSON_IMAGE`/render 默认值。
|
||
- G14 host 只用于 source workspace、GitOps render、k3s 控制和轻量语法/静态合同检查;不要把 host 当成浏览器执行面。当前 G14 host OS 可能比 Playwright 官方 browser 包支持矩阵更新,`npx playwright install chromium` 会失败或 browser executable 不存在。`web:check` 默认允许这类明确的 Playwright/browser runtime 环境限制作为 `environment_blocker`/`skip` 通过源码合同;真正的布局、移动端和交互 browser smoke 必须在 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-v1`,包含 Node 22、npm、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 脚本在 apt/apk、npm、Playwright browser、Docker base image pull 和 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>
|
||
npm run g14:gitops:check
|
||
```
|
||
|
||
## Monorepo 组件计划与兼容 render
|
||
|
||
HWLAB 是 monorepo,G14 CI/CD 加速必须按组件输入判断构建和滚动,但不能破坏当前 `CI.json`、`deploy/deploy.json` 与全量 source commit render 合同。
|
||
|
||
- `scripts/g14-ci-plan.mjs` 是只读 planner,默认读取当前 `CI.json`、`deploy/deploy.json` 和 `deploy/artifact-catalog.dev.json`,输出 `affectedServices`、`reusedServices`、`componentCommitId`、`componentInputHash`、`dockerfileHash`、`baseImageDigest`、`buildArgsHash` 和原因;它不得修改 CI、deploy、catalog 或 GitOps 文件。
|
||
- 服务清单兼容顺序固定为:显式 `--services`、`deploy.services[]`、`deploy.k3s.serviceMappings[]`、`internal/protocol.SERVICE_IDS`。因此旧 `deploy/deploy.json` 形态和当前完整 `deploy.services[]` 形态都必须能被 planner 识别。
|
||
- `CI.json` 仍是 CI 命令与禁用项合同来源;可选新增 `g14CiPlan.components.<serviceId>` 覆盖 `componentPaths`、`sharedPaths`、`runtimeDeps`、`buildSystemPaths`,但不得要求删除或改写现有 `commands` / `forbidden` 字段。
|
||
- `scripts/g14-artifact-publish.mjs` 默认启用组件级 lazy build:先运行 planner,再只构建/推送 `affectedServices`,`reusedServices` 必须从 `deploy/artifact-catalog.dev.json` 复用已有 sha256 digest;如果 catalog 没有可验证 digest,必须结构化阻塞并提示先跑全量 publish。`--full-build` 是兼容回退开关,用于强制构建/发布每个 required service。
|
||
- `scripts/g14-artifact-publish.mjs` 的 publish report 必须携带 planner 的 per-service 元数据;`scripts/refresh-artifact-catalog.mjs` 把这些字段复制进 `deploy/artifact-catalog.dev.json` 和 `deploy/deploy.json` 的对应 service。旧字段 `commitId`、`image`、`imageTag`、`digest`、`publishState` 语义不变,旧检查脚本不得因为新增字段失败。
|
||
- `scripts/g14-gitops-render.mjs` 默认启用混合 desired state:workload 的 container image、`HWLAB_IMAGE`、`HWLAB_IMAGE_TAG` 和 pod template `source-commit` 来自 `deploy.services[]` 的 per-service image/env;全局 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 刷新 workspace 内的 `deploy/deploy.json`、`deploy/artifact-catalog.dev.json` 和 `deploy/k8s/base/workloads.yaml`,并把该 per-service artifact truth 以 `chore: promote G14 GitOps source <short>` 提交回 `G14` source branch;poller 必须跳过这类生成提交,避免自触发循环。这样下一轮 planner 才能从最新 catalog 复用上一轮已构建服务,避免把未变服务回滚到旧 digest。
|
||
- 后续 Tekton 并发化只能以 planner 输出作为输入,先做到每个 changed service 一个构建任务;unchanged service 复用 catalog digest,且不能改 pod template,避免无意义 rollout。没有完整 per-service desired state 证据时,必须使用 `--full-build` 或 `--legacy-source-images` 回退,而不是猜测复用。
|
||
|
||
|
||
## 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 bridge;Service 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 forwarder,forwarder upstream 为 hyueapi | 独立 Codex/OpenAI-compatible Responses API 通道;不得依赖 DeepSeek bridge。 |
|
||
|
||
`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`。Codex app-server 启动参数必须同时设置 provider base URL、provider `name`、`model` 和 `review_model`,否则 Codex/DeepSeek 链路可能在模型目录或 `/v1/responses` 阶段退化成 `model=None`。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` sidecar:Service 端口 4000 指向 bridge,bridge 只做 zstd request body 解压、删除 `Content-Encoding`/重写 `Content-Length`、丢弃非 `function` tool 类型和 Codex-style `GET /v1/models` 目录适配,再把请求转发给 4001 的 Moon Bridge。DeepSeek API 只接受 `function` tools,bridge 必须在转发前丢弃 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 每 5 分钟通过 Git SSH 拉取 `G14` HEAD,并用 source commit 的前 12 位生成 PipelineRun 名称 `hwlab-g14-ci-poll-<short12>`。
|
||
|
||
如果同名 PipelineRun 已存在,poller 直接跳过;如果不存在,则创建新的 PipelineRun。这样可以用 Kubernetes 原生 CronJob、ServiceAccount、RBAC 和 Tekton API 实现无 GitHub webhook 的分支监控,同时避免同一个 commit 被反复派单。历史上的 `chore: promote G14 GitOps source ...` 生成提交会被 poller 跳过。
|
||
|
||
Pipeline 的标准路径是:`G14` source commit -> `CI.json` Tekton 测试 -> commit-tagged image push 到 G14 registry -> render `deploy/gitops/g14/**` -> push `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 正常但后续测试失败,按对应 `CI.json` 命令和业务检查排障。
|
||
|
||
手动 render/apply 只允许作为 bootstrap 或 reconciler 故障抢修;正常路径必须由 `hwlab-g14-control-plane-reconciler` 自动完成 CI 控制面 manifest 更新。
|
||
|
||
## 集群资源
|
||
|
||
- `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。
|
||
|
||
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 或流量切换动作。
|