ConfigMap
ConfigMap 是一种 API 对象,用于以键值对的形式存储非机密数据。Pod 可以将 ConfigMap 用作环境变量、命令行参数,或作为配置文以卷的形式挂载。
ConfigMap 允许你将环境特定的配置与你的容器镜像解耦,从而使你的应用更易于移植。
注意
ConfigMap 不提供机密性或加密。如果你想要存储的数据是机密的,应使用Secret而非 ConfigMap,或者使用额外的(第三方)工具来保护你的数据隐私。动机
使用 ConfigMap 将配置数据与应用代码分离。
例如,假设你正在开发一个应用,你可以在自己的计算机上运行它(用于开发),也可以在云中运行它(处理实际流量)。你编写代码使其查找名为 DATABASE_HOST
的环境变量。在本地,你将该变量设置为 localhost
。在云中,你将其设置为引用 Kubernetes 的Service,该 Service 将数据库组件暴露给你的集群。这使得你可以拉取在云中运行的容器镜像,并在需要时在本地调试完全相同的代码。
注意
ConfigMap 不适合存放大量数据块。ConfigMap 中存储的数据不能超过 1 MiB。如果你需要存储超过此限制的设置,你可能需要考虑挂载一个卷或者使用单独的数据库或文件服务。ConfigMap 对象
ConfigMap 是一个API 对象,它允许你存储供其他对象使用的配置。与大多数具有 spec
的 Kubernetes 对象不同,ConfigMap 具有 data
和 binaryData
字段。这些字段接受键值对作为其值。data
字段和 binaryData
字段都是可选的。data
字段用于存放 UTF-8 字符串,而 binaryData
字段用于存放 base64 编码的二进制数据。
ConfigMap 的名称必须是有效的DNS 子域名。
data
或 binaryData
字段下的每个键必须由字母数字字符、-
、_
或 .
组成。存储在 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 中的容器。
- 在容器命令和参数中
- 作为容器的环境变量
- 在只读卷中添加文件,供应用读取
- 编写运行在 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
- 创建一个 ConfigMap 或使用现有的。多个 Pod 可以引用同一个 ConfigMap。
- 修改你的 Pod 定义,在
.spec.volumes[]
下添加一个卷。为该卷指定任意名称,并将.spec.volumes[].configMap.name
字段设置为引用你的 ConfigMap 对象。 - 为需要 ConfigMap 的每个容器添加
.spec.containers[].volumeMounts[]
。指定.spec.containers[].volumeMounts[].readOnly = true
,并将.spec.containers[].volumeMounts[].mountPath
指定为一个你希望 ConfigMap 出现的未使用的目录名。 - 修改你的镜像或命令行,以便程序在该目录中查找文件。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 作为subPath 卷挂载的容器不会接收 ConfigMap 更新。将 ConfigMap 作为环境变量使用
在 Pod 中将 ConfigMap 用作环境变量。
- 对于 Pod 规约中的每个容器,将你想使用的每个 ConfigMap 键作为环境变量添加到
env[].valueFrom.configMapKeyRef
字段中。 - 修改你的镜像和/或命令行,以便程序在指定的环墰变量中查找值。
这是一个将 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 被标记为不可变,就无法撤销此更改,也无法更改 data
或 binaryData
字段的内容。你只能删除并重新创建 ConfigMap。由于现有 Pod 维护着对已删除 ConfigMap 的挂载点,建议重新创建这些 Pod。
下一步
- 阅读关于Secret 的内容。
- 阅读配置 Pod 使用 ConfigMap。
- 阅读关于更改 ConfigMap(或任何其他 Kubernetes 对象) 的内容。
- 阅读《云原生应用十二要素》,以理解将代码与配置分离的动机。