ConfigMap

ConfigMap 是一种 API 对象,用于以键值对的形式存储非机密数据。Pod 可以将 ConfigMap 用作环境变量、命令行参数,或作为配置文以的形式挂载。

ConfigMap 允许你将环境特定的配置与你的容器镜像解耦,从而使你的应用更易于移植。

动机

使用 ConfigMap 将配置数据与应用代码分离。

例如,假设你正在开发一个应用,你可以在自己的计算机上运行它(用于开发),也可以在云中运行它(处理实际流量)。你编写代码使其查找名为 DATABASE_HOST 的环境变量。在本地,你将该变量设置为 localhost。在云中,你将其设置为引用 Kubernetes 的Service,该 Service 将数据库组件暴露给你的集群。这使得你可以拉取在云中运行的容器镜像,并在需要时在本地调试完全相同的代码。

ConfigMap 对象

ConfigMap 是一个API 对象,它允许你存储供其他对象使用的配置。与大多数具有 spec 的 Kubernetes 对象不同,ConfigMap 具有 databinaryData 字段。这些字段接受键值对作为其值。data 字段和 binaryData 字段都是可选的。data 字段用于存放 UTF-8 字符串,而 binaryData 字段用于存放 base64 编码的二进制数据。

ConfigMap 的名称必须是有效的DNS 子域名

databinaryData 字段下的每个键必须由字母数字字符、-_. 组成。存储在 data 中的键不能与 binaryData 字段中的键重叠。

从 v1.19 版本开始,你可以将 immutable 字段添加到 ConfigMap 定义中,以创建一个不可变 ConfigMap

ConfigMap 和 Pod

你可以编写引用 ConfigMap 的 Pod spec,并根据 ConfigMap 中的数据配置该 Pod 中的容器。Pod 和 ConfigMap 必须位于同一个命名空间中。

这里有一个示例 ConfigMap,它包含一些具有单个值的键,以及其他值看起来像配置格式片段的键。

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # property-like keys; each key maps to a simple value
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # file-like keys
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true    

有四种不同的方式可以使用 ConfigMap 来配置 Pod 中的容器。

  1. 在容器命令和参数中
  2. 作为容器的环境变量
  3. 在只读卷中添加文件,供应用读取
  4. 编写运行在 Pod 中的代码,使用 Kubernetes API 读取 ConfigMap

这些不同的方法适用于不同方式来建模正在被消费的数据。对于前三种方法,kubelet 在启动 Pod 的容器时会使用 ConfigMap 中的数据。

第四种方法意味着你必须编写代码来读取 ConfigMap 及其数据。然而,由于你直接使用 Kubernetes API,你的应用可以订阅更新,以便在 ConfigMap 发生变化时获得通知,并在变化发生时做出反应。通过直接访问 Kubernetes API,这种技术还允许你访问不同命名空间中的 ConfigMap。

这里有一个示例 Pod,它使用来自 game-demo 的值来配置一个 Pod。

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # Define the environment variable
        - name: PLAYER_INITIAL_LIVES # Notice that the case is different here
                                     # from the key name in the ConfigMap.
          valueFrom:
            configMapKeyRef:
              name: game-demo           # The ConfigMap this value comes from.
              key: player_initial_lives # The key to fetch.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
  # You set volumes at the Pod level, then mount them into containers inside that Pod
  - name: config
    configMap:
      # Provide the name of the ConfigMap you want to mount.
      name: game-demo
      # An array of keys from the ConfigMap to create as files
      items:
      - key: "game.properties"
        path: "game.properties"
      - key: "user-interface.properties"
        path: "user-interface.properties"
        

ConfigMap 不区分单行属性值和多行文件式的值。重要的是 Pod 和其他对象如何消费这些值。

对于本例,定义一个卷并将其挂载到 demo 容器内作为 /config 会创建两个文件 /config/game.properties/config/user-interface.properties,尽管 ConfigMap 中有四个键。这是因为 Pod 定义在 volumes 部分指定了 items 数组。如果你完全省略 items 数组,ConfigMap 中的每个键都会成为一个与键同名的文件,并且你会得到 4 个文件。

使用 ConfigMap

ConfigMap 可以作为数据卷挂载。ConfigMap 也可以被系统的其他部分使用,而无需直接暴露给 Pod。例如,ConfigMap 可以存放系统其他部分用于配置的数据。

使用 ConfigMap 最常见的方式是配置运行在同一命名空间中 Pod 内容器的设置。你也可以单独使用 ConfigMap。

例如,你可能会遇到插件Operator,它们会根据 ConfigMap 调整其行为。

将 ConfigMap 作为文件从 Pod 中使用

在 Pod 的卷中消费 ConfigMap

  1. 创建一个 ConfigMap 或使用现有的。多个 Pod 可以引用同一个 ConfigMap。
  2. 修改你的 Pod 定义,在 .spec.volumes[] 下添加一个卷。为该卷指定任意名称,并将 .spec.volumes[].configMap.name 字段设置为引用你的 ConfigMap 对象。
  3. 为需要 ConfigMap 的每个容器添加 .spec.containers[].volumeMounts[]。指定 .spec.containers[].volumeMounts[].readOnly = true,并将 .spec.containers[].volumeMounts[].mountPath 指定为一个你希望 ConfigMap 出现的未使用的目录名。
  4. 修改你的镜像或命令行,以便程序在该目录中查找文件。ConfigMap data 映射中的每个键都会成为 mountPath 下的文件名。

