fix: clarify G14 Argo template health
This commit is contained in:
@@ -60,13 +60,14 @@ G14 CI/CD 加速的第一判定标准不是 PipelineRun 总耗时单点变短,
|
||||
- 并发 fan-out:per-service TaskRun 应由 Tekton/k8s scheduler 同时调度,多个 service 的 build/reuse 窗口应接近“最慢单个服务任务耗时”,而不是所有服务串行相加。reuse-only 场景的 fan-out 窗口当前基线约为十几秒;真实组件构建场景仍需按 changed service 单独记录 BuildKit 耗时和 cache hit 情况。
|
||||
- CD rollout:unchanged service 必须复用 `deploy.services[]` 的 image/digest/env,pod template 不应因全局 source commit 改变而无意义滚动。手写 manifest(例如 `deepseek-proxy`、device-agent 类辅助 workload)只要复用某个已发布服务镜像,也必须走同一个 deploy 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 判定:`spec.suspend=true` 的模板型 Job 会让 Application 总 health 显示 `Suspended`。`hwlab-agent-worker-template`、`hwlab-cli-template` 这类只作为手动启动模板的 Job 必须带 `argocd.argoproj.io/ignore-healthcheck: "true"`,避免 DEV 实际 workload 全部 ready 时仍被误判为 Suspended;真正发布验收仍以长驻 workload ready、公网 health 和失败 Pod 清单为准。
|
||||
|
||||
当前仍然慢的主要位置:
|
||||
|
||||
- `ci-json` 仍是大头,当前量级约 90 秒。它包含 Git clone、checkout、`npm ci`、Playwright cache probe 和 `CI.json` 命令执行。继续加速应优先减少每轮 `npm ci` 成本,例如使用预热 workspace/cache、依赖层镜像或把源码校验拆成更细粒度的 Tekton Task。
|
||||
- `gitops-promote` 仍是大头,当前量级约 100 秒。它包含 source branch freshness check、artifact catalog refresh、render/check、clone `G14-gitops`、复制 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 Task,changed service 才启动 BuildKit。
|
||||
- Argo sync/health 不是 instant。`Sync=Synced` 后 health 仍可能因为 replicas=0 的模板 Deployment、Job 模板或 StatefulSet 旧 revision 显示 `Suspended`/`Progressing`。这时应先看实际长驻 workload ready 与具体 unhealthy resource,不要只看 Application 总 health。
|
||||
- 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 总耗时、`ci-json`、`plan-artifacts`、per-service fan-out 起止窗口、`collect-artifacts`、`gitops-promote`、Argo sync 到 workload ready 的时间,并标明本轮是 reuse-only、单组件 build 还是多组件 build。没有这些分段数据,不要只用“感觉变快/变慢”判断优化成败。
|
||||
|
||||
@@ -128,10 +128,15 @@ function usage() {
|
||||
` --prod-web-endpoint URL default: ${defaultProdWebEndpoint}`,
|
||||
" --use-deploy-images default: render workload images/env from deploy/deploy.json per service",
|
||||
" --legacy-source-images compatibility fallback: render every workload with the source revision tag",
|
||||
" --check verify generated files are current without writing"
|
||||
" --check verify generated files are current without writing",
|
||||
" --no-write plan and print generated file list without writing"
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function generatedPath(filePath) {
|
||||
return path.isAbsolute(filePath) ? filePath : path.join(repoRoot, filePath);
|
||||
}
|
||||
|
||||
async function readJson(relativePath) {
|
||||
return JSON.parse(await readFile(path.join(repoRoot, relativePath), "utf8"));
|
||||
}
|
||||
@@ -153,7 +158,7 @@ async function resolveSourceRevision(value) {
|
||||
async function defaultCheckSourceRevision(args) {
|
||||
if (!args.check || args.sourceRevision) return args;
|
||||
try {
|
||||
const raw = await readFile(path.join(repoRoot, args.outDir, "source.json"), "utf8");
|
||||
const raw = await readFile(generatedPath(path.join(args.outDir, "source.json")), "utf8");
|
||||
const parsed = JSON.parse(raw);
|
||||
if (typeof parsed.sourceCommit === "string" && sourceCommitPattern.test(parsed.sourceCommit)) {
|
||||
return { ...args, sourceRevision: parsed.sourceCommit };
|
||||
@@ -312,6 +317,11 @@ function transformWorkloads({ workloads, deploy, source, registryPrefix, runtime
|
||||
annotate(item.metadata, {
|
||||
"argocd.argoproj.io/sync-options": "Force=true,Replace=true"
|
||||
});
|
||||
if (item.spec?.suspend === true) {
|
||||
annotate(item.metadata, {
|
||||
"argocd.argoproj.io/ignore-healthcheck": "true"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (serviceIdForWorkload(item, null) === "hwlab-tunnel-client" && typeof item.spec?.replicas === "number") {
|
||||
item.spec.replicas = 0;
|
||||
@@ -2254,8 +2264,8 @@ async function plannedFiles(args) {
|
||||
}
|
||||
|
||||
async function writeFiles(files) {
|
||||
for (const [relativePath, content] of files) {
|
||||
const absolutePath = path.join(repoRoot, relativePath);
|
||||
for (const [filePath, content] of files) {
|
||||
const absolutePath = generatedPath(filePath);
|
||||
await mkdir(path.dirname(absolutePath), { recursive: true });
|
||||
await writeFile(absolutePath, content);
|
||||
}
|
||||
@@ -2263,14 +2273,14 @@ async function writeFiles(files) {
|
||||
|
||||
async function checkFiles(files) {
|
||||
const mismatches = [];
|
||||
for (const [relativePath, expected] of files) {
|
||||
for (const [filePath, expected] of files) {
|
||||
let actual = null;
|
||||
try {
|
||||
actual = await readFile(path.join(repoRoot, relativePath), "utf8");
|
||||
actual = await readFile(generatedPath(filePath), "utf8");
|
||||
} catch (error) {
|
||||
if (error?.code !== "ENOENT") throw error;
|
||||
}
|
||||
if (actual !== expected) mismatches.push(relativePath);
|
||||
if (actual !== expected) mismatches.push(filePath);
|
||||
}
|
||||
return mismatches;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user