--- title: 使用 Kustomize 对 Kubernetes 对象进行声明式管理 content_type: task weight: 20 --- [Kustomize](https://github.com/kubernetes-sigs/kustomize) 是一个独立的工具,用来通过 [kustomization 文件](https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kustomization) 定制 Kubernetes 对象。 从 1.14 版本开始,`kubectl` 也开始支持使用 kustomization 文件来管理 Kubernetes 对象。 要查看包含 kustomization 文件的目录中的资源,执行下面的命令: ```shell kubectl kustomize ``` 要应用这些资源,使用 `--kustomize` 或 `-k` 参数来执行 `kubectl apply`: ```shell kubectl apply -k ``` ## {{% heading "prerequisites" %}} 安装 [`kubectl`](/zh-cn/docs/tasks/tools/)。 {{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} ## Kustomize 概述 {#overview-of-kustomize} Kustomize 是一个用来定制 Kubernetes 配置的工具。它提供以下功能特性来管理应用配置文件: * 从其他来源生成资源 * 为资源设置贯穿性(Cross-Cutting)字段 * 组织和定制资源集合 ### 生成资源 {#generating-resources} ConfigMap 和 Secret 包含其他 Kubernetes 对象(如 Pod)所需要的配置或敏感数据。 ConfigMap 或 Secret 中数据的来源往往是集群外部,例如某个 `.properties` 文件或者 SSH 密钥文件。 Kustomize 提供 `secretGenerator` 和 `configMapGenerator`,可以基于文件或字面值来生成 Secret 和 ConfigMap。 #### configMapGenerator 要基于文件来生成 ConfigMap,可以在 `configMapGenerator` 的 `files` 列表中添加表项。 下面是一个根据 `.properties` 文件中的数据条目来生成 ConfigMap 的示例: ```shell # 生成一个 application.properties 文件 cat <application.properties FOO=Bar EOF cat <./kustomization.yaml configMapGenerator: - name: example-configmap-1 files: - application.properties EOF ``` 所生成的 ConfigMap 可以使用下面的命令来检查: ```shell kubectl kustomize ./ ``` 所生成的 ConfigMap 为: ```yaml apiVersion: v1 data: application.properties: | FOO=Bar kind: ConfigMap metadata: name: example-configmap-1-8mbdf7882g ``` 要从 env 文件生成 ConfigMap,请在 `configMapGenerator` 中的 `envs` 列表中添加一个条目。 下面是一个用来自 `.env` 文件的数据生成 ConfigMap 的例子: ```shell # 创建一个 .env 文件 cat <.env FOO=Bar EOF cat <./kustomization.yaml configMapGenerator: - name: example-configmap-1 envs: - .env EOF ``` 可以使用以下命令检查生成的 ConfigMap: ```shell kubectl kustomize ./ ``` 生成的 ConfigMap 为: ```yaml apiVersion: v1 data: FOO: Bar kind: ConfigMap metadata: name: example-configmap-1-42cfbf598f ``` {{< note >}} `.env` 文件中的每个变量在生成的 ConfigMap 中成为一个单独的键。这与之前的示例不同, 前一个示例将一个名为 `application.properties` 的文件(及其所有条目)嵌入到同一个键的值中。 {{< /note >}} ConfigMap 也可基于字面的键值偶对来生成。要基于键值偶对来生成 ConfigMap, 在 `configMapGenerator` 的 `literals` 列表中添加表项。下面是一个例子, 展示如何使用键值偶对中的数据条目来生成 ConfigMap 对象: ```shell cat <./kustomization.yaml configMapGenerator: - name: example-configmap-2 literals: - FOO=Bar EOF ``` 可以用下面的命令检查所生成的 ConfigMap: ```shell kubectl kustomize ./ ``` 所生成的 ConfigMap 为: ```yaml apiVersion: v1 data: FOO: Bar kind: ConfigMap metadata: name: example-configmap-2-g2hdhfc6tk ``` 要在 Deployment 中使用生成的 ConfigMap,使用 configMapGenerator 的名称对其进行引用。 Kustomize 将自动使用生成的名称替换该名称。 这是使用生成的 ConfigMap 的 deployment 示例: ```yaml # 创建一个 application.properties 文件 cat <application.properties FOO=Bar EOF cat <deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: app image: my-app volumeMounts: - name: config mountPath: /config volumes: - name: config configMap: name: example-configmap-1 EOF cat <./kustomization.yaml resources: - deployment.yaml configMapGenerator: - name: example-configmap-1 files: - application.properties EOF ``` 生成 ConfigMap 和 Deployment: ```shell kubectl kustomize ./ ``` 生成的 Deployment 将通过名称引用生成的 ConfigMap: ```yaml apiVersion: v1 data: application.properties: | FOO=Bar kind: ConfigMap metadata: name: example-configmap-1-g4hk9g2ff8 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: my-app name: my-app spec: selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - image: my-app name: app volumeMounts: - mountPath: /config name: config volumes: - configMap: name: example-configmap-1-g4hk9g2ff8 name: config ``` #### secretGenerator 你可以基于文件或者键值偶对来生成 Secret。要使用文件内容来生成 Secret, 在 `secretGenerator` 下面的 `files` 列表中添加表项。 下面是一个根据文件中数据来生成 Secret 对象的示例: ```shell # 创建一个 password.txt 文件 cat <./password.txt username=admin password=secret EOF cat <./kustomization.yaml secretGenerator: - name: example-secret-1 files: - password.txt EOF ``` 所生成的 Secret 如下: ```yaml apiVersion: v1 data: password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg== kind: Secret metadata: name: example-secret-1-t2kt65hgtb type: Opaque ``` 要基于键值偶对字面值生成 Secret,先要在 `secretGenerator` 的 `literals` 列表中添加表项。下面是基于键值偶对中数据条目来生成 Secret 的示例: ```shell cat <./kustomization.yaml secretGenerator: - name: example-secret-2 literals: - username=admin - password=secret EOF ``` 所生成的 Secret 如下: ```yaml apiVersion: v1 data: password: c2VjcmV0 username: YWRtaW4= kind: Secret metadata: name: example-secret-2-t52t6g96d8 type: Opaque ``` 与 ConfigMap 一样,生成的 Secret 可以通过引用 secretGenerator 的名称在 Deployment 中使用: ```shell # 创建一个 password.txt 文件 cat <./password.txt username=admin password=secret EOF cat <deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: app image: my-app volumeMounts: - name: password mountPath: /secrets volumes: - name: password secret: secretName: example-secret-1 EOF cat <./kustomization.yaml resources: - deployment.yaml secretGenerator: - name: example-secret-1 files: - password.txt EOF ``` #### generatorOptions 所生成的 ConfigMap 和 Secret 都会包含内容哈希值后缀。 这是为了确保内容发生变化时,所生成的是新的 ConfigMap 或 Secret。 要禁止自动添加后缀的行为,用户可以使用 `generatorOptions`。 除此以外,为生成的 ConfigMap 和 Secret 指定贯穿性选项也是可以的。 ```shell cat <./kustomization.yaml configMapGenerator: - name: example-configmap-3 literals: - FOO=Bar generatorOptions: disableNameSuffixHash: true labels: type: generated annotations: note: generated EOF ``` 运行 `kubectl kustomize ./` 来查看所生成的 ConfigMap: ```yaml apiVersion: v1 data: FOO: Bar kind: ConfigMap metadata: annotations: note: generated labels: type: generated name: example-configmap-3 ``` ### 设置贯穿性字段 {#setting-cross-cutting-fields} 在项目中为所有 Kubernetes 对象设置贯穿性字段是一种常见操作。 贯穿性字段的一些使用场景如下: * 为所有资源设置相同的名字空间 * 为所有对象添加相同的前缀或后缀 * 为对象添加相同的标签集合 * 为对象添加相同的注解集合 下面是一个例子: ```shell # 创建一个 deployment.yaml cat <./deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx EOF cat <./kustomization.yaml namespace: my-namespace namePrefix: dev- nameSuffix: "-001" commonLabels: app: bingo commonAnnotations: oncallPager: 800-555-1212 resources: - deployment.yaml EOF ``` 执行 `kubectl kustomize ./` 查看这些字段都被设置到 Deployment 资源上: ```yaml apiVersion: apps/v1 kind: Deployment metadata: annotations: oncallPager: 800-555-1212 labels: app: bingo name: dev-nginx-deployment-001 namespace: my-namespace spec: selector: matchLabels: app: bingo template: metadata: annotations: oncallPager: 800-555-1212 labels: app: bingo spec: containers: - image: nginx name: nginx ``` ### 组织和定制资源 {#composing-and-customizing-resources} 一种常见的做法是在项目中构造资源集合并将其放到同一个文件或目录中管理。 Kustomize 提供基于不同文件来组织资源并向其应用补丁或者其他定制的能力。 #### 组织 {#composing} Kustomize 支持组合不同的资源。`kustomization.yaml` 文件的 `resources` 字段定义配置中要包含的资源列表。 你可以将 `resources` 列表中的路径设置为资源配置文件的路径。 下面是由 Deployment 和 Service 构成的 NGINX 应用的示例: ```shell # 创建 deployment.yaml 文件 cat < deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx ports: - containerPort: 80 EOF # 创建 service.yaml 文件 cat < service.yaml apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-nginx spec: ports: - port: 80 protocol: TCP selector: run: my-nginx EOF # 创建 kustomization.yaml 来组织以上两个资源 cat <./kustomization.yaml resources: - deployment.yaml - service.yaml EOF ``` `kubectl kustomize ./` 所得到的资源中既包含 Deployment 也包含 Service 对象。 #### 定制 {#customizing} 补丁文件(Patches)可以用来对资源执行不同的定制。 Kustomize 通过 `patchesStrategicMerge` 和 `patchesJson6902` 支持不同的打补丁机制。 `patchesStrategicMerge` 的内容是一个文件路径的列表,其中每个文件都应可解析为 [策略性合并补丁(Strategic Merge Patch)](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md)。 补丁文件中的名称必须与已经加载的资源的名称匹配。 建议构造规模较小的、仅做一件事情的补丁。 例如,构造一个补丁来增加 Deployment 的副本个数;构造另外一个补丁来设置内存限制。 ```shell # 创建 deployment.yaml 文件 cat < deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx ports: - containerPort: 80 EOF # 生成一个补丁 increase_replicas.yaml cat < increase_replicas.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 3 EOF # 生成另一个补丁 set_memory.yaml cat < set_memory.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: template: spec: containers: - name: my-nginx resources: limits: memory: 512Mi EOF cat <./kustomization.yaml resources: - deployment.yaml patchesStrategicMerge: - increase_replicas.yaml - set_memory.yaml EOF ``` 执行 `kubectl kustomize ./` 来查看 Deployment: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 3 selector: matchLabels: run: my-nginx template: metadata: labels: run: my-nginx spec: containers: - image: nginx name: my-nginx ports: - containerPort: 80 resources: limits: memory: 512Mi ``` 并非所有资源或者字段都支持策略性合并补丁。为了支持对任何资源的任何字段进行修改, Kustomize 提供通过 `patchesJson6902` 来应用 [JSON 补丁](https://tools.ietf.org/html/rfc6902)的能力。 为了给 JSON 补丁找到正确的资源,需要在 `kustomization.yaml` 文件中指定资源的组(group)、 版本(version)、类别(kind)和名称(name)。 例如,为某 Deployment 对象增加副本个数的操作也可以通过 `patchesJson6902` 来完成: ```shell # 创建一个 deployment.yaml 文件 cat < deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx ports: - containerPort: 80 EOF # 创建一个 JSON 补丁文件 cat < patch.yaml - op: replace path: /spec/replicas value: 3 EOF # 创建一个 kustomization.yaml cat <./kustomization.yaml resources: - deployment.yaml patchesJson6902: - target: group: apps version: v1 kind: Deployment name: my-nginx path: patch.yaml EOF ``` 执行 `kubectl kustomize ./` 以查看 `replicas` 字段被更新: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 3 selector: matchLabels: run: my-nginx template: metadata: labels: run: my-nginx spec: containers: - image: nginx name: my-nginx ports: - containerPort: 80 ``` 除了补丁之外,Kustomize 还提供定制容器镜像或者将其他对象的字段值注入到容器中的能力,并且不需要创建补丁。 例如,你可以通过在 `kustomization.yaml` 文件的 `images` 字段设置新的镜像来更改容器中使用的镜像。 ```shell cat < deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx ports: - containerPort: 80 EOF cat <./kustomization.yaml resources: - deployment.yaml images: - name: nginx newName: my.image.registry/nginx newTag: 1.4.0 EOF ``` 执行 `kubectl kustomize ./` 以查看所使用的镜像已被更新: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 2 selector: matchLabels: run: my-nginx template: metadata: labels: run: my-nginx spec: containers: - image: my.image.registry/nginx:1.4.0 name: my-nginx ports: - containerPort: 80 ``` 有些时候,Pod 中运行的应用可能需要使用来自其他对象的配置值。 例如,某 Deployment 对象的 Pod 需要从环境变量或命令行参数中读取读取 Service 的名称。 由于在 `kustomization.yaml` 文件中添加 `namePrefix` 或 `nameSuffix` 时 Service 名称可能发生变化,建议不要在命令参数中硬编码 Service 名称。 对于这种使用场景,Kustomize 可以通过 `vars` 将 Service 名称注入到容器中。 ```shell # 创建一个 deployment.yaml 文件(引用此处的文档分隔符) cat <<'EOF' > deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx command: ["start", "--host", "$(MY_SERVICE_NAME)"] EOF # 创建一个 service.yaml 文件 cat < service.yaml apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-nginx spec: ports: - port: 80 protocol: TCP selector: run: my-nginx EOF cat <./kustomization.yaml namePrefix: dev- nameSuffix: "-001" resources: - deployment.yaml - service.yaml vars: - name: MY_SERVICE_NAME objref: kind: Service name: my-nginx apiVersion: v1 EOF ``` 执行 `kubectl kustomize ./` 以查看注入到容器中的 Service 名称是 `dev-my-nginx-001`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: dev-my-nginx-001 spec: replicas: 2 selector: matchLabels: run: my-nginx template: metadata: labels: run: my-nginx spec: containers: - command: - start - --host - dev-my-nginx-001 image: nginx name: my-nginx ``` ## 基准(Bases)与覆盖(Overlays) Kustomize 中有 **基准(bases)** 和 **覆盖(overlays)** 的概念区分。 **基准** 是包含 `kustomization.yaml` 文件的一个目录,其中包含一组资源及其相关的定制。 基准可以是本地目录或者来自远程仓库的目录,只要其中存在 `kustomization.yaml` 文件即可。 **覆盖** 也是一个目录,其中包含将其他 kustomization 目录当做 `bases` 来引用的 `kustomization.yaml` 文件。 **基准**不了解覆盖的存在,且可被多个覆盖所使用。 覆盖则可以有多个基准,且可针对所有基准中的资源执行组织操作,还可以在其上执行定制。 ```shell # 创建一个包含基准的目录 mkdir base # 创建 base/deployment.yaml cat < base/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx EOF # 创建 base/service.yaml 文件 cat < base/service.yaml apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-nginx spec: ports: - port: 80 protocol: TCP selector: run: my-nginx EOF # 创建 base/kustomization.yaml cat < base/kustomization.yaml resources: - deployment.yaml - service.yaml EOF ``` 此基准可在多个覆盖中使用。你可以在不同的覆盖中添加不同的 `namePrefix` 或其他贯穿性字段。 下面是两个使用同一基准的覆盖: ```shell mkdir dev cat < dev/kustomization.yaml resources: - ../base namePrefix: dev- EOF mkdir prod cat < prod/kustomization.yaml resources: - ../base namePrefix: prod- EOF ``` ## 如何使用 Kustomize 来应用、查看和删除对象 在 `kubectl` 命令中使用 `--kustomize` 或 `-k` 参数来识别被 `kustomization.yaml` 所管理的资源。 注意 `-k` 要指向一个 kustomization 目录。例如: ```shell kubectl apply -k / ``` 假定使用下面的 `kustomization.yaml`: ```shell # 创建 deployment.yaml 文件 cat < deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx ports: - containerPort: 80 EOF # 创建 kustomization.yaml cat <./kustomization.yaml namePrefix: dev- commonLabels: app: my-nginx resources: - deployment.yaml EOF ``` 执行下面的命令来应用 Deployment 对象 `dev-my-nginx`: ```shell > kubectl apply -k ./ deployment.apps/dev-my-nginx created ``` 运行下面的命令之一来查看 Deployment 对象 `dev-my-nginx`: ```shell kubectl get -k ./ ``` ```shell kubectl describe -k ./ ``` 执行下面的命令来比较 Deployment 对象 `dev-my-nginx` 与清单被应用之后集群将处于的状态: ```shell kubectl diff -k ./ ``` 执行下面的命令删除 Deployment 对象 `dev-my-nginx`: ```shell > kubectl delete -k ./ deployment.apps "dev-my-nginx" deleted ``` ## Kustomize 功能特性列表 {#kustomize-feature-list} | 字段 | 类型 | 解释 | |-----------------------|--------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| | namespace | string | 为所有资源添加名字空间 | | namePrefix | string | 此字段的值将被添加到所有资源名称前面 | | nameSuffix | string | 此字段的值将被添加到所有资源名称后面 | | commonLabels | map[string]string | 要添加到所有资源和选择算符的标签 | | commonAnnotations | map[string]string | 要添加到所有资源的注解 | | resources | []string | 列表中的每个条目都必须能够解析为现有的资源配置文件 | | configMapGenerator | [][ConfigMapArgs](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/configmapargs.go#L7) | 列表中的每个条目都会生成一个 ConfigMap | | secretGenerator | [][SecretArgs](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/secretargs.go#L7) | 列表中的每个条目都会生成一个 Secret | | generatorOptions | [GeneratorOptions](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/generatoroptions.go#L7) | 更改所有 ConfigMap 和 Secret 生成器的行为 | | bases | []string | 列表中每个条目都应能解析为一个包含 kustomization.yaml 文件的目录 | | patchesStrategicMerge | []string | 列表中每个条目都能解析为某 Kubernetes 对象的策略性合并补丁 | | patchesJson6902 | [][Patch](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/patch.go#L10) | 列表中每个条目都能解析为一个 Kubernetes 对象和一个 JSON 补丁 | | vars | [][Var](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/var.go#L19) | 每个条目用来从某资源的字段来析取文字 | | images | [][Image](https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/image.go#L8) | 每个条目都用来更改镜像的名称、标记与/或摘要,不必生成补丁 | | configurations | []string | 列表中每个条目都应能解析为一个包含 [Kustomize 转换器配置](https://github.com/kubernetes-sigs/kustomize/tree/master/examples/transformerconfigs) 的文件 | | crds | []string | 列表中每个条目都应能够解析为 Kubernetes 类别的 OpenAPI 定义文件 | ## {{% heading "whatsnext" %}} * [Kustomize](https://github.com/kubernetes-sigs/kustomize) * [Kubectl Book](https://kubectl.docs.kubernetes.io) * [Kubectl 命令参考](/docs/reference/generated/kubectl/kubectl-commands/) * [Kubernetes API 参考](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/)