K8s新手系列之ConfigMap资源

概述

在 Kubernetes(K8s)中,ConfigMap 是一种 API 对象,用于将非机密性的数据保存到键值对中。Pod 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

ConfigMap 允许你将配置清单与镜像内容分离,以保持容器化的应用程序的可移植性。 例如,你可以下载并运行相同的容器镜像来启动容器, 用于本地开发、系统测试或运行实时终端用户工作负载。

ConfigMap 是一个让你可以存储其他对象所需要使用的配置的 API 对象。 和其他 Kubernetes 对象都有一个 spec 不同的是,ConfigMap 使用 data 和 binaryData 字段。这些字段能够接收键-值对作为其取值。data 和 binaryData 字段都是可选的。data 字段设计用来保存 UTF-8 字符串,而 binaryData 则被设计用来保存二进制数据作为 base64 编码的字串。

官方文档地址:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-pod-configmap/

ConfigMap的作用

集中存储配置信息:

  • ConfigMap可以将应用程序的配置信息(如数据库连接字符串、日志级别、服务端口等)集中存储在一个地方。这些配置信息以键值对的形式存在,方便管理和维护。例如,一个应用程序需要连接到数据库,其数据库的用户名、密码、地址等信息可以存储在ConfigMap中。

  • 通过ConfigMap,开发者可以将配置信息与应用程序代码分离,使得应用程序更加通用和可移植。当需要在不同环境中运行应用程序时(如开发环境、测试环境和生产环境),只需修改ConfigMap中的配置值,而无需修改应用程序代码。

    支持多种数据格式

  • ConfigMap支持多种数据格式,包括字符串、文件内容等。可以将配置文件(如properties文件、yaml文件等)的内容直接存储到ConfigMap中。例如,一个应用程序的配置文件config.properties中包含多个配置项,可以将其内容读取后存储到ConfigMap中,然后应用程序通过ConfigMap来读取这些配置项。

注入配置

ConfigMap可以将配置信息注入到容器中,容器可以通过环境变量、挂载卷等方式访问这些配置信息。

  • 环境变量方式:可以将ConfigMap中的配置项作为环境变量注入到容器中。例如,一个容器需要访问数据库,其数据库的用户名和密码可以通过ConfigMap以环境变量的形式注入到容器中,容器在运行时可以直接使用这些环境变量来获取配置信息。

  • 挂载卷方式:可以将ConfigMap中的配置文件挂载到容器的文件系统中。例如,一个应用程序需要读取一个配置文件config.yaml,可以将存储该配置文件内容的ConfigMap挂载到容器的某个目录下,容器在运行时可以通过文件路径来访问该配置文件。

动态更新配置

当ConfigMap中的配置信息发生变化时,容器可以感知到这些变化,并根据需要重新加载配置信息。这种动态更新配置的能力使得应用程序可以在不重启容器的情况下,快速响应配置的变化。例如,当数据库的连接地址发生变化时,更新ConfigMap中的相应配置项,容器可以自动获取新的配置并重新建立数据库连接。

提高代码的可维护性

将配置信息存储在ConfigMap中,使得应用程序代码更加简洁和清晰。开发者不需要在代码中硬编码配置信息,减少了代码的复杂性和耦合度。当需要修改配置时,只需修改ConfigMap,而无需修改代码,降低了维护成本。

增强代码的可移植性

由于配置信息与代码分离,应用程序可以在不同的环境中运行,而无需修改代码。通过调整ConfigMap中的配置值,可以轻松地将应用程序从开发环境迁移到测试环境或生产环境,提高了应用程序的可移植性。

多容器共享配置

在 Kubernetes 中,一个ConfigMap可以被多个容器共享。当多个容器需要使用相同的配置信息时,可以使用同一个ConfigMap,避免了重复配置的问题。例如,在一个微服务架构中,多个服务实例可能需要访问同一个数据库,可以将数据库的配置信息存储在一个ConfigMap中,所有服务实例都可以通过该ConfigMap来获取配置信息。

多应用共享配置

不同的应用程序也可以共享同一个ConfigMap。例如,多个应用程序可能需要访问同一个外部服务,可以将该外部服务的配置信息存储在一个ConfigMap中,所有应用程序都可以通过该ConfigMap来获取配置信息。

简化部署流程

在部署应用程序时,通过ConfigMap可以快速地将配置信息传递给容器。开发者只需定义好ConfigMap,然后在部署时将ConfigMap与容器关联起来,即可完成配置的注入,简化了部署流程。

方便运维管理

运维人员可以通过ConfigMap集中管理配置信息,方便对配置进行修改、备份和恢复。当出现问题时,可以通过查看ConfigMap中的配置信息来快速定位问题原因,提高了运维效率。

