ConfigMap
ConfigMap

目录
[toc]
本节实战
| 实战项目 |
|---|
| 💘 实战:yaml中|和>测试-2023.1.10(测试成功) |
| 💘 实战:ConfigMap创建测试-2023.1.8(测试成功) |
| 💘 实战:使用 ConfigMap 来填充我们的环境变量-2023.1.14(测试成功) |
| 💘 实战:使用 ConfigMap来设置命令行参数-2023.1.14(测试成功) |
| 💘 实战:通过数据卷来使用ConfigMap-2023.1.14(测试成功) |
| 💘 实战:在 ConfigMap 值被映射的数据卷里去控制路径-items-2023.1.14(测试成功) |
| 💘 实战:ConfigMap里使用SubPath-2023.1.14(测试成功) |
| 💘 实战:ConfigMap里使用subPathExpr-2023.1.15(测试成功) |
| 💘 实战:不可变更的 ConfigMap-2023.1.15(测试成功) |
前言
前面我们学习了一些常用的资源对象的使用,但是单纯依靠这些资源对象,还不足以满足我们的日常需求,一个重要的需求就是应用的配置管理、敏感信息的存储和使用(如:密码、Token 、私钥等)、安全管控、身份认证等等。
对于应用的可变配置在 Kubernetes 中是通过一个 ConfigMap 资源对象来实现的,ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时 Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap 将你的环境配置信息和容器镜像进行解耦,便于应用配置的修改。不过需要注意 ConfigMap 并不提供保密或者加密功能。如果你想存储的数据是机密的,则可以使用 Secret 对象,或者使用其他第三方工具来保证你的数据的私密性,而不是用 ConfigMap。
我们知道许多应用经常会有从配置文件、命令行参数或者环境变量中读取一些配置信息的需求,这些配置信息我们肯定不会直接写死到应用程序中去的,比如你一个应用连接一个 redis 服务,下一次想更换一个了的,还得重新去修改代码,重新制作一个镜像,这肯定是不可取的。而 ConfigMap 就给我们提供了向容器中注入配置信息的能力,不仅可以用来保存单个属性,还可以用来保存整个配置文件,比如我们可以用来配置一个 redis 服务的访问地址,也可以用来保存整个 redis 的配置文件等等。
值得注意的是 ConfigMap 虽然也是一个 K8s 资源对象,但是和其他 Kubernetes 对象都有一个 spec 属性不同的是,ConfigMap 使用 data 和 binaryData 字段,这些字段能够接收键值对作为其值,data 和 binaryData 字段都是可选的,data 字段设计用来保存 UTF-8 字符串,而 binaryData 则被设计用来保存二进制数据作为 base64编码的字串。data 或 binaryData 字段下面的每个键的名称都必须由字母数字字符或者 -、_ 或 . 组成,在data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。此外从 v1.19 版本开始,我们还可以添加一个immutable 字段到 ConfigMap 对象中,用于创建不可变更的 ConfigMap。
应用程序配置文件存储:ConfigMap的应用场景。
- 将应用程序配置文件解耦出来,存放在k8s中。
- 根据不同的业务环境,存放不同的应用配置文件。
你可以整一个nginx镜像,把它的配置文件给分离出来并存储到k8s的ConfigMap里面,然后再挂载到pod里进行使用。

