【转载】使用 Kustomize 对 Kubernetes 对象进行声明式管理

154 阅读13分钟

介绍

Kustomize是一个独立的工具,用来通过kustomization文件定制Kubernetes对象。

它提供以下功能特性来管理应用配置文件:

  • 从其他来源生成资源
  • 为资源设置贯穿性(Cross-Cutting)字段
  • 组织和定制资源集合

从1.14版本开始,kubectl开始支持使用kustomization文件来管理Kubernetes对象。要查看包含kustomization文件目录中的资源,执行下面命令:

kubectl kustomize <kustomization_directory>

要应用这些资源,使用参数--kustomize-k标志来执行kubectl apply:

kubectl apply -k <kustomization_directory>

生成资源

ConfigMap和Secret包含其他Kubernetes对象(如Pod)所需要的配置和敏感数据。ConfigMap和Secret中的数据来源往往是集群外部,例如某个.properties文件或者SSH密钥文件。

Kustomize提供secretGeneratorconfigMapGenerator,可以基于文件或字面值生成SecretConfigMap

configMapGenerator

要基于文件来生成ConfigMap,可以在configMapGeneratorfiles列表中添加表项。下面是一个根据.properties文件的数据条目来生成ConfigMap的示例:

# 生成一个 application.properties 文件
cat <<EOF >application.properties
FOO=Bar
EOF

# 生成kustomization.yaml
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

所生成的ConfigMap可以使用瞎main的命令来检查:

kubectl kustomize ./

所生成的ConfigMap为:

apiVersion: v1
data:
  application.properties: |
    FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-g4hk9g2ff8

要从env文件生成ConfigMap,需要在configMapGenerator中的envs列表中添加一个条目。这也可以用于通过省略=和值来设置本地环境变量的值。

建议谨慎使用本地环境变量填充功能,用补丁覆盖通常更易于维护。当无法亲送预测变量的值时,在环境中设置值可能很有用。

下面是一个用.env文件里的数据生成ConfigMap的例子:

# 创建一个.env文件
# BAZ 将使用本地环境变量
cat <<EOF >.env
FOO=Bar
BAZ
EOF


# 创建kustomization.yaml
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  envs:
  - .env
EOF

可以使用以下命令检查生成的ConfigMap:

BAZ=Qux kubectl kustomize ./

生成的ConfigMap为:

apiVersion: v1
data:
  BAZ: Qux
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-892ghb99c8

说明:

.env文件中的每个变量在生成ConfigMap中称为一个单独的键。这与之前的示例不同,前一个示例将一个名为.properties的文件(及其所有条目)潜入到同一个键的值中。

ConfigMap也可基于字面的键值对来生成,要基于键值对来生成ConfigMap,在configMapGeneratorliterals列表中添加表项。下面的例子展示如何使用简直对中的数据条目来生成ConfigMap对象:

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
  literals:
  - FOO=Bar
EOF

可以使用下面的命令检查所生成的ConfigMap:

kubectl kustomize ./

所生成的ConfigMap为:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-2-42cfbf598f

要在Deployment中使用生成的ConfigMap,使用configMapGenerator的名称对其进行引用,Kustomize将自动使用生成的名称替换该名称。

示例:

cat <<EOF >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 <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

生成ConfigMap和Deployment:

kubectl kustomize ./

生成的Deployment将通过名称引用生成的ConfigMap:

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

可以基于文件或者键值对生成Secert。要使用文件内容来生成Secret,在secretGenerator下面的files里列表中添加表项。

下面是一个根据文件中数据来生成Secret对象的示例:

# 创建一个password.txt文件
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

同样使用kubectl kustomize ./命令生成Secret,所生成Secret如下:

apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-2kdd8ckcc7
type: Opaque

要基于键值对字面值生成Secret,先要在secretGeneratorliterals列表中添加表项。

下面是基于键值对中数据条目生成Secret的示例:

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
  literals:
  - username=admin
  - password=secret
EOF

生成的Secret如下所示:

apiVersion: v1
data:
  password: c2VjcmV0
  username: YWRtaW4=
kind: Secret
metadata:
  name: example-secret-2-8c5228dkb9
type: Opaque

