约定与规范
这一页汇总在 k8s-config 仓库里写 manifest 时必须遵守的统一约定:目录结构、取密方式、镜像版本管理、升级守门与合并前校验。改动几分钟后就会被 ArgoCD 自动同步进集群(详见 GitOps 工作流),所以这些约定既是风格也是安全护栏。
每个 app 目录的 kustomize 约定
每个 app 目录(applications/<category>/<app>/)都是一个 kustomize 根,由 kustomization.yaml 统一设置 namespace: <category> 和 namePrefix: <category>-,这样生成的 Application app-magicbox 就对应到资源 app-magicbox-*。
一个典型 app 目录里会出现这些文件:
- Name
- deployment.yaml / statefulset.yaml
- Description
- 工作负载定义。多副本服务需要
podAntiAffinity配合matchLabelKeys: pod-template-hash;单副本的 PDB 必须用maxUnavailable: 1,用minAvailable: 1会卡住节点排空。
- Name
- service.yaml
- Description
- ClusterIP / LoadBalancer 等 Service。
- Name
- external-secret.yaml
- Description
- 从 Vault 拉密钥的 ExternalSecret,见下一节。
- Name
- servicemonitor.yaml / prometheusrule.yaml
- Description
- Prometheus 抓取目标与告警规则。
- Name
- hpa.yaml / vpa.yaml / pdb
- Description
- 自动扩缩容与中断预算。
Secrets 走 ExternalSecret + Vault
密钥绝不进 git。所有敏感配置通过 ExternalSecret 从 Vault 拉取,由 ClusterSecretStore vault-backend(定义在 applications/infrastructure/external-secrets/clustersecretstores-vault.yaml,挂载点 secret,KV v2)统一对接。
约定的 remoteRef.key 形如 yldm/production/<app>,另有共享路径如 yldm/database。注意一个容易踩的前缀差异:ESO 的 remoteRef.key 省略 secret/ 挂载前缀(store 会自动补上),而 vault CLI 不省略。也就是说同一份密钥,写入用 vault kv put secret/yldm/production/<app> ...,而 ExternalSecret 里只写 yldm/production/<app>。
# external-secret.yaml 里的 remoteRef —— 不带 secret/ 前缀
spec:
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
data:
- secretKey: <key>
remoteRef:
key: yldm/production/<app>
property: <field>
镜像 tag 由 argocd-image-updater 管理
镜像 tag 由 argocd-image-updater 统一管理,不要手工钉版本。每个 app 的更新规则是一份独立 CR,放在 applications/infrastructure/argocd-image-updater/imageupdaters/<app>.yaml(semver 策略,严格匹配 ^\d+\.\d+\.\d+$)。它会把新 tag 通过 git commit 回写到该 app 的 kustomization.yaml 的 images: 块,commit message 形如 build: automatic update of …。
build: automatic update of … 的提交,那是 image-updater 自己写的,不要去和它抢着手钉 tag。私有业务镜像(ghcr.io/yldm-tech/ 等)和 image-updater 托管的镜像在 renovate.json 里被显式 enabled: false,Renovate 不会去碰它们。版本升级常是破坏性变更
对自建基础设施来说,一次版本号 bump 往往是破坏性变更:新 tag 经常还需要配套的 CRD、探针、env、配置或数据迁移,光换 tag 会让组件 crash-loop。这一点已经咬过多次:
| 组件 | 单纯换 tag 的后果 | 同 PR 还要做的事 |
|---|---|---|
| vault | 2.0.x 镜像以非 root 运行,mlock 失败、core 初始化报 cannot allocate memory 后 crash-loop | 在 vault.hcl 设 disable_mlock = true(#1019) |
| mongo | 需要 AVX,且 6.0 移除了 mongo shell,探针仍用 mongo --eval 会一直 un-Ready | 节点 vCPU 升到 x86-64-v3(在 pve-infra),探针改用 mongosh;跨大版本需清空数据目录(#1022) |
| metallb | minor 升级带新 CRD + 探针/env 变化,控制器与 speaker 双双 crash-loop,L2 ARP 不稳 | 从上游 native manifest 移植 CRD / RBAC / 探针 / env(#1030) |
| meilisearch | 引擎拒绝打开旧版本写的库且不自动迁移,v1.11→v1.46 直接 crash-loop | dumpless 升级须从 ≥v1.12 一步步来,或明确清空空库(#968) |
这类包在 renovate.json 里带 dependencyDashboardApproval,PR 不会自动开,必须先在 dependency dashboard 上批准。当你接下这个 bump 时,要把对应的上游 manifest 改动(或数据迁移 / 清库)放进同一个 PR —— 永远不要单独合一个裸 tag。
renovate.json 还对所有第三方基础设施的 major 升级(postgres / gitea / mongo / traefik / charts 等)统一要求 dashboard 批准;minor + patch 则按周分组成一个 PR 以降噪。合并前的 validate-manifests 校验
CI(.github/workflows/validate-manifests.yml,每个 PR 都跑)是合并的门槛,建议本地先复现同样的三步检查再推送:
# 1. 每个 kustomize 根都必须能 build
find . -name kustomization.yaml -not -path './.git/*' -exec dirname {} \; | sort -u \
| while read d; do kustomize build "$d" >/dev/null || echo "FAILED: $d"; done
# 先把所有内容渲染到一处,供后面的 schema / 规则检查用
find . -name kustomization.yaml -not -path './.git/*' -exec dirname {} \; | sort -u \
| xargs -I{} sh -c 'kustomize build "{}"; echo ---' > /tmp/all.yaml
# 2. 原生 k8s 对象必须通过 schema 校验(CRD 跳过)
kubeconform -strict -summary -ignore-missing-schemas -schema-location default /tmp/all.yaml
# 3. PrometheusRule 必须能解析(PromQL)
python3 .github/scripts/extract-prometheus-rules.py /tmp/all.yaml /tmp/rules
promtool check rules /tmp/rules/*.yaml
工具版本固定在 workflow 的 env 里(kustomize 5.4.3、kubeconform 0.6.7、promtool 2.54.1)。要快速校验单个 app,跑 kustomize build applications/<category>/<app> 即可。