创建ConfigMap

通过文件来创建ComfigMap

语法:

复制代码
kubectl create configmap [config的名称] --from-file=[file] ... --from-file=[file]

# 或者通过
kubectl create configmap [config的名称] --from-env-file=[file] ... --from-env-file=[file]

解析:
--from-env-file:可以通过env的方式来创建ConfigMap,例如name=zhangsan,使用--from-file创建时,其配置项不会变化。
但是通过--from-env-file创建时,会自动解析成name: zhangsan的形式

哪些文件可以用来创建ConfigMap呢?

  • .properties文件:这是 Java 应用常用的配置文件类型,采用键值对的格式存储配置信息,例如:

    database.host=localhost
    database.port=3306

  • .yaml/.yml文件:是一种可读性高的数据序列化格式,在 Kubernetes 自身配置和许多应用配置中广泛使用,例如:

    database:
    host: localhost
    port: 3306
    server:
    port: 8080

  • .ini 文件:常用于存储配置数据,其结构包含多个节(section),每个节下面有多个键值对,示例如下:

    [database]
    host=localhost
    port=3306

    [server]
    port=8080

  • .json 文件:一种轻量级的数据交换格式,常用于存储结构化数据,示例如下:

    {
    "database": {
    "host": "localhost",
    "port": 3306
    },
    "server": {
    "port": 8080
    }
    }

  • .conf 文件:通常是各种服务的配置文件,像 Nginx、Apache 等服务的配置文件就常以 .conf 为扩展名。

    worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
    http {
    server {
    }
    }

  • .txt文件:普通文本文件,可存储任意文本信息,例如存储日志级别、应用描述等内容。

    asdsxfaseqwesd

通过properties文件创建ConfigMap

示例:

复制代码
# 创建properties文件
[root@master01 ~/configmap]# cat db.properties
database.url=jdbc:mysql://localhost:3306/mydb
database.username=root
database.password=password

# 创建ComfigMap
[root@master01 ~/configmap]# kubectl create configmap db-config --from-file=db.properties
configmap/db-config created

# 查看ConfigMap
[root@master01 ~/configmap]# kubectl get configmap
NAME               DATA   AGE
db-config          1      53s

# 查看ConfigMap生成的yaml文件
[root@master01 ~/configmap]# kubectl get configmap db-config -o yaml
apiVersion: v1
metadata:
  creationTimestamp: "2025-05-02T06:02:42Z"
  name: db-config
  namespace: default
  resourceVersion: "59760"
  uid: dc9de3eb-5e19-47fd-854d-2c4fcddbf8ba
data:
  db.properties: |
    database.url=jdbc:mysql://localhost:3306/mydb
    database.username=root
    database.password=password
kind: ConfigMap

# 查看ConfigMap的详细信息
[root@master01 ~/configmap]# kubectl describe configmap db-config
Name:         db-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data #这里就是对应的数据信息
====
db.properties:
----
database.url=jdbc:mysql://localhost:3306/mydb
database.username=root
database.password=password


BinaryData #这里是二进制数据
====

Events:  <none>

通过目录创建ConfigMap

语法:

复制代码
kubectl create configmap [config的名称] --from-file=[filepath] ... --from-file=[filepath]

# 或者通过
kubectl create configmap [config的名称] --from-env-file=[filepath] ... --from-env-file=[filepath]

解析:
--from-env-file:可以通过env的方式来创建ConfigMap,例如name=zhangsan,使用--from-file创建时,其配置项不会变化。
但是通过--from-env-file创建时,会自动解析成name: zhangsan的形式

这种方式其实和通过文件来创建ConfigMap是一样的原理。

示例:

复制代码
# 创建目录
[root@master01 ~]# mkdir -p /root/configmap/app

# 造数据
[root@master01 ~]# wget https://kubernetes.io/examples/configmap/game.properties -O /root/configmap/app/game.properties
#...省略部分信息
/root/configmap/app/game.properties                 100%[==================================================================================================================>]     157  --.-KB/s    in 0s

2025-05-02 14:09:46 (178 MB/s) - '/root/configmap/app/game.properties' saved [157/157]
[root@master01 ~]# wget https://kubernetes.io/examples/configmap/ui.properties -O /root/configmap/app/ui.properties
#...省略部分信息
/root/configmap/app/ui.properties                   100%[==================================================================================================================>]      83  --.-KB/s    in 0s

2025-05-02 14:10:03 (156 MB/s) - '/root/configmap/app/ui.properties' saved [83/83]