与ConfigMap一样,生成的Secret可以通过引用名称的方式在Deployment中使用:

cat <<EOF >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 <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

生成的Deployment将通过名称引用生成的Secret:

apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-2kdd8ckcc7
type: Opaque
---
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: /secrets
          name: password
      volumes:
      - name: password
        secret:
          secretName: example-secret-1-2kdd8ckcc7

generatorOptions

所生成的ConfigMap和Secret都包含内容哈希值后缀。这是为了确保内容发生变化时,所生成的事最新的ConfigMap或Secret。

要禁止自动添加后缀的行为,可以使用generatorOptions。除此之外,为生成的ConfigMap和Secret指定贯穿性选项也是可以的。

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
  literals:
  - FOO=Bar
generatorOptions:
  disableNameSuffixHash: true
  labels:
    type: generated
  annotations:
    note: generated
EOF

运行kubectl kustomize ./来查看所生成的ConfigMap:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  annotations:
    note: generated
  labels:
    type: generated
  name: example-configmap-3

设置贯穿性字段

在项目中为所有Kubernetes对象设置贯穿性字段事一种常见的操作,贯穿性字段的一些使用常见如下:

  • 为所有资源设置相同的命名空间
  • 为所有对象添加相同的前缀或者后缀
  • 为对象添加相同的标签集合
  • 为对象添加相同的注解集合

示例:

cat <<EOF >./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 <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
  app: bingo
commonAnnotations:
  oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF

执行kubectl kustomize ./查看这些字段都被设置到Deployment资源上了:

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

组织和定制资源

一种常见的做法是在项目中构造资源集合并将其放到同一个文件或目录中管理。Kustomize提供基于不同文件来组织资源并向其应用补丁或者其他定制的能力。

组织

Kustomize支持组合不同的资源。kustomization.yaml文件的resources字段定义配置中要包含的资源列表。可以将resources列表中的路径设置为资源配置文件的路径。

下面是由Deployment和Service构成的nginx应用的示例:

# 创建 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
        ports:
        - containerPort: 80
EOF

# 创建 service.yaml 文件
cat <<EOF > 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 <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

kubectl kustomize ./所得到的资源中既包含Deployment也包含Service对象。

apiVersion: v1
kind: Service
metadata:
  labels:
    run: my-nginx
  name: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
---
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: nginx
        name: my-nginx
        ports:
        - containerPort: 80

定制

补丁文件(Patches)可以用来对资源执行不同的定制。 Kustomize 通过 patchesStrategicMergepatchesJson6902 支持不同的打补丁机制。patchesStrategicMerge 的内容是一个文件路径的列表,其中每个文件都应可解析为 策略性合并补丁(Strategic Merge Patch)。 补丁文件中的名称必须与已经加载的资源的名称匹配。 建议构造规模较小的、仅做一件事情的补丁。

例如,构造一个补丁来增加 Deployment 的副本个数;构造另外一个补丁来设置内存限制。

# 创建 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
        ports:
        - containerPort: 80
EOF

# 生成一个补丁 increase_replicas.yaml
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
EOF

# 生成另外一个补丁 set_memroy.yaml
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-nginx
        resources:
          limits:
            memory: 512Mi
EOF

# 生成 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF

执行kubectl kustomize ./来查看Deployment:

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 补丁 的能力。

为了给 JSON 补丁找到正确的资源,需要在 kustomization.yaml 文件中指定资源的 组(group)、版本(version)、类别(kind)和名称(name)。

例如,为某 Deployment 对象增加副本个数的操作也可以通过 patchesJson6902 来完成:

# 创建一个 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
        ports:
        - containerPort: 80
EOF

# 创建一个 JSON 补丁文件
cat <<EOF > patch.yaml
- op: replace
  path: /spec/replicas
  value: 3
EOF

# 创建 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-nginx
  path: patch.yaml
EOF

执行kubectl kustomize ./以查看replicas字段被更新:

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 字段设置新的镜像来更改容器中使用的镜像。

# 创建 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
        ports:
        - containerPort: 80
EOF

# 创建 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
  newName: my.image.registry/nginx
  newTag: 1.4.0
EOF

