kustomize 实战:用 configMapGenerator 实现 ConfigMap 生成与滚动更新
2026/9/23 12:42:32 网站建设 项目流程
  • CLI
  • 开发工具
  • 云原生

【免费下载链接】kustomize

Customization of kubernetes YAML configurations

项目地址:https://gitcode.com/gh_mirrors/ku/kustomize
点击查看免费下载

本指南以 kustomize 仓库中的 examples/zh/configGeneration.md 为骨架,结合api/examples/helloWorld下的真实源码与示例,完整讲解两种声明 ConfigMap 的方式、基于内容哈希的名称后缀机制,以及如何通过"换新 ConfigMap 名称"而非"改旧 ConfigMap 数据"来触发 Deployment 的滚动更新。读完本文,你将掌握configMapGeneratorliterals/files/envs三种数据来源、behaviordisableNameSuffixHash等高级选项,并能在 base/overlay 结构中独立搭建一套可自动滚动更新的配置管理方案。

两种在 kustomization.yaml 中声明 ConfigMap 的方式

kustomize 在一个kustomization中提供两种添加 ConfigMap 的方法:

  1. 将 ConfigMap 声明为 [resource]:把它当作普通资源文件引入;
  2. 通过 configMapGenerator 声明:让 kustomize 根据literalsfilesenvs等输入现场生成 ConfigMap。

kustomization.yaml中,两种方法的格式分别如下:

# 将 ConfigMap 声明为 resource resources: - configmap.yaml # 在 ConfigMapGenerator 中声明 ConfigMap configMapGenerator: - name: a-configmap files: - configs/configfile - configs/another_configfile

两种方式的行为差异是本文的核心:

  • 声明为resource的 ConfigMap 与其他资源一视同仁,kustomize不会在其名称上追加任何哈希后缀;
  • configMapGenerator声明的 ConfigMap 则默认会被追加一个基于内容计算的哈希后缀,ConfigMap 中的任何更改都会导致哈希值变化,进而触发滚动更新

在仓库的 hello_world 示例(原始文件位于 examples/helloWorld)中,官方演示了用 configMapGenerator 替换"将 ConfigMap 声明为 resource"的旧做法——这样生成的 ConfigMap 一旦内容变化,哈希值和滚动更新就会自动发生。

从源码上看,configMapGeneratorKustomization结构体的一个字段,与resourcesnamePrefixnameSuffix等并列,定义在 api/types/kustomization.go 中,其具体参数类型为ConfigMapArgs(见 api/types/configmapargs.go),内部内嵌了 ConfigMap 与 Secret 共用的GeneratorArgs

configMapGenerator 支持的参数

GeneratorArgs定义在 api/types/generatorargs.go,包含:

参数类型说明
namestring生成资源的"部分名称",完整名称形如NamePrefix + name + 内容哈希
namespacestring可选,生成资源的命名空间
behaviorstring生成行为,取值create(新建)/replace(替换)/merge(合并),默认create
literals[]string字面量键值对列表,形如key=value
files[]string文件数据源,形如[{key}=]{path},省略key=时以文件 basename 作为键,值为文件内容;也可直接指定目录
envs[]string环境变量文件数据源,每行一个key=value(类似 Docker/npm 的.env文件)
optionsGeneratorOptions对全局generatorOptions的局部覆盖

其中literalsfilesenvs三个数据源定义在 api/types/kvpairsources.go。files的完整语法还支持显式指定键名,例如:

configMapGenerator: - name: a-configmap files: - configs/configfile # 键为 configfile - configkey=configs/another_configfile # 键为 configkey

MakeConfigMap(见 api/internal/generators/configmap.go)负责把上述输入组装成 ConfigMap 节点:先创建带name/namespace的基础节点,再通过makeValidatedDataMap校验并合并各数据源,最后写入data字段并应用标签、注解与不可变选项。

完整示例:搭建 base 与 staging

接下来按原文档的实操步骤,从零搭建一个 hello-world 应用的 base 与 staging overlay。

建立 base:使用 configMapGenerator