# 创建ConfigMap
[root@master01 ~]# kubectl create configmap app-config  --from-file=/root/configmap/app
configmap/app-config created

# 查看ConfigMap,两个文件变成了一个configMap
[root@master01 ~]# kubectl get configmap app-config -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: "2025-05-02T06:13:01Z"
  name: app-config
  namespace: default
  resourceVersion: "61185"
  uid: 2cce2598-db6f-41a2-89fe-f144fac4159c
data:
  game.properties: |-
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice

通过字面量创建ConfigMap

ConfigMap还支持直接输入字面量来创建

语法:

复制代码
kubectl create configmap [configName] [--from-literal="key=value"] ... [--from-literal="key=value"]

示例:

复制代码
# 创建ConfigMap
[root@master01 ~]# kubectl create configmap user-config --from-literal="user.name=zhangsan" \
                                      --from-literal="user.age=18" \
                                      --from-literal="user.addr=beijing"
configmap/user-config created

# 查看ConfigMap的yaml文件
[root@master01 ~]# kubectl get configmap user-config -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: "2025-05-02T06:35:22Z"
  name: user-config
  namespace: default
  resourceVersion: "64263"
  uid: 9ffe4dc3-31ee-4cde-bbfe-24041aa824bd
data:
  user.addr: beijing
  user.age: "18"
  user.name: zhangsan

# 查看详细信息
[root@master01 ~]# kubectl describe configmap user-config
Name:         user-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
user.age:
----
18
user.name:
----
zhangsan
user.addr:
----
beijing

BinaryData
====

Events:  <none>

通过资源清单创建ConfigMap

ConfigMap也可以通过K8s的Yaml文件进行创建,通过资源清单创建ConfigMap有两种方式,一种是类属性键,一种是类文件键

示例:

复制代码
# 定义资源清单
[root@master01 ~/configmap]# cat game-configmap.yaml
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
  nginx.conf: |
        server {
            listen:80;
            server_name:xxx;
            root:/root;
            location:{
                xxx
            }
        }

# 创建configMap
[root@master01 ~/configmap]# kubectl apply -f game-configmap.yaml
configmap/game-demo created

# 查看yaml文件
[root@master01 ~/configmap]# kubectl get configmap game-demo -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"game.properties":"enemy.types=aliens,monsters\nplayer.maximum-lives=5\n","nginx.conf":"server {\n    listen:80;\n    server_name:xxx;\n    root:/root;\n    location:{\n        xxx\n    }\n}\n","player_initial_lives":"3","ui_properties_file_name":"user-interface.properties"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"game-demo","namespace":"default"}}
  creationTimestamp: "2025-05-02T06:48:39Z"
  name: game-demo
  namespace: default
  resourceVersion: "66094"
  uid: d2f71bfb-5a8d-4cd4-b9fc-b490aef77f67
data:
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
  nginx.conf: |
    server {
        listen:80;
        server_name:xxx;
        root:/root;
        location:{
            xxx
        }
    }
  player_initial_lives: "3"
  ui_properties_file_name: user-interface.properties

# 查看详细信息
[root@master01 ~/configmap]# kubectl describe cm game-demo
Name:         game-demo
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
nginx.conf:
----
server {
    listen:80;
    server_name:xxx;
    root:/root;
    location:{
        xxx
    }
}

player_initial_lives:
----
3
ui_properties_file_name:
----
user-interface.properties
game.properties:
----
enemy.types=aliens,monsters
player.maximum-lives=5


BinaryData
====

Events:  <none>

修改ConfigMap的值

ConfigMap无法修改名称,但是可以修改其值

通过kubectl方式修改

可以通过kubectl edit configmap configmapname来进行修改其值,使用kubectl命令之后,会打开一个类似vim的编辑界面,修改之后使用:wq保存之后即可

通过文件修改

修改了资源文件之后,configmap相同的key会进行覆盖,不同的key会进行添加,并不会进行删除多余的key

查看ConfigMap

复制代码
# 查看所有的ConfigMap
[root@master01 ~]# kubectl get configmap -A
NAMESPACE          NAME                                 DATA   AGE
calico-apiserver   kube-root-ca.crt                     1      6d6h
calico-system      active-operator                      1      6d6h
calico-system      cni-config                           1      6d6h
calico-system      kube-root-ca.crt                     1      6d6h
calico-system      tigera-ca-bundle                     2      6d6h
default            app-config                           2      4h39m
default            db-config                            1      6h13m
default            env-config                           3      5h17m

# 查看指定命名空间下的ConfigMap
[root@master01 ~]# kubectl get configmap -n calico-system
NAME               DATA   AGE
active-operator    1      6d6h
cni-config         1      6d6h
kube-root-ca.crt   1      6d6h
tigera-ca-bundle   2      6d6h