执行kubectl kustomize ./以查看所使用的镜像已被更新:

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 文件中添加 namePrefixnameSuffix 时 Service 名称可能发生变化,建议不要在命令参数中硬编码 Service 名称。 对于这种使用场景,Kustomize 可以通过 vars 将 Service 名称注入到容器中。

# 创建一个 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 <<EOF > 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 <<EOF >./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:

apiVersion: v1
kind: Service
metadata:
  labels:
    run: my-nginx
  name: dev-my-nginx-001
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
---
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

基准与覆盖

Kustomize 中有 基准(bases)覆盖(overlays) 的概念区分。

基准 是包含 kustomization.yaml 文件的一个目录,其中包含一组资源及其相关的定制。 基准可以是本地目录或者来自远程仓库的目录,只要其中存在 kustomization.yaml 文件即可。

覆盖 也是一个目录,其中包含将其他 kustomization 目录当做 bases 来引用的 kustomization.yaml 文件。 基准不了解覆盖的存在,且可被多个覆盖所使用。 覆盖则可以有多个基准,且可针对所有基准中的资源执行组织操作,还可以在其上执行定制。

# 创建一个基准的目录
mkdir base

# 创建 base/deployment.yaml
cat <<EOF > 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 <<EOF > 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 <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

此基准可在多个覆盖中使用。

可以在不同的覆盖中添加不同的namePrefix或其他贯穿性字段。

下面是两个使用同一基准的覆盖:

mkdir dev
cat <<EOF > dev/kustomization.yaml
bases:
- ../base
namePrefix: dev-
EOF

mkdir prod
cat <<EOF > prod/kustomization.yaml
bases:
- ../base
namePrefix: prod-
EOF

分别执行kubectl kustomize devkubectl kustomize prod会发现生成的ServiceDeploymentname分别加上了devprod

使用Kustomize应用、查看和删除对象

kubectl 命令中使用 --kustomize-k 参数来识别被 kustomization.yaml 所管理的资源。 注意 -k 要指向一个 kustomization 目录。例如:

kubectl apply -k <kustomization 目录>/

假定使用下面的kustomization.yaml:

# 创建 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
        ports:
        - containerPort: 80
EOF

# 创建 kustomization.yaml
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
  app: my-nginx
resources:
- deployment.yaml
EOF

执行下面的命令来应用Deployment对象dev-my-nginx:

kubectl apply -k ./

输出类似于:

deployment.apps/dev-my-nginx created

运行下面命令之一来查看Deployment对象dev-my-nginx:

kubectl get -k ./

kubectl describe -k ./

执行下面命令来比较Deployment对象dev-my-nginx与清单被应用之后集群将处于的状态:

kubectl diff -k ./

执行下面命令删除Deployment对象dev-my-nginx:

kubectl delete -k ./

kustomize 功能特性列表

字段类型解释
namespacestring为所有资源添加命名空间
namePrefixstring此字段的值将被添加到素有资源名称前面
nameSuffixstring此字段的值将被添加到所有资源名称后面
commonLabelsmap[string]string要添加到所有资源和选择算符的标签
commonAnnotationsmap[string]string要添加到所有资源的注释
resources[]string列表中的每个条目都必须能够解析成现有的资源配置文件
configMapGenerator[]ConfigMapArgs列表中的每个条目都会生成一个 ConfigMap
secretGenerator[]SecretArgs列表中的每个条目都会生成一个Secret
generatorOptionsGeneratorOptions更改所有 ConfigMap 和 Secret 生成器的行为
bases[]string列表中每个条目都应能解析为一个包含 kustomization.yaml 文件的目录
patchesStrategicMerge[]string列表中每个条目都能解析为某 Kubernetes 对象的策略性合并补丁
patchesJson6902[]Path列表中每个条目都能解析为一个 Kubernetes 对象和一个 JSON 补丁
vars[]Var每个条目用来从某资源的字段来析取文字
images[]Image每个条目都用来更改镜像的名称、标记与/或摘要,不必生成补丁
configurations[]string列表中每个条目都应能解析为一个包含 Kustomize 转换器配置 的文件
crds[]string列表中每个条目都应能够解析为 Kubernetes 类别的 OpenAPI 定义文件

原文链接

kubernetes.io/zh-cn/docs/…