DEMO_HOME=$(mktemp -d) BASE=$DEMO_HOME/base mkdir -p $BASE curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\ /kubernetes-sigs/kustomize\ /master/examples/helloWorld\ /{deployment,service}.yaml" cat <<'EOF' >$BASE/kustomization.yaml commonLabels: app: hello resources: - deployment.yaml - service.yaml configMapGenerator: - name: the-map literals: - altGreeting=Good Morning! - enableRisky="false" EOF

这里configMapGenerator通过literals生成名为the-map的 ConfigMap,包含altGreetingenableRisky两个键。注意enableRisky="false"中的引号是为了保留字符串形式的布尔值。

建立 staging:通过 ConfigMap patch 定制

OVERLAYS=$DEMO_HOME/overlays mkdir -p $OVERLAYS/staging cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml namePrefix: staging- nameSuffix: -v1 commonLabels: variant: staging org: acmeCorporation commonAnnotations: note: Hello, I am staging! resources: - ../../base patches: - path: map.yaml EOF cat <<EOF >$OVERLAYS/staging/map.yaml apiVersion: v1 kind: ConfigMap metadata: name: the-map data: altGreeting: "Have a pineapple!" enableRisky: "true" EOF

staging 这个 [variant](对 base 的定制变体)通过namePrefix: staging-nameSuffix: -v1重命名所有资源,并用patches中的map.yaml对名为the-map的 ConfigMap 打补丁,把问候语改成Have a pineapple!、风险开关改成true

为什么"改数据"不如"换名字":滚动更新的原理

集群中运行的hello-worldDeployment 配置了来自 ConfigMap 的数据,它按名称引用这个 ConfigMap:

grep -C 2 configMapKeyRef $BASE/deployment.yaml

仓库中的 examples/helloWorld/deployment.yaml 展示了这种引用方式——容器通过envvalueFrom.configMapKeyRef指定name: the-map和对应的key

env: - name: ALT_GREETING valueFrom: configMapKeyRef: name: the-map key: altGreeting - name: ENABLE_RISKY valueFrom: configMapKeyRef: name: the-map key: enableRisky

直接修改集群中"存活"的 ConfigMap 数据通常不是好做法:Deployment 无法感知它所引用的 ConfigMap 已经变化,这类更新不会产生任何效果。

推荐的做法是两步走:

  1. 使用新名称创建一个新的 ConfigMap;
  2. 为 deployment 添加 patch,把对应configMapKeyRef字段中的名称值改成新名称。

后一种更改会启动 deployment 中 pod 的滚动更新;旧的 ConfigMap 在不再被任何资源引用后,最终会被 Kubernetes 垃圾回收。

哈希后缀是如何生成的:源码级解析

在这个示例中,patch 要能生效,metadata/name字段中的名称必须与目标资源匹配。但问题在于:文件中写死的名称并不是集群中实际使用的名称——kustomize 设计上会修改从configMapGenerator声明的 ConfigMap 的名称。要查看最终集群中使用的名称,只需运行:

kustomize build $OVERLAYS/staging |\ grep -B 8 -A 1 staging-the-map

输出中可以看到最终名称由三部分组成:

  • 前缀staging-:来自$OVERLAYS/staging/kustomization.yamlnamePrefix字段;
  • 核心名the-map:来自configMapGeneratorname
  • 后缀-v1:来自nameSuffix字段;
  • 哈希-5276h4th55:由 ConfigMap内容计算而来。
kustomize build $OVERLAYS/staging | grep 5276h4th55

这个哈希不是随机生成的。在 api/hasher/hasher.go 中可以看到完整算法(该实现参考了 kubernetes 官方pkg/kubectl/util/hash):

  • 对 ConfigMap,参与哈希的字段是kindmetadata/namedata以及非空的binaryData(见 encodeConfigMap),拼接后经json.Marshal保证键序稳定;
  • 对上述 JSON 字符串取sha256,再截取前 10 个十六进制字符;
  • 为避免十六进制字符与 DNS 名称规则冲突,做一次字符替换:0→g1→h3→ka→me→t(见 encode 函数)。

由此可知:只要 ConfigMap 的data内容(或名称)发生变化,哈希就会变化,从而生成一个全新的名称。这正是滚动更新能被触发的根因——新名称意味着新的configMapKeyRef引用,Deployment 的 pod 模板随之变化,Kubernetes 便会滚动重建 pod。

