- CLI
- 开发工具
- 云原生
【免费下载链接】kustomize
Customization of kubernetes YAML configurations
本指南以 kustomize 仓库中的 examples/zh/configGeneration.md 为骨架,结合api/与examples/helloWorld下的真实源码与示例,完整讲解两种声明 ConfigMap 的方式、基于内容哈希的名称后缀机制,以及如何通过"换新 ConfigMap 名称"而非"改旧 ConfigMap 数据"来触发 Deployment 的滚动更新。读完本文,你将掌握configMapGenerator的literals/files/envs三种数据来源、behavior与disableNameSuffixHash等高级选项,并能在 base/overlay 结构中独立搭建一套可自动滚动更新的配置管理方案。
两种在 kustomization.yaml 中声明 ConfigMap 的方式
kustomize 在一个kustomization中提供两种添加 ConfigMap 的方法:
- 将 ConfigMap 声明为 [resource]:把它当作普通资源文件引入;
- 通过 configMapGenerator 声明:让 kustomize 根据
literals、files、envs等输入现场生成 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 一旦内容变化,哈希值和滚动更新就会自动发生。
从源码上看,configMapGenerator是Kustomization结构体的一个字段,与resources、namePrefix、nameSuffix等并列,定义在 api/types/kustomization.go 中,其具体参数类型为ConfigMapArgs(见 api/types/configmapargs.go),内部内嵌了 ConfigMap 与 Secret 共用的GeneratorArgs。
configMapGenerator 支持的参数
GeneratorArgs定义在 api/types/generatorargs.go,包含:
| 参数 | 类型 | 说明 |
|---|---|---|
name | string | 生成资源的"部分名称",完整名称形如NamePrefix + name + 内容哈希 |
namespace | string | 可选,生成资源的命名空间 |
behavior | string | 生成行为,取值create(新建)/replace(替换)/merge(合并),默认create |
literals | []string | 字面量键值对列表,形如key=value |
files | []string | 文件数据源,形如[{key}=]{path},省略key=时以文件 basename 作为键,值为文件内容;也可直接指定目录 |
envs | []string | 环境变量文件数据源,每行一个key=value(类似 Docker/npm 的.env文件) |
options | GeneratorOptions | 对全局generatorOptions的局部覆盖 |
其中literals、files、envs三个数据源定义在 api/types/kvpairsources.go。files的完整语法还支持显式指定键名,例如:
configMapGenerator: - name: a-configmap files: - configs/configfile # 键为 configfile - configkey=configs/another_configfile # 键为 configkeyMakeConfigMap(见 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,包含altGreeting与enableRisky两个键。注意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" EOFstaging 这个 [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 展示了这种引用方式——容器通过env的valueFrom.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 已经变化,这类更新不会产生任何效果。
推荐的做法是两步走:
- 使用新名称创建一个新的 ConfigMap;
- 为 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.yaml的namePrefix字段; - 核心名
the-map:来自configMapGenerator的name; - 后缀
-v1:来自nameSuffix字段; - 哈希
-5276h4th55:由 ConfigMap内容计算而来。
kustomize build $OVERLAYS/staging | grep 5276h4th55这个哈希不是随机生成的。在 api/hasher/hasher.go 中可以看到完整算法(该实现参考了 kubernetes 官方pkg/kubectl/util/hash):
- 对 ConfigMap,参与哈希的字段是
kind、metadata/name、data以及非空的binaryData(见 encodeConfigMap),拼接后经json.Marshal保证键序稳定; - 对上述 JSON 字符串取sha256,再截取前 10 个十六进制字符;
- 为避免十六进制字符与 DNS 名称规则冲突,做一次字符替换:
0→g、1→h、3→k、a→m、e→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-map的configMapKeyRef都会在构建时被重写为最终的哈希名称。相关内容在 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: merge与behavior: 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
相关推荐
Kustomize 配置生成实战:ConfigMapGenerator 与滚动更新机制解析
Kustomize 配置生成实战:ConfigMapGenerator 与滚动更新机制解析 本文围绕 kustomize 的 configMapGenerato
CLI开发工具云原生kustomize configMapGenerator 实战指南:从文件、literals 与 env 文件生成 ConfigMap
kustomize configMapGenerator 实战指南:从文件、literals 与 env 文件生成 ConfigMap ConfigMap 是
CLI开发工具云原生kustomize ConfigMapGenerator 完全指南:参数解析、源码原理与滚动更新实践
kustomize ConfigMapGenerator 完全指南:参数解析、源码原理与滚动更新实践 导读 ConfigMapGenerator 是 kusto
CLI开发工具云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考