Flux CD Flux CD

使用 Kustomize Overlay 按环境组织仓库结构

为 dev 和 production 组织一个仓库,并避开 ConfigMap 滚动发布的陷阱。

15 min 中级

真实的应用会运行在多个环境中,这些环境之间只有细微差别:副本数、主机名、配置值。Flux 应用纯 YAML 或 Kustomize overlay,并能检测文件夹使用的是哪一种。本指南展示如何用 Kustomize 为多个环境组织仓库,如何告诉 Flux 每个环境的位置,并介绍一个在修改配置时容易踩到的坑。本指南基于前面指南中的 webapp 示例。

选择布局:用文件夹,而不是分支

你可以把环境建模为长期存在的分支(dev、staging、main),但这会让升级变成一次次合并,也会让各环境逐渐漂移。单一分支加每个环境一个文件夹的方式更容易理解:所有环境并排可见,对其中一个的修改就是一次普通的 diff。

Flux 的文档介绍了三种常见的仓库策略:

  • Monorepo。 所有集群、应用和基础设施都在一个仓库中。最容易上手,也是这里所使用的。
  • 每个环境一个仓库。 例如,staging 和 production 各一个,具有各自独立的访问规则。
  • 每个团队或每个应用一个仓库。 隔离性强,由一个平台仓库来注册每个团队的仓库。

一个典型的 monorepo 如下所示:

fleet-infra/
├── clusters/
│   ├── dev/                 # Flux Kustomizations for the dev cluster
│   └── production/          # Flux Kustomizations for the production cluster
├── infrastructure/          # ingress controller, cert-manager, monitoring
└── apps/
    ├── base/webapp/         # shared manifests
    ├── dev/                 # overlay for dev
    └── production/          # overlay for production

构建 Kustomize base 与 overlay

apps/base/webapp/kustomization.yaml 列出共享资源:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

在 apps/base/webapp/deployment.yaml 中,让容器从一个 ConfigMap 读取设置,该 ConfigMap 将由各个 overlay 生成(base 中不要写 namespace 字段,因为由 overlay 来设置):

          envFrom:
            - configMapRef:
                name: webapp-config

apps/dev/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: webapp-dev
resources:
  - namespace.yaml
  - ../base/webapp
replicas:
  - name: webapp
    count: 2
configMapGenerator:
  - name: webapp-config
    literals:
      - HEADER=Hello from dev

并配有一个创建 webapp-dev 的 namespace.yaml。apps/production/kustomization.yaml 与之类似,只是 namespace: webapp-prod、count: 4、有它自己的命名空间清单和它自己的 HEADER 值。你可以在提交之前检查某个 overlay 会渲染出什么:

kustomize build apps/dev
# or, without installing kustomize separately:
kubectl kustomize apps/dev

让 Flux Kustomization 指向每个 overlay

Flux 会检测路径中的 kustomization.yaml 并为你构建。在每个集群的文件夹中,为每个环境添加一个 Flux Kustomization。clusters/dev/apps.yaml:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./apps/dev
  prune: true
  wait: true
  timeout: 3m

对于 production 集群,clusters/production/apps.yaml 使用 path: ./apps/production。由于每个集群在 bootstrap 时都使用了自己的 --path,每个集群只能看到它自己的文件夹。

让生产环境保持审慎

与带有手动同步按钮的工具不同,Flux 是自动应用的,所以你要通过 Git 为生产环境把关。一种有力的模式是让 dev 跟随一个分支,而让 production 跟随发布的标签。在 production 集群的 source 中:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: fleet-releases
  namespace: flux-system
spec:
  interval: 5m
  url: https://github.com/your-org/fleet-infra
  ref:
    semver: ">=1.0.0"

这样,production 只有在你打标签发布版本时才会变化。对主分支的拉取请求审批是另一种常见的把关方式。

有意识地处理配置变更

修改某个环境中的 HEADER 值,看看会发生什么。Kustomize 会在生成的 ConfigMap 名称后附加内容哈希。新的值会产生新的 ConfigMap(webapp-config-<hash>),并更新 Deployment 的引用,从而触发 Pod 滚动发布。旧的 ConfigMap 现在成为孤儿,而在 prune: true 下 Flux 会为你删除它。这种行为正是你想要的:配置变更会让 Pod 滚动。

手写的普通 ConfigMap 则不同。如果你编辑了一个被 Deployment 通过 envFrom 读取的 ConfigMap,Deployment 的 spec 并没有变化,所以运行中的 Pod 会保留旧值。对于任何在变化时应当重启工作负载的内容,请使用 configMapGenerator。Helm chart 有等价的技术,Helm 指南会介绍。

按集群替换值

有时某个值因集群而异,但你又不想为它建立一整个 overlay,比如域名。Flux Kustomization 可以在构建之后替换变量:

spec:
  postBuild:
    substitute:
      cluster_domain: dev.example.com
    substituteFrom:
      - kind: ConfigMap
        name: cluster-vars

清单中任何 ${cluster_domain} 都会在应用时被替换。请谨慎使用,因为 overlay 更易于审查。

在各环境之间升级变更

采用这种布局,升级就是一连串普通的提交。先修改 dev overlay(例如,在 apps/dev 中运行 kustomize edit set image nginx=nginx:1.26 来升级镜像),让 Flux 应用它,然后验证。接着对 apps/production 做同样的修改,通过一个需要审批的拉取请求。CI 流水线可以自动完成 dev 的提交,而审阅者则把关 production 的提交。Flux 的镜像自动化也可以为你完成 dev 这一步,镜像自动化指南会介绍。

热门指南