1、创建
⚠️ yaml里易混淆的点
ConfigMap对象yaml文件:
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: cm-demo
5 namespace: default
6data:
7 data.1: hello
8 data.2: world
9 config: |
10 property.1=value-1
11 property.2=value-2
12 property.3=value-3
⚠️ 注意:以下3种用法可能会比较容易混淆,多看几次就能记清楚了。(一般使用| |-2种用法就足够了)
(1)|用法
我们可以看到 config 后面有一个竖线符 |,这在 yaml 中表示保留换行,(最后一行的空白和换行符会被取掉,前面换行被被保留),而额外的缩进会被保留。
1lines: |
2 我是第一行
3 我是第二行
4 我是吴彦祖
5 我是第四行
6 我是第五行
7
8# JSON
9{"lines": "我是第一行\n我是第二行\n 我是吴彦祖\n 我是第四行\n我是第五行"}
(2)|+和|-用法
我们还可以使用竖线和加号或者减号进行配合使用,+ 表示保留文字块末尾的换行,- 表示删除字符串末尾的换行。
1value: |
2 hello
3 hello
4
5# {"value": "hello\nhello"}
6
7value: |-
8 hello
9
10# {"value": "hello"}
11
12value: |+
13 hello
14
15# {"value": "hello\n\n"} (有多少个回车就有多少个\n)
(3)>用法
使用 > 右尖括号,用来表示折叠换行,只有空白行才会被识别为换行,原来的换行符都会被转换成空格。
1lines: >
2 我是第一行
3 我也是第一行
4 我仍是第一行
5 我依旧是第一行
6
7 我是第二行
8 这么巧我也是第二行
9
10# JSON
11{"lines": "我是第一行 我也是第一行 我仍是第一行 我依旧是第一行\n我是第二行 这么巧我也是第二行"}
💘 实战:yaml中|和>测试-2023.1.10(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
编写yaml文件
1#cm-demo.yaml
2apiVersion: v1
3kind: ConfigMap
4metadata:
5 name: cm-demo
6 namespace: default
7data:
8 data.1: hello
9 data.2: world
10 config: |
11 property.1=value-1
12 property.2=value-2
13 property.3=value-3
14
15 lines: >
16 我是第一行
17 我也是第一行
18 我仍是第一行
19 我依旧是第一行
20
21 我是第二行
22 这么巧我也是第二行
23

- 部署并测试

奇怪:这里不是第一次部署嘛?annotations这里为什么还会显示last-applied-configuration呢?……😢
总感觉这里的演示情况和前面理论部分有点出入:……😢

前面的理论部分:


这里关于细节部分,还是有点疑惑……先就这样吧。
测试结束。😘
1.通过资源清单文件方法创建ConfigMap
ConfigMap 资源对象使用 key-value 形式的键值对来配置数据,这些数据可以在 Pod 里面使用,如下所示的资源清单:
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: cm-demo
5 namespace: default
6data:
7 data.1: hello
8 data.2: world
9 config: |
10 property.1=value-1
11 property.2=value-2
12 property.3=value-3
其中配置数据在 data 属性下面进行配置,前两个被用来保存单个属性,后面一个被用来保存一个配置文件。
当然同样的我们可以使用kubectl create -f xx.yaml来创建上面的 ConfigMap 对象。
- 注意:这里的key和value一定都是字符串
binaryData <map[string]string>

2.通过from-file关键字创建ConfigMap
但是如果我们不知道怎么创建 ConfigMap 的话,不要忘记 kubectl 是我们最好的帮手,可以使用kubectl create configmap -h来查看关于创建 ConfigMap 的帮助信息:
1[root@master1 ~]#kubectl create configmap --help
2Create a config map based on a file, directory, or specified literal value.
3……
4Examples:
5 # Create a new configmap named my-config based on folder bar
6 kubectl create configmap my-config --from-file=path/to/bar
7
8 # Create a new configmap named my-config with specified keys instead of file basenames on disk
9 kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
10
11 # Create a new configmap named my-config with key1=config1 and key2=config2
12 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
💘 实战:ConfigMap创建测试-2023.1.8(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
创建测试文件
我们可以看到可以从一个给定的目录来创建一个 ConfigMap 对象。比如我们创建 testcm 的目录,该目录下面包含一些配置文件,redis 和 mysql 的连接信息,如下:
1cd ~
2mkdir testcm
3cd testcm/
4echo host=127.0.0.1 > redis.conf
5echo port=6379 >> redis.conf
6
7echo host=127.0.0.1 > mysql.conf
8echo port=3306 >> mysql.conf
9
10cat redis.conf
11cat mysql.conf
(1)from-file 关键字:使用指定的目录进行创建 ConfigMap
- 然后,我们就可以使用 from-file 关键字来创建包含这个目录下面所有配置文件的 ConfigMap:
1[root@master1 ~]#kubectl create cm cm-demo1 --from-file=testcm/
2configmap/cm-demo1 created
其中 from-file 参数指定在该目录下面的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容。
- 创建完成后,同样我们可以使用如下命令来查看 ConfigMap 列表:
1[root@master1 ~]#kubectl get cm
2NAME DATA AGE
3cm-demo1 2 46s
- 可以看到已经创建了一个 cm-demo1 的 ConfigMap 对象,然后可以使用 describe 命令查看详细信息:
1[root@master1 ~]#kubectl describe cm cm-demo1
2Name: cm-demo1
3Namespace: default
4Labels: <none>
5Annotations: <none>
6
7Data
8====
9mysql.conf:
10----
11host=127.0.0.1
12port=3306
13
14redis.conf:
15----
16host=127.0.0.1
17port=6379
18
19
20BinaryData
21====
22
23Events: <none>
我们可以看到两个 key 是 testcm 目录下面的文件名称,对应的 value 值就是文件内容。
- 这里值得注意的是如果文件里面的配置信息很大的话,describe 的时候可能不会显示对应的值,要查看完整的键值,可以使用如下命令:
1[root@master1 ~]#kubectl get cm cm-demo1 -oyaml
2apiVersion: v1
3data:
4 mysql.conf: |
5 host=127.0.0.1
6 port=3306
7 redis.conf: |
8 host=127.0.0.1
9 port=6379
10kind: ConfigMap
11metadata:
12 creationTimestamp: "2023-01-08T13:55:22Z"
13 name: cm-demo1
14 namespace: default
15 resourceVersion: "901730"
16 uid: cbebf27b-f1b5-4b7c-bfe9-d3c07790a48d
(2)from-file 关键字:使用指定的文件进行创建 ConfigMap
除了通过文件目录进行创建,我们也可以使用指定的文件进行创建 ConfigMap。
- 同样的,以上面的配置文件为例,我们创建一个 redis 的配置的一个单独 ConfigMap 对象:
1[root@master1 ~]#kubectl create cm cm-demo2 --from-file=testcm/redis.conf -n default
2configmap/cm-demo2 created
- 查看
1[root@master1 ~]#kubectl get cm cm-demo2
2NAME DATA AGE
3cm-demo2 1 17s
4[root@master1 ~]#kubectl get cm cm-demo2 -oyaml
5apiVersion: v1
6data:
7 redis.conf: |
8 host=127.0.0.1
9 port=6379
10kind: ConfigMap
11metadata:
12 creationTimestamp: "2023-01-08T13:59:11Z"
13 name: cm-demo2
14 namespace: default
15 resourceVersion: "902076"
16 uid: 95b6f6ed-977a-4830-8124-2ce28a61eb96
我们可以看到一个关联 redis.conf 文件配置信息的 ConfigMap 对象创建成功了。
- 另外值得注意的是 –from-file 这个参数可以使用多次,比如我们这里使用两次分别指定 redis.conf 和 mysql.conf 文件,就和直接指定整个目录是一样的效果了。
1[root@master1 ~]#kubectl create cm cm-demo22 --from-file=testcm/redis.conf --from-file=testcm/mysql.conf -n default
2configmap/cm-demo22 created
3[root@master1 ~]#kubectl get cm cm-demo22
4NAME DATA AGE
5cm-demo22 2 10s
6[root@master1 ~]#kubectl get cm cm-demo22 -oyaml
7apiVersion: v1
8data:
9 mysql.conf: |
10 host=127.0.0.1
11 port=3306
12 redis.conf: |
13 host=127.0.0.1
14 port=6379
15kind: ConfigMap
16metadata:
17 creationTimestamp: "2023-01-08T14:01:14Z"
18 name: cm-demo22
19 namespace: default
20 resourceVersion: "902262"
21 uid: b044c573-35dc-4867-86ff-4d7cc07faa1b
(3)from-file 关键字:使用字符串进行创建ConfigMap
- 另外,通过帮助文档我们可以看到我们还可以直接使用字符串进行创建,通过 –from-literal 参数传递配置信息,同样的,这个参数可以使用多次,格式如下:
1[root@master1 ~]#kubectl create configmap cm-demo3 --from-literal=db.host=localhost --from-literal=db.port=3306
2configmap/cm-demo3 created
3[root@master1 ~]#kubectl get cm cm-demo3 -oyaml
4apiVersion: v1
5data:
6 db.host: localhost
7 db.port: "3306" #注意:这里的数字用引号阔起来了,被当做是字符串来处理
8kind: ConfigMap
9metadata:
10 creationTimestamp: "2023-01-10T13:41:55Z"
11 name: cm-demo3
12 namespace: default
13 resourceVersion: "905135"
14 uid: b10d8e47-c9bf-46d1-8868-914f18ea490d
注意:一般数据较小的话,可以直接在资源配置清单里写一下,数据量比较大的话,就可以写到文件里进行创建使用。
测试结束。😘
2、使用
ConfigMap 创建成功了,那么我们应该怎么在 Pod 中来使用呢?我们可以使用四种方式来使用 ConfigMap 配置 Pod中的容器:
- 在容器命令和参数内
- 容器的环境变量
- 在只读卷里面添加一个文件,让应用来读取
- 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap
这些不同的方法适用于不同的数据使用方式,对前三个方法,kubelet 会使用 ConfigMap 中的数据在 Pod 中启动容器。第四种方法需要编写代码才能读取 ConfigMap 数据。
1.使用 ConfigMap 来填充我们的环境变量
💘 实战:使用 ConfigMap 来填充我们的环境变量-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
创建资源清单
1#testcm1-pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: testcm1-pod
6spec:
7 containers:
8 - name: testcm1
9 image: busybox
10 command: [ "/bin/sh", "-c", "env" ]
11 env: #注意:这里是env方式
12 - name: DB_HOST
13 valueFrom:
14 configMapKeyRef: #这里Ref翻译为引用
15 name: cm-demo3
16 key: db.host #注意:这个db.host仅仅只是cm-demo3这个ConnfigMap里的Key.
17 - name: DB_PORT
18 valueFrom:
19 configMapKeyRef:
20 name: cm-demo3
21 key: db.port
22 envFrom: #注意:这里是envFrom方式
23 - configMapRef:
24 name: cm-demo1
- 我们来再次确认下之前创建好的cm
1[root@master1 ~]#kubectl get cm cm-demo1 -oyaml
2apiVersion: v1
3data:
4 mysql.conf: |
5 host=127.0.0.1
6 port=3306
7 redis.conf: |
8 host=127.0.0.1
9 port=6379
10kind: ConfigMap
11metadata:
12 creationTimestamp: "2023-01-08T13:55:22Z"
13 name: cm-demo1
14 namespace: default
15 resourceVersion: "901730"
16 uid: cbebf27b-f1b5-4b7c-bfe9-d3c07790a48d
17[root@master1 ~]#kubectl get cm cm-demo3 -oyaml
18apiVersion: v1
19data:
20 db.host: localhost
21 db.port: "3306"
22kind: ConfigMap
23metadata:
24 creationTimestamp: "2023-01-10T13:41:55Z"
25 name: cm-demo3
26 namespace: default
27 resourceVersion: "905135"
28 uid: b10d8e47-c9bf-46d1-8868-914f18ea490d
- 部署上面的testcm1-pod.yaml并查看
1[root@master1 ~]#kubectl apply -f testcm1-pod.yaml
2pod/testcm1-pod created
3[root@master1 ~]#kubectl get po
4NAME READY STATUS RESTARTS AGE
5testcm1-pod 0/1 Completed 2 (26s ago) 28s
6
7#这个 Pod 运行后会输出如下所示的信息:
8[root@master1 ~]#kubectl logs testcm1-pod
9KUBERNETES_PORT=tcp://10.96.0.1:443
10KUBERNETES_SERVICE_PORT=443
11HOSTNAME=testcm1-pod
12DB_PORT=3306 #这里
13SHLVL=1
14HOME=/root
15mysql.conf=host=127.0.0.1 #特别,注意下这里:这里的key:value是一个整体,是作为文件名`mysql.conf`的value的;
16port=3306
17
18redis.conf=host=127.0.0.1 #这里
19port=6379
20
21KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
22PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
23KUBERNETES_PORT_443_TCP_PORT=443
24KUBERNETES_PORT_443_TCP_PROTO=tcp
25KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
26KUBERNETES_SERVICE_PORT_HTTPS=443
27KUBERNETES_SERVICE_HOST=10.96.0.1
28PWD=/
29DB_HOST=localhost #这里
我们可以看到 DB_HOST 和 DB_PORT 都已经正常输出了,另外的环境变量是因为我们这里直接把 cm-demo1 给注入进来了,所以把他们的整个键值给输出出来了,这也是符合预期的。
符合预期,测试完成。😘
2.使用 ConfigMap来设置命令行参数
另外我们也可以使用 ConfigMap 来设置命令行参数,ConfigMap 也可以被用来设置容器中的命令或者参数值,如下Pod:
💘 实战:使用 ConfigMap来设置命令行参数-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
创建资源清单
1#testcm2-pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: testcm2-pod
6spec:
7 containers:
8 - name: testcm2
9 image: busybox
10 command: [ "/bin/sh", "-c", "echo $(DB_HOST) $(DB_PORT)" ]
11 env:
12 - name: DB_HOST
13 valueFrom:
14 configMapKeyRef:
15 name: cm-demo3
16 key: db.host
17 - name: DB_PORT
18 valueFrom:
19 configMapKeyRef:
20 name: cm-demo3
21 key: db.port
- 我们来再次确认下之前创建好的cm
1[root@master1 ~]#kubectl get cm cm-demo3 -oyaml
2apiVersion: v1
3data:
4 db.host: localhost
5 db.port: "3306"
6kind: ConfigMap
7metadata:
8 creationTimestamp: "2023-01-10T13:41:55Z"
9 name: cm-demo3
10 namespace: default
11 resourceVersion: "905135"
12 uid: b10d8e47-c9bf-46d1-8868-914f18ea490d
- 部署并查看
1[root@master1 ~]#kubectl apply -f testcm2-pod.yaml
2pod/testcm2-pod created
3
4#运行这个 Pod 后会输出如下信息:
5[root@master1 ~]#kubectl logs testcm2-pod
6localhost 3306
测试结束。😘
🍀 问题
注意:有的同学可能会说,既然我可以在容器中的命令行参数里使用这个变量,那么我是否可以在我的ConfigMap里去使用我的环境变量呢?

3.通过数据卷来使用ConfigMap
另外一种是非常常见的使用 ConfigMap 的方式:通过数据卷使用,在数据卷里面使用 ConfigMap,就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容。
💘 实战:通过数据卷来使用ConfigMap-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
创建资源清单
1#testcm3-pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: testcm3-pod
6spec:
7 volumes:
8 - name: config-volume
9 configMap:
10 name: cm-demo2
11 containers:
12 - name: testcm3
13 image: busybox
14 command: [ "/bin/sh", "-c", "cat /etc/config/redis.conf" ]
15 volumeMounts:
16 - name: config-volume
17 mountPath: /etc/config
- 我们来再次确认下之前创建好的cm
1[root@master1 ~]#kubectl get cm cm-demo2 -oyaml
2apiVersion: v1
3data:
4 redis.conf: |
5 host=127.0.0.1
6 port=6379
7kind: ConfigMap
8metadata:
9 creationTimestamp: "2023-01-08T13:59:11Z"
10 name: cm-demo2
11 namespace: default
12 resourceVersion: "902076"
13 uid: 95b6f6ed-977a-4830-8124-2ce28a61eb96
- 部署并测试
1[root@master1 ~]#kubectl apply -f testcm3-pod.yaml
2pod/testcm3-pod created
3[root@master1 ~]#kubectl logs testcm3-pod
4host=127.0.0.1
5port=6379
符合预期,测试结束。😘
💘 实战:在 ConfigMap 值被映射的数据卷里去控制路径-items-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
==当然我们也可以在 ConfigMap 值被映射的数据卷里去控制路径==,如下 Pod 定义:
1#testcm4-pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: testcm4-pod
6spec:
7 volumes:
8 - name: config-volume
9 configMap:
10 name: cm-demo1
11 items:
12 - key: mysql.conf
13 path: path/to/msyql.conf
14 containers:
15 - name: testcm4
16 image: busybox
17 command: [ "/bin/sh","-c","cat /etc/config/path/to/msyql.conf" ]
18 volumeMounts:
19 - name: config-volume
20 mountPath: /etc/config
- 我们来再次确认下之前创建好的cm
1[root@master1 ~]#kubectl get cm cm-demo1 -oyaml
2apiVersion: v1
3data:
4 mysql.conf: |
5 host=127.0.0.1
6 port=3306
7 redis.conf: |
8 host=127.0.0.1
9 port=6379
10kind: ConfigMap
11metadata:
12 creationTimestamp: "2023-01-08T13:55:22Z"
13 name: cm-demo1
14 namespace: default
15 resourceVersion: "901730"
16 uid: cbebf27b-f1b5-4b7c-bfe9-d3c07790a48d
- 部署并测试
1[root@master1 ~]#kubectl apply -f testcm4-pod
2pod/testcm4-pod created
3[root@master1 ~]#kubectl logs testcm4-pod
4host=127.0.0.1
5port=3306
符合预期,测试结束。😘
⚠️ ConfigMap热更新&应用热更新
另外需要注意的是,当 ConfigMap 以数据卷的形式挂载进 Pod 的时,这时更新 ConfigMap(或删掉重建ConfigMap),Pod 内挂载的配置信息会热更新。kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。这时可以增加一些监测配置文件变更的脚本,然后重加载对应服务就可以实现应用的热更新。以环境变量方式使用的ConfigMap 数据不会被自动更新,更新这些数据需要重新启动 Pod。
只有通过 Kubernetes API 创建的 Pod 才能使用 ConfigMap,其他方式创建的(比如静态 Pod)不能使用;ConfigMap 文件大小限制为 1MB (ETCD 的要求)。
💘 实战:测试ConfigMap热更新-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
创建资源清单
1#testcm5-pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: testcm5-pod
6spec:
7 volumes:
8 - name: config-volume
9 configMap:
10 name: cm-demo1
11 items:
12 - key: mysql.conf
13 path: path/to/msyql.conf
14 containers:
15 - name: testcm4
16 image: busybox
17 command: [ "/bin/sh","-c"," cat /etc/config/path/to/msyql.conf;sleep 24h" ]
18 volumeMounts:
19 - name: config-volume
20 mountPath: /etc/config
- 查看之前的cm
1[root@master1 ~]#kubectl get cm cm-demo1 -oyaml
2apiVersion: v1
3data:
4 mysql.conf: |
5 host=127.0.0.1
6 port=3306
7 redis.conf: |
8 host=127.0.0.1
9 port=6379
10kind: ConfigMap
11metadata:
12 creationTimestamp: "2023-01-08T13:55:22Z"
13 name: cm-demo1
14 namespace: default
15 resourceVersion: "901730"
16 uid: cbebf27b-f1b5-4b7c-bfe9-d3c07790a48d
- 部署资源并查看
1[root@master1 ~]#kubectl apply -f testcm5-pod.yaml
2pod/testcm5-pod created
3[root@master1 ~]#kubectl logs testcm5-pod
4host=127.0.0.1
5port=3306
- 更新configmap里的信息
1[root@master1 ~]#kubectl edit cm cm-demo1
2#将3306改成3307

- 查看pod打印日志及pod里的文件是否发生了变化
1[root@master1 ~]#kubectl logs testcm5-pod
2host=127.0.0.1
3port=3306
4[root@master1 ~]#kubectl exec -it testcm5-pod -- cat /etc/config/path/to/msyql.conf
5host=127.0.0.1
6port=3307
可以看到pod打印日志依然是3306,但pod容器里的文件已经发生了变化,符合预期。
测试结束。😘
🍀 问题:ConfigMap支持动态更改吗?pod启动后,更改ConfigMap。
大佬回答:
支持的。我记得大概有1min多的延迟,相当于任何的ConfigMap如果mount到pod里面,那么会有一个什么样的动作呢? 它会把这个ConfigMap从apiserver下下来,然后放在本地docker的一个DRI里面,然后通过docker的mount mount到里面去。只要你的应用进程支持本地文件刷新的时候,它会重新去load,那么就可以动态更改。注意:我们有反复强调,以volume形式挂载到容器里的配置,是热更新,但不是实时更新,需要一定的加载时间!(关键看你的应用支不支持热加载!)
3、使用SubPath
1.subPath
上面我们介绍了可以将 ConfigMap 以数据卷的形式挂载到容器中去,但是如果原本容器目录下已经有一些文件或者数据,将数据挂载进去后便会覆盖容器目录下的数据,这个时候我们可以指定 volumeMounts.subPath 属性来指定卷内的子路径,而不是其根路径。
注意:
subPath形式挂载的数据不能热更新,除非重建Pod。(这个就很尴尬了……要他有何用……)
💘 实战:ConfigMap里使用SubPath-2023.1.14(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
比如我们将一个 Nginx 容器的配置放置到一个 ConfigMap 对象中来进行管理:
1#nginx-config.yaml
2apiVersion: v1
3kind: ConfigMap
4metadata:
5 name: nginx-config
6 namespace: default
7data:
8 nginx.conf: |
9 user nginx;
10 worker_processes auto;
11
12 error_log /var/log/nginx/error.log notice;
13 pid /var/run/nginx.pid;
14
15
16 events {
17 worker_connections 1024;
18 }
19
20
21 http {
22 include /etc/nginx/mime.types;
23 default_type application/octet-stream;
24
25 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
26 '$status $body_bytes_sent "$http_referer" '
27 '"$http_user_agent" "$http_x_forwarded_for"';
28
29 access_log /var/log/nginx/access.log main;
30
31 sendfile on;
32 #tcp_nopush on;
33
34 keepalive_timeout 65;
35
36 #gzip on;
37
38 include /etc/nginx/conf.d/*.conf;
39 }
- 然后我们将上面的配置文件挂载到容器中去使用,创建如下所示的一个资源对象:
1# nginx-deploy.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: nginx
6spec:
7 selector:
8 matchLabels:
9 app: nginx
10 template:
11 metadata:
12 labels:
13 app: nginx
14 spec:
15 containers:
16 - name: web
17 image: nginx:1.7.9
18 ports:
19 - containerPort: 80
20 volumeMounts:
21 - name: config
22 mountPath: /etc/nginx
23 volumes:
24 - name: config
25 configMap:
26 name: nginx-config
27 items:
28 - key: nginx.conf
29 path: nginx.conf
- 按照我们的想法是将 ConfigMap 中的 nginx.conf 挂载到容器的 /etc/nginx 目录下面去,但是该目录下面原本已经有很多文件了,所以用上面的方式会将该目录中的数据覆盖掉,这样就会导致我们的 Nginx 应用启动失败。
1[root@master1 ~]#kubectl apply -f nginx-config.yaml
2configmap/nginx-config created
3[root@master1 ~]#kubectl apply -f nginx-deploy.yaml
4deployment.apps/nginx created
5
6[root@master1 ~]# kubectl get pods -l app=nginx
7NAME READY STATUS RESTARTS AGE
8nginx-5456544546-2nmt6 0/1 CrashLoopBackOff 3 (12s ago) 55s
9[root@master1 ~]#kubectl logs nginx-5456544546-2nmt6
102023/01/15 01:16:41 [emerg] 1#0: open() "/etc/nginx/mime.types" failed (2: No such file or directory) in /etc/nginx/nginx.conf:14
11nginx: [emerg] open() "/etc/nginx/mime.types" failed (2: No such file or directory) in /etc/nginx/nginx.conf:14
12
13#注意:nginx下默认是有很多文件的
14[root@master1 ~]#kubectl exec -it nginx-7498977d8c-r6sqx -- ls /etc/nginx
15conf.d fastcgi_params koi-utf koi-win mime.types nginx.conf scgi_params uwsgi_params win-utf
- 我们只需要在容器的 volumeMounts 中通过 subPath 指定下子路径即可:
1# nginx-deploy.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: nginx
6spec:
7 selector:
8 matchLabels:
9 app: nginx
10 template:
11 metadata:
12 labels:
13 app: nginx
14 spec:
15 containers:
16 - name: web
17 image: nginx:1.7.9
18 ports:
19 - containerPort: 80
20 volumeMounts:
21 - name: config
22 mountPath: /etc/nginx/nginx.conf
23 subPath: nginx.conf
24 volumes:
25 - name: config
26 configMap:
27 name: nginx-config
28 items:
29 - key: nginx.conf
30 path: nginx.conf

- 更新后我们的容器就可以正常启动了:
1[root@master1 ~]# kubectl get pods -l app=nginx
2NAME READY STATUS RESTARTS AGE
3nginx-7498977d8c-r6sqx 1/1 Running 0 4s
4[root@master1 ~]#kubectl logs nginx-7498977d8c-r6sqx
52023/01/15 01:23:03 [notice] 1#0: using the "epoll" event method
62023/01/15 01:23:03 [notice] 1#0: nginx/1.7.9
72023/01/15 01:23:03 [notice] 1#0: built by gcc 4.7.2 (Debian 4.7.2-5)
82023/01/15 01:23:03 [notice] 1#0: OS: Linux 3.10.0-957.el7.x86_64
92023/01/15 01:23:03 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 65536:65536
102023/01/15 01:23:03 [notice] 1#0: start worker processes
112023/01/15 01:23:03 [notice] 1#0: start worker process 6
122023/01/15 01:23:03 [notice] 1#0: start worker process 7
符合预期,测试结束。😘
2.subPathExpr
除此之外我们还可以使用带有扩展环境变量的 subPath ,该功能在 v1.17 版本已经进行稳定状态了,通过使用subPathExpr 字段可以基于 downward API 环境变量来构造 subPath 目录名,不过需要注意 subPath 和subPathExpr 属性是互斥的。
💘 实战:ConfigMap里使用subPathExpr-2023.1.15(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
我们经常有在容器中创建以 Pod 名为路径的目录来保存日志数据,我们就可以使用该特性来实现。比如我们在 Pod 中使用subPathExpr 在 hostPath 卷的 /var/log 中创建目录 pod1 ,hostPath 卷采用来自 downward API 的Pod 名称生成目录名,然后宿主机目录 /var/log/pod1 被挂载到容器的 /logs 中。如下资源对象所示:
1#test-subPathExpr.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: pod1
6spec:
7 containers:
8 - name: container1
9 env:
10 - name: POD_NAME
11 valueFrom:
12 fieldRef:
13 apiVersion: v1
14 fieldPath: metadata.name
15 image: busybox:1.28
16 command:
17 [
18 "sh",
19 "-c",
20 "while true; do echo 'Hello' >> /logs/hello.txt; sleep 1; done",
21 ]
22 volumeMounts:
23 - name: log
24 mountPath: /logs
25 subPathExpr: $(POD_NAME) # 包裹变量名的是小括号,而不是大括号
26 restartPolicy: Never
27 volumes:
28 - name: log
29 hostPath:
30 path: /var/log
- 当我们创建上面的资源对象后会通过 downward api 获取到 POD_NAME 环境变量的值为 pod1,然后通过subPathExpr 会在宿主机的 /var/log 目录下面创建 pod1 子目录,然后和容器中的 /logs 目录进行映射,所以最终日志数据会出现在宿主机的 /var/log/pod1 目录下面。
1[root@master1 ~]#kubectl get po pod1 -owide
2NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3pod1 1/1 Running 0 43s 10.244.1.69 node1 <none> <none>
4[root@master1 ~]#ssh 172.29.9.62
5Last login: Tue Dec 27 21:23:31 2022 from 172.29.0.1
6[root@node1 ~]# tail -f /var/log/pod1/hello.txt
7Hello
8Hello
9Hello
10Hello
11Hello
12Hello
13Hello
14Hello
15Hello
16Hello
17Hello
18Hello
19……
符合预期,测试结束。😘
4、不可变更的 ConfigMap
Kubernetes 特性 Immutable Secret 和 ConfigMaps 提供了一种将各个 Secret 和 ConfigMap 设置为不可变更的选项。对于大量使用 ConfigMap 的集群(至少有数万个各不相同的 ConfigMap 给 Pod 挂载)而言,禁止更改ConfigMap 的数据有以下好处:
- 保护应用,使之免受意外(不想要的)更新所带来的负面影响。
- 通过大幅降低对 kube-apiserver 的压力提升集群性能,这是因为系统会关闭对已标记为不可变更的 ConfigMap的 watch 操作。
此功能在 v1.21 版本就已经是稳定的特性了,可以通过 ImmutableEphemeralVolumes 特性门控来控制,我们只需要通过将 immutable 字段设置为 true 即可创建不可变更的 ConfigMap。
💘 实战:不可变更的 ConfigMap-2023.1.15(测试成功)

- 实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.25.4
5 containerd://1.6.10
实验软件(无)
编写配置清单
1# immu-cm.yaml
2apiVersion: v1
3kind: ConfigMap
4metadata:
5 name: immu-cm
6data:
7 mysql.conf: |-
8 host=127.0.0.1
9 port=3306
10immutable: true
一旦某 ConfigMap 被标记为不可变更,则无法逆转这一变化,也无法更改 data 或 binaryData 字段的内容,只能删除并重建 ConfigMap。因为现有的 Pod 会维护一个已被删除的 ConfigMap 的挂载点,建议重新创建这些 Pods。
- 部署上面的资源
1[root@master1 ~]#kubectl apply -f immu-cm.yaml
2configmap/immu-cm created
- 当我们修改上面的 ConfigMap 数据内容后,重新更新该对象就会出现如下所示的错误信息:
1[root@master1 ~]#kubectl edit cm immu-cm
2#当编辑次不可变cm时,无法正常保存。

这里我们直接在原来的资源清单重新编辑下,再apply下看下效果:
1[root@master1 ~]#vim immu-cm.yaml
2随便改动下一些参数:
3[root@master1 ~]#kubectl apply -f immu-cm.yaml
4The ConfigMap "immu-cm" is invalid: data: Forbidden: field is immutable when `immutable` is set
符合预期,测试结束。😘
FAQ
ConfigMap和配置中心(apollo/nacos)的区别
“配置管理中心"在微服务中比较主流,它肯定比configmap这种要强很多;
注意:ConfigMap是肯定的会用到的,它当然没有apollo,nacos功能强大,但是这些注册中心是需要应用程序去做一个对接的,而ConfigMap就是,你把配置写给我,我给你注入进去就可以了,如果你是volume的话,我给你挂载进去就可以了。至于你的程序要怎么做热更新,怎么自动读取,那是你程序的事情。这个k8s它是管不了的。当然,我们是可以通过一些额外的手段可以做到,即使你的程序不支持热更新,我们也可以让它做一个reload操作的,是可以做到的;
像redeis,mysql这些配置,只要你应用层面和apollo/nacos对接做了配置的话,那么 在apollo/nacos后台直接改个配置,这边就直接下发下去,就直接生效了。
这个configmap可能更多的还是在一个简单的配置方面,它和你的应用代码的耦合度就没那么高。但是你想利用configmap去做apollo/nacos这样的一个事情,应该是可以做到的。 就是你想通过configmmap去实现一个,去开发一个类似于apollo这样的一个配置中心,应该是可以做到的。
可以用命令直接获取configmap里某个key的值吗?
答案:不可以,这里只有kubectl get cm命令;
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

🍀 微信公众号 《云原生架构师实战》

🍀 博客 www.onlyyou520.com


🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

🍀 知乎 https://www.zhihu.com/people/foryouone

最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

1