# 以yaml文件查看
[root@master01 ~]# kubectl get configmap db-config -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: "2025-05-02T06:02:42Z"
  name: db-config
  namespace: default
  resourceVersion: "59760"
  uid: dc9de3eb-5e19-47fd-854d-2c4fcddbf8ba
data:
  db.properties: |
    database.url=jdbc:mysql://localhost:3306/mydb
    database.username=root
    database.password=password

删除ConfigMap

使用kubectl delete configmap [name1 name2 ... nameN]的方式删除即可

示例:

复制代码
[root@master01 ~]# kubectl delete configmap db-config
configmap "db-config" deleted
# 验证是否删除成功
[root@master01 ~]# kubectl get configmap db-config
Error from server (NotFound): configmaps "db-config" not found

配置Pod使用ConfigMap

Pod使用ConfigMap资源,有两种方式,一种是通过环境变量的方式使用,另一种方式通过数据卷挂载的方式使用

注意:

  • 静态Pod不能使用ConfigMap
  • Pod 和 ConfigMap 必须要在同一个namespace中。

Pod通过环境变量使用ConfigMap

示例:

先创建ConfigMap

复制代码
# 创建ConfigMap
[root@master01 ~]# echo 'apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
data:
  USER_NAME: huangsir
  APP: haha
  COLOR: red
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  USER_AGE: "18"
  USER_NAME: zhangsan
  USER_ADDR: beijing' | kubectl apply -f -

将 ConfigMap 的内容用作Pod的环境变量:

复制代码
# 创建Pod
[root@master01 ~/configmap]# echo 'apiVersion: v1
kind: Pod
metadata:
  name: env-configmap-pod
spec:
  containers:
    - name: app
      # 打印环境变量
      command: ["/bin/sh", "-c", "printenv"]
      image: busybox:latest
      # 指定env来自哪里
      envFrom:
        # 配置ConfigMap的引用
        - configMapRef:
            # 指定要引用的ConfigMap的名称
            name: env-config' | kubectl apply -f -

# 查看Pod的日志是否打印出配置的环境变量
[root@master01 ~/configmap]# kubectl logs env-configmap-pod | grep USER
USER_ADDR=beijing
USER_AGE=18
USER_NAME=zhangsan

指定Pod的环境变量为ConfigMap中的Key

示例:

复制代码
# 创建Pod
echo 'apiVersion: v1
kind: Pod
metadata:
  name: env-configmap-pod-1
spec:
  containers:
    - name: app
      # 打印环境变量
      command: ["/bin/sh", "-c", "printenv"]
      image: busybox:latest
      env:
      # 指定Pod中环境变量的key
      - name: POD-NAME
        # 指定环境变量的value从哪里获取
        valueFrom:
          # 指定从ConfigMap中获取
          configMapKeyRef:
            # 指定ConfigMap的名称
            name: special-config
            # 指定ConfigMap中存在key
            key: USER_NAME
      # 可以定义多个值
      - name: POD-ADDR
        valueFrom:
          configMapKeyRef:
            name: env-config
            key: USER_ADDR' | kubectl apply -f -

# 查看日志中打印的环境变量
[root@master01 ~/configmap]# kubectl logs env-configmap-pod-1 | grep POD
POD-ADDR=beijing
POD-NAME=huangsir

Pod通过存储卷挂载使用ConfigMap的方式

创建ConfigMap

复制代码
[root@master01 ~/configmap]# echo 'apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  config.properties: |
    database.url=jdbc:mysql://localhost:3306/mydb
    database.username=root
    database.password=password
  log.properties: |
    log.level=INFO
    log.file=/var/log/app.log' | kubectl apply -f -

创建Pod挂载ConfigMap

方式一:将ConfigMap中所有的Key挂载到Pod指定的路径下,这种方式有以下几个地方需要注意:

  • 如果Pod挂载所在的目录中有其它的文件或目录,会将其删除
  • 如果指定Pod挂载的目录不存在,会自动创建
  • 挂载时会自动创建对应ConfigMap中的文件

示例:

复制代码
# 创建Pod
echo 'apiVersion: v1
kind: Pod
metadata:
  name: volume-pod-1
spec:
  containers:
    - name: app
      image: nginx:latest
      # 存储卷挂载到本地
      volumeMounts:
      # Pod存储卷挂载的名称
      - name: config-volume
        mountPath: /etc/config
  volumes:
  - name: config-volume
    # 存储卷挂载的类型
    configMap:
      name: app-config' | kubectl apply -f -
	  