修改 patch 观察滚动更新:完整实验

现在修改 map patch,更改服务将要使用的问候消息:

sed -i.bak 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml

查看新的问候消息:

kustomize build $OVERLAYS/staging |\ grep -B 2 -A 3 kiwi

再次运行 kustomize 查看新的 ConfigMap 名称:

kustomize build $OVERLAYS/staging |\ grep -B 8 -A 1 staging-the-map

确认 ConfigMap 内容的更改生成了以c2g8fcbf88结尾的三个新名称——一个出现在 ConfigMap 名称本身,另外两个出现在使用该 ConfigMap 的 deployment(即两处configMapKeyRef引用)中:

test 3 == \ $(kustomize build $OVERLAYS/staging | grep c2g8fcbf88 | wc -l); \ echo $?

输出0表示断言通过(共匹配到 3 处)。将这些资源应用到集群,会导致 deployment 的 pod 发生滚动更新,把它们从引用5276h4th55map 重新指向c2g8fcbf88map;旧 map 随后会被系统垃圾回收。

这个"一处修改、三处联动"的现象背后是 kustomize 的引用自动更新机制:所有指向the-mapconfigMapKeyRef都会在构建时被重写为最终的哈希名称。相关内容在 api/internal/accumulator/namereferencetransformer.go 等引用解析代码中实现,并可由 api/krusty/configmaps_test.go 中的大量用例验证。

回滚

回滚非常简单:撤消在源码配置中做的任何编辑,然后在还原后的配置上重新运行 kustomize,并将其应用于集群即可。由于滚动更新完全由"配置内容 → 哈希名称"这一确定性映射驱动,只要源码回到旧状态,构建出的名称就会回到旧哈希,Kubernetes 会再次滚动更新 pod 以匹配。

进阶控制:behavior 与 disableNameSuffixHash

behavior:create / replace / merge

当同一个 kustomization 中同时存在同名资源与configMapGenerator时,可通过behavior控制生成行为(默认create):

  • create:新建,若名称冲突可能报错;
  • replace:用生成的 ConfigMap 替换同名资源;
  • merge:将生成的键值合并进同名资源。

例如 api/krusty/configmaps_test.go 中就大量使用了behavior: mergebehavior: create的组合来测试资源合并语义。

disableNameSuffixHash:关闭哈希后缀

默认行为下,哈希后缀一定会追加到 ConfigMap 名称上。如果你不希望滚动更新被触发(例如 ConfigMap 由应用内动态读取),可以在generatorOptions中关闭该行为:

generatorOptions: disableNameSuffixHash: true configMapGenerator: - name: the-map literals: - altGreeting=Good Morning!

该字段定义于 api/types/generatoroptions.go,其注释明确说明:"如果为 true,则禁用默认的追加名称哈希后缀行为"。注意它是generatorOptions的全局选项,也可以在每个 generator 的options中局部覆盖,覆盖合并逻辑同样实现在 api/types/generatoroptions.go 中。

小结

  • ConfigMap 作为resource声明与通过configMapGenerator声明,核心区别在于后者默认会追加内容哈希后缀并自动联动引用;
  • 哈希由 api/hasher/hasher.go 基于 sha256 计算并做了字符映射,任何内容变化都会产生新名称,从而触发 Deployment 滚动更新;
  • 修改线上配置的正确姿势是"换新名字 + 更新configMapKeyRef引用",而不是原地改写已挂载的 ConfigMap 数据;
  • behavior(create/replace/merge)与disableNameSuffixHash提供了对生成与哈希行为的精细控制。

想进一步动手验证,可以直接查阅 examples/helloWorld 的原始资源(kustomization.yaml、deployment.yaml、configMap.yaml),或对照 api/krusty/configmaps_test.go 中的测试用例,把上面的每一步用kustomize build亲手跑一遍。

  • CLI
  • 开发工具
  • 云原生

【免费下载链接】kustomize

Customization of kubernetes YAML configurations

项目地址:https://gitcode.com/gh_mirrors/ku/kustomize
点击查看免费下载
上一篇:如何快速掌握Box2D:打造真实2D游戏物理效果的终极指南 🎮
下一篇:【亲测免费】 开源项目 `spoof` 使用教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询