ConfigMap 是 Kubernetes 中的一个 API 对象,用于保存应用程序所需的非敏感性配置数据,如字符串、文件和命令行参数等。使用 ConfigMap 可以将这些配置数据与容器镜像分离,方便应用程序的可移植性和部署管理。
Pod 可以通过多种方式使用 ConfigMap 中保存的配置数据,例如将其作为环境变量注入到容器中、将其作为命令行参数传递给容器、或者将其作为配置文件挂载到容器中。这样就可以使应用程序在不同环境中更加灵活地运行,并且可以避免因为配置问题而导致的应用程序出错。
ConfigMap 的一个主要作用就是将应用程序配置信息与容器镜像解耦,当需要修改应用程序的配置信息时,可以不必重新构建和部署容器镜像,而只需要修改对应的 ConfigMap 即可。
注意:ConfigMap 并不提供保密或者加密功能,如果想存储机密数据,请使用 Secret, 或者使用其他第三方工具来保证数据的私密性。
一、动机
使用 ConfigMap 可以实现将配置数据和应用程序代码分开的需求。
比如想开发一款应用程序,该应用程序能够在本地和云环境中运行,并且需要根据执行环境设置 DATABASE_HOST 环境变量以访问数据库。为了实现这个目标,可以使用 Kubernetes 的 ConfigMap 功能来管理应用程序的配置信息。
首先,可以创建一个名为 database-config 的 ConfigMap,将本地开发环境的 DATABASE_HOST 设置为 localhost:
apiVersion: v1 kind: ConfigMap metadata: name: database-config data: DATABASE_HOST: localhost
然后,在云环境中,可以创建另一个 ConfigMap,将 DATABASE_HOST 设置为引用 Kubernetes 集群中公开数据库组件的服务:
apiVersion: v1 kind: ConfigMap metadata: name: database-config data: DATABASE_HOST: <service-name>
在部署应用程序时,可以通过挂载 ConfigMap 到 Pod 中的环境变量来使用这些配置。可以在 Pod 的配置文件中定义一个环境变量,例如:
spec: containers: - name: my-app image: my-app-image envFrom: - configMapRef: name: database-config
这样,当应用程序运行时,它将根据所在环境从 ConfigMap 中获取 DATABASE_HOST 的值。在本地开发环境中,它将获取到 localhost,而在云环境中,它将获取到集群中公开数据库组件的服务地址。
通过这种方式,可以更好地管理应用程序的配置数据,使代码在不同环境中运行时能够根据需要获取正确的配置值。同时,通过将配置信息和应用程序代码分离,可以轻松地在本地和云环境中进行开发和调试,并且无需修改代码即可切换配置。这样就可以获取在云中运行的容器镜像,并且如果有需要的话,在本地调试完全相同的代码。
注意:ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。如果需要保存超出此尺寸限制的数据,可能希望考虑挂载存储卷 或者使用独立的数据库或者文件服务。
二、ConfigMap对象
ConfigMap 是 Kubernetes 提供的一种用于存储配置数据的 API 对象。与其他 Kubernetes 对象类似,ConfigMap 也有一个 spec 字段,但与其他对象不同的是,它包含了两个字段:data 和 binaryData。
data 字段用于存储以键值对形式表示的配置数据,这些数据必须是 UTF-8 编码的字符串。您可以在其中定义多个键值对,每个键值对表示一个配置项;而binaryData 字段用于存储以键值对形式表示的二进制配置数据,这些数据被编码为 base64 字符串。
data 或 binaryData 字段下面的每个键的名称都必须由字母数字字符或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。
注意:
- ConfigMap 的名字必须是一个合法的 DNS 子域名。
- 从 v1.19 开始,可以添加一个 immutable 字段到 ConfigMap 定义中, 创建不可变更的 ConfigMap。
三、ConfigMaps和Pods
当创建一个 Pod,可以使用 env 字段来引用 ConfigMap 中的数据,并将其设置为容器的环境变量。通过这种方式可以在 Pod 中使用 ConfigMap 中的配置数据来配置容器。
注意: 静态 Pod 中的 spec 字段不能引用 ConfigMap 或任何其他 API 对象。
这是一个 ConfigMap 的示例,它的一些键只有一个值,其他键的值看起来像是配置的片段格式:
apiVersion: v1 kind: ConfigMap metadata: name: game-demo data: # 类属性键;每一个键都映射到一个简单的值 player_initial_lives: "3" ui_properties_file_name: "user-interface.properties" # 类文件键 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 使用 ConfigMap 中的数据在 Pod 中启动容器。第四种方法必须编写代码才能读取 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: # 定义环境变量 - name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的 valueFrom: configMapKeyRef: name: game-demo # 这个值来自 ConfigMap key: player_initial_lives # 需要取值的键 - name: UI_PROPERTIES_FILE_NAME valueFrom: configMapKeyRef: name: game-demo key: ui_properties_file_name volumeMounts: - name: config mountPath: "/config" readOnly: true volumes: # 可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中 - name: config configMap: # 提供想要挂载的 ConfigMap 的名字 name: game-demo # 来自 ConfigMap 的一组键,将被创建为文件 items: - key: "game.properties" path: "game.properties" - key: "user-interface.properties" path: "user-interface.properties"
ConfigMap 不会区分单行属性值和多行类似文件的值,重要的是 Pods 和其他对象如何使用这些值。
上面的例子定义了一个卷并将它作为 /config 文件夹挂载到 demo 容器内, 创建两个文件,/config/game.properties 和 /config/user-interface.properties, 尽管 ConfigMap 中包含了四个键。 这是因为 Pod 定义中在 volumes 节指定了一个 items 数组。 如果完全忽略 items 数组,则 ConfigMap 中的每个键都会变成一个与该键同名的文件, 因此会得到四个文件。
四、使用ConfigMap
ConfigMap 可以作为数据卷挂载,也可被系统的其他组件使用, 而不一定直接暴露给 Pod。例如,ConfigMap 可以保存系统中其他组件要使用的配置数据。
ConfigMap 最常见的用法是为同一命名空间里某 Pod 中运行的容器执行配置。 也可以单独使用 ConfigMap。例如可能会遇到基于 ConfigMap 来调整其行为的 插件 或者 operator。
1、在 Pod 中将 ConfigMap 当做文件使用
需要在一个 Pod 的存储卷中使用 ConfigMap:
- 创建一个 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 块。
2、被挂载的 ConfigMap 内容会被自动更新
当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。 kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。 不过,kubelet 使用的是其本地的高速缓存来获得 ConfigMap 的当前值, 高速缓存的类型可以通过 KubeletConfiguration 结构. 的 configMapAndSecretChangeDetectionStrategy 字段来配置。
ConfigMap 既可以通过 watch 操作实现内容传播(默认形式),也可实现基于 TTL 的缓存,还可以直接经过所有请求重定向到 API 服务器。 因此,从 ConfigMap 被更新的那一刻算起,到新的主键被投射到 Pod 中去, 这一时间跨度可能与 kubelet 的同步周期加上高速缓存的传播延迟相等。 这里的传播延迟取决于所选的高速缓存类型 (分别对应 watch 操作的传播延迟、高速缓存的 TTL 时长或者 0)。
注意:
- 以环境变量方式使用的 ConfigMap 数据不会被自动更新。 更新这些数据需要重新启动 Pod。
- 使用 ConfigMap 作为 subPath 卷挂载的容器将不会收到 ConfigMap 的更新。
五、不可变更的ConfigMap
Kubernetes 的 Immutable Secrets 和 ConfigMaps 特性确实提供了一种将这些对象设置为不可变更的选项。对于大量使用 ConfigMap 的集群并且希望防止更改 ConfigMap 数据的情况,禁止更改 ConfigMap 的数据可以带来以下好处:
1、保护应用程序:通过将 ConfigMap 设置为不可变更,可以确保应用程序的配置数据不会意外被修改。这可以避免由于错误的配置更改导致的不可预期的问题和潜在的安全风险。当您确定 ConfigMap 中的配置是稳定和正确的时候,禁止更改可以帮助保护应用程序免受意外更新的负面影响。
2、改善集群性能:ConfigMap 是 Kubernetes 中的一种资源对象,监视 ConfigMap 变化需要 kube-apiserver 不断地查询和处理请求。在大规模集群中,这可能导致 kube-apiserver 承受过多的负载压力。通过禁止更改已标记为不可变更的 ConfigMap,可以减少对 kube-apiserver 的监视操作,从而改善集群的性能。
可以通过将 immutable 字段设置为 true 创建不可变更的 ConfigMap。 例如:
apiVersion: v1 kind: ConfigMap metadata: ... data: ... immutable: true
一旦某 ConfigMap 被标记为不可变更,则 无法 逆转这一变化,,也无法更改 data 或 binaryData 字段的内容。只能删除并重建 ConfigMap。 因为现有的 Pod 会维护一个已被删除的 ConfigMap 的挂载点,建议重新创建这些 Pods。