# 验证挂载路径
[root@master01 ~/configmap]# kubectl exec -it volume-pod-1 -- ls -l /etc/config
total 0
lrwxrwxrwx 1 root root 24 May  2 07:50 config.properties -> ..data/config.properties
lrwxrwxrwx 1 root root 21 May  2 07:50 log.properties -> ..data/log.properties

如果只想挂载ConfigMap中的部分键,并且指定挂载到Pod中的文件名,可以使用itemssubPath字段来实现

示例:

复制代码
 # 创建Pod
[root@master01 ~/configmap]# echo 'apiVersion: v1
kind: Pod
metadata:
  name: volume-pod-2
spec:
  containers:
    - name: app
      image: nginx:latest
      # 存储卷挂载到本地
      volumeMounts:
      # Pod存储卷挂载的名称
      - name: config-volume
        mountPath: /etc/config/log-custom.properties
        # 当使用subPath属性时,则mountPath执行的不是目录,而是文件
        # 必要条件:一定要让subPath的值和items列表中的path值相同
        subPath: log-custom.properties
  volumes:
  - name: config-volume
    # 存储卷挂载的类型
    configMap:
      name: app-config
      items:
      - key: log.properties
        path: log-custom.properties' | kubectl apply -f -

# 验证
[root@master01 ~/configmap]# kubectl exec -it volume-pod-2 -- cat /etc/config/log-custom.properties
log.level=INFO
log.file=/var/log/app.log

配置不可变更的 ConfigMap

Kubernetes v1.21的版本中提供了一种将各个 Secret 和 ConfigMap 设置为不可变更的选项。

对于大量使用 ConfigMap 的集群 (至少有数万个各不相同的 ConfigMap 给 Pod 挂载)而言,禁止更改 ConfigMap 的数据有以下好处:

  • 保护应用,使之免受意外(不想要的)更新所带来的负面影响。
  • 通过大幅降低对 kube-apiserver 的压力提升集群性能, 这是因为系统会关闭对已标记为不可变更的 ConfigMap 的监视操作。

实现方式:

复制代码
# 通过将 immutable 字段设置为 true 创建不可变更的 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

一旦某 ConfigMap 被标记为不可变更,则 无法 逆转这一变化,也无法更改 data 或 binaryData 字段的内容。你只能删除并重建 ConfigMap。 因为现有的 Pod 会维护一个已被删除的 ConfigMap 的挂载点,建议重新创建这些 Pods。

挂载的 ConfigMap 内容会被自动更新

当Pod中使用的 ConfigMap 被更新时,所对应的Pod中对应Key也会被更新。

kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。不过,kubelet 使用的是其本地的高速缓存来获得 ConfigMap 的当前值。 高速缓存的类型可以通过 KubeletConfiguration.configMapAndSecretChangeDetectionStrategy 字段来配置。

注意以下两种方式不会被更新,需要重启Pod:

  • 以环境变量方式使用的 ConfigMap 数据不会被自动更新。
  • 使用 ConfigMap 作为 subPath 卷挂载的容器将不会收到 ConfigMap 的更新。

示例:以上述Pod通过存储卷挂载使用ConfigMap的方式来验证

复制代码
# 修改ConfigMap前的挂载内容
# pod1的内容,以普通数据卷的挂载方式
[root@master01 ~]# kubectl exec -it volume-pod-1 -- cat /etc/config/log.properties
log.level=INFO
log.file=/var/log/app.log
# pod2的内容,以subPath的挂载方式
[root@master01 ~]# kubectl exec -it volume-pod-2 -- cat /etc/config/log-custom.properties
log.level=INFO
log.file=/var/log/app.log

# 修改ConfigMap
[root@master01 ~]# echo 'apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  config.properties: |
    database.url=jdbc:mysql://localhost:3306/mydb
    database.username=root
    database.password=password
  log.properties: |
    log.level=error
    log.file=/var/log/error.log' | kubectl apply -f -

# 验证是否修改成功
[root@master01 ~]# kubectl describe cm app-config
Name:         app-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
config.properties:
----
database.url=jdbc:mysql://localhost:3306/mydb
database.username=root
database.password=password

log.properties:
----
log.level=error
log.file=/var/log/error.log


BinaryData
====

Events:  <none>

# 验证Pod内部是否自动更新
# pod1更新成功,
[root@master01 ~]# kubectl exec -it volume-pod-1 -- cat /etc/config/log.properties
log.level=error
log.file=/var/log/error.log
# pod2未更新
[root@master01 ~]# kubectl exec -it volume-pod-2 -- cat /etc/config/log-custom.properties
log.level=INFO
log.file=/var/log/app.log