这是一个将 ConfigMap 挂载到卷中的 Pod 示例。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configMap:
      name: myconfigmap

你想使用的每个 ConfigMap 都需要在 .spec.volumes 中引用。

如果 Pod 中有多个容器,那么每个容器都需要自己的 volumeMounts 块,但每个 ConfigMap 只需要一个 .spec.volumes

挂载的 ConfigMap 会自动更新

当当前在卷中使用的 ConfigMap 被更新时,投影的键最终也会被更新。kubelet 在每次周期性同步时都会检查挂载的 ConfigMap 是否是最新的。然而,kubelet 使用其本地缓存来获取 ConfigMap 的当前值。可以使用 KubeletConfiguration 结构体 中的 configMapAndSecretChangeDetectionStrategy 字段来配置缓存类型。ConfigMap 可以通过 watch(默认)、基于 ttl 或通过将所有请求直接重定向到 API 服务器来传播。因此,从 ConfigMap 更新到新键投影到 Pod 的总延迟可能长达 kubelet 同步周期 + 缓存传播延迟,其中缓存传播延迟取决于所选择的缓存类型(分别等于 watch 传播延迟、缓存的 ttl 或零)。

作为环境变量使用的 ConfigMap 不会自动更新,并且需要 Pod 重启。

将 ConfigMap 作为环境变量使用

在 Pod 中将 ConfigMap 用作环境变量

  1. 对于 Pod 规约中的每个容器,将你想使用的每个 ConfigMap 键作为环境变量添加到 env[].valueFrom.configMapKeyRef 字段中。
  2. 修改你的镜像和/或命令行,以便程序在指定的环墰变量中查找值。

这是一个将 ConfigMap 定义为 Pod 环境变量的示例。

以下 ConfigMap (myconfigmap.yaml) 存储了两个属性:username 和 access_level

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  username: k8s-admin
  access_level: "1"

以下命令将创建 ConfigMap 对象

kubectl apply -f myconfigmap.yaml

以下 Pod 将 ConfigMap 的内容作为环境变量使用

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
    - name: app
      command: ["/bin/sh", "-c", "printenv"]
      image: busybox:latest
      envFrom:
        - configMapRef:
            name: myconfigmap

envFrom 字段指示 Kubernetes 从其中嵌套的源创建环境变量。内层 configMapRef 通过名称引用一个 ConfigMap 并选择其所有的键值对。将 Pod 添加到你的集群中,然后获取其日志以查看 printenv 命令的输出。这应该会确认 ConfigMap 中的两个键值对已被设置为环境变量。

kubectl apply -f env-configmap.yaml
kubectl logs pod/ env-configmap

输出类似于这样:

...
username: "k8s-admin"
access_level: "1"
...

有时 Pod 不需要访问 ConfigMap 中的所有值。例如,你可以有另一个只使用 ConfigMap 中 username 值的 Pod。对于这种情况,你可以使用 env.valueFrom 语法代替,它允许你选择 ConfigMap 中的单个键。环境变量的名称也可以与 ConfigMap 中的键不同。例如:

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: CONFIGMAP_USERNAME
      valueFrom:
        configMapKeyRef:
          name: myconfigmap
          key: username

在从此清单创建的 Pod 中,你将看到环境变量 CONFIGMAP_USERNAME 被设置为 ConfigMap 中 username 的值。ConfigMap 数据中的其他键不会被复制到环境中。

需要注意的是,Pod 中允许用于环境变量名称的字符范围是受限的。如果任何键不符合规则,这些键将不会提供给你的容器,尽管 Pod 仍然允许启动。

不可变 ConfigMap

特性状态: Kubernetes v1.21 [stable]

Kubernetes 特性 Immutable Secrets and ConfigMaps 提供了一个选项,可以将单个 Secret 和 ConfigMap 设置为不可变。对于广泛使用 ConfigMap 的集群(至少有数万个独立的 ConfigMap 到 Pod 挂载),阻止对其数据进行更改具有以下优点:

  • 保护你免受可能导致应用中断的意外(或不希望的)更新
  • 通过关闭对标记为不可变的 ConfigMap 的 watch,显著减少 kube-apiserver 上的负载,从而提高集群性能。

你可以通过将 immutable 字段设置为 true 来创建一个不可变 ConfigMap。例如:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

一旦 ConfigMap 被标记为不可变,就无法撤销此更改,也无法更改 databinaryData 字段的内容。你只能删除并重新创建 ConfigMap。由于现有 Pod 维护着对已删除 ConfigMap 的挂载点,建议重新创建这些 Pod。

下一步

最后修改于 2024 年 9 月 11 日 6:37 PM PST: 在文档中添加 envFrom 详情 (#47709) (2ccaf064f8)