0%

Kubernetes 配置之ConfigMap

这里介绍Kubernetes配置的ConfigMap

abstract.png

创建ConfigMap

对于非敏感性、非机密性的配置数据,Kubernetes提供了ConfigMap进行存储。具体地,其以键值对的形式进行存储。下面即是一个简单的利用Yaml文件定义配置数据的ConfigMap示例

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
# 资源类型
kind: ConfigMap
metadata:
name: my-configmap-1
data:
# 配置数据, 即键值对
mysql.ip: 196.168.1.2
mysql.user: root
mysql.password: "123456"

效果如下所示

figure 1.jpeg

事实上,ConfigMap还支持通过命令行进行创建。具体地:

  • —from-literal=[key]=[value] :从字面量定义配置
  • —from-file=[fileName] :从指定文件创建配置,Key默认为文件名,Value为该文件的内容
  • —from-file=[key]=[fileName] :从指定文件创建配置,Key为指定名称,Value为该文件的内容
  • —from-file=[folderName] :从指定文件夹创建配置, Key为该文件夹下各文件名, Value为相应文件的内容

假设目前应用的所有配置文件按如下方式存储

figure 2.jpeg

则我们可通过下述命令创建ConfigMap

1
2
3
4
5
6
7
8
9
10
# 创建一个名为my-configmap-2的configmap
kubectl create configmap my-configmap-2 \
`# 从指定文件夹创建配置, Key为该文件夹下各文件名, Value为相应文件的内容` \
--from-file=PersonInfo/ \
`# 从指定文件创建配置, Key默认为文件名, 即MongoDB.properties, Value为该文件的内容` \
--from-file=MongoDB.properties \
`# 从指定文件创建配置, Key名自定义为pgdb, Value为该文件的内容` \
--from-file=pgdb=PostgreSQL.properties \
`# 从字面量创建配置, Key为socre, Value为98` \
--from-literal=score="98"

效果如下所示

figure 3.jpeg

使用ConfigMap

基于环境变量

对于容器而言,其使用ConfigMap最简单的方式即是通过环境变量实现。具体地,利用ConfigMap的条目来初始化容器运行过程中所需的环境变量值。这里我们构建一个名为demo:1.0的容器镜像,Dockerfile如下所示

1
2
3
4
5
# 镜像:demo:1.0

FROM ubuntu:22.10
COPY demo.sh /home/aaron/
ENTRYPOINT ["sh", "/home/aaron/demo.sh" ]

该镜像很简单,就是执行一个Shell脚本,具体地如下所示,获取环境变量的值、重复打印即可

1
2
3
4
5
6
#!/bin/bash
while true
do
echo "${greet}, I'm ${name}. I want to say: ${msg}"
sleep 5s
done

文件结构如下所示。Docker镜像构建完毕后推送至中央仓库Docker Hub当中,以便后续Kubernetes可以从中央仓库中拉取、使用该镜像

figure 4.jpeg

我们可以先通过Docker创建容器来验证下镜像是否正常

1
2
3
4
docker run --name demo1 --rm -it  \
--env greet="Hello" \
--env name="Tom" \
--env msg="Good Luck" demo:1.0

效果如下所示,符合预期

figure 5.jpeg

现在我们创建一个ConfigMap,并将ConfigMap中的条目通过环境变量的方式暴露、传递给容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 创建 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap-3
data:
app.greet: Hi
app.name: Tom
app.msg: Thank you

---

# 创建RS

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-1
spec:
replicas: 1
selector:
matchLabels:
app: my-app-1
# Pod 模板
template:
metadata:
labels:
app: my-app-1
spec:
# 容器信息
containers:
- name: my-app-1
image: aaron1995/demo:1.0
env:
# 将名为my-configmap-3的ConfigMap中Key为app.greet的配置 赋值给 容器中名为greet的环境变量
- name: greet
valueFrom:
configMapKeyRef:
name: my-configmap-3
key: app.greet
# 对ConfigMap的引用是可选的, 这样即使ConfigMap不存在, 容器也能正常启动
optional: true
# 将名为my-configmap-3的ConfigMap中Key为app.name的配置 赋值给 容器中名为name的环境变量
- name: name
valueFrom:
configMapKeyRef:
name: my-configmap-3
key: app.name
optional: true
# 将名为my-configmap-3的ConfigMap中Key为app.msg的配置 赋值给 容器中名为msg的环境变量
- name: msg
valueFrom:
configMapKeyRef:
name: my-configmap-3
key: app.msg
optional: true

---

效果如下所示,通过查看容器日志说明其成功获取到了配置信息

figure 6.jpeg

如果ConfigMap的条目非常多,一个一个定义环境变量显然非常繁琐、麻烦。故我们还可以一次性将ConfigMap中的所有条目全部设置容器当中的环境变量。其中ConfigMap中的键名将作为环境变量的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 创建 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap-4
data:
greet: Hello
name: Aaron
msg: I Love U

---

# 创建RS

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-2
spec:
replicas: 1
selector:
matchLabels:
app: my-app-2
# Pod 模板
template:
metadata:
labels:
app: my-app-2
spec:
# 容器信息
containers:
- name: my-app-2
image: aaron1995/demo:1.0
envFrom:
# 将名为my-configmap-4的ConfigMap中的配置 全部作为容器中的环境变量
- configMapRef:
name: my-configmap-4

---

效果如下所示,通过查看容器日志说明其成功获取到了配置信息

figure 7.jpeg

基于命令行参数

在Kubernetes中镜像的ENTRYPOINT、CMD指令均可以被覆盖,仅需在容器定义时设置command、args属性的值即可。虽然我们定义容器时无法对args属性直接引用ConfigMap中的条目。但可以先利用ConfigMap条目初始化环境变量,然后在args属性中引用该环境变量。以此实现将ConfigMap中的条目通过命令行参数暴露、传递给容器

这里我们定义一个名为demo:2.0的容器镜像,Dockerfile如下所示。其本质就是调用一个Shell脚本

1
2
3
4
5
# 镜像:demo:2.0

FROM ubuntu:22.10
COPY demo.sh /home/aaron/
ENTRYPOINT ["sh", "/home/aaron/demo.sh" ]

该Shell脚本如下所示,其获取命令行参数并循环打印

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
greet=$1
name=$2
msg=$3

while true
do
echo "${greet}, My name is ${name}. say: ${msg}"
sleep 5s
done

文件结构如下所示。Docker镜像构建完毕后推送至中央仓库Docker Hub当中,以便后续Kubernetes可以从中央仓库中拉取、使用该镜像

figure 8.jpeg

我们可以先通过Docker创建容器来验证下镜像是否正常

1
2
docker run --name demo2 --rm -it demo:2.0 \
"Hi" "Lucy" "Have a nice Weekend"

效果如下所示,符合预期

figure 9.jpeg

现在我们创建一个ConfigMap,并将ConfigMap中的条目通过命令行参数的方式暴露、传递给容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 创建 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap-5
data:
app.greet: Hi
app.name: Tony
app.msg: Good Night

---

# 创建RS

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-3
spec:
replicas: 1
selector:
matchLabels:
app: my-app-3
# Pod 模板
template:
metadata:
labels:
app: my-app-3
spec:
# 容器信息
containers:
- name: my-app-3
image: aaron1995/demo:2.0
# 将环境变量作为命令行参数传递给容器
args: ["$(my.greet)", "$(my.name)", "$(my.msg)"]
# 通过ConfigMap定义环境变量
env:
- name: my.greet
valueFrom:
configMapKeyRef:
name: my-configmap-5
key: app.greet
- name: my.name
valueFrom:
configMapKeyRef:
name: my-configmap-5
key: app.name
- name: my.msg
valueFrom:
configMapKeyRef:
name: my-configmap-5
key: app.msg

---

效果如下所示,通过查看容器日志说明其成功获取到了配置信息

figure 10.jpeg

基于ConfigMap类型卷

基于环境变量、命令行参数的方式适用于少量配置的场景。且在上述两种方式当中,一旦ConfigMap中的条目值发生更新,容器无法在不重启的前提下感知到新的配置值。故可以通过ConfigMap类型卷的方式将ConfigMap中的条目暴露、传递到容器当中。显然该方式更适合较多配置的场景。且当ConfigMap发生更新后,ConfigMap类型卷中的文件也会自动更新。而无需重启容器

在此之前,我们先创建一个ConfigMap。其从文件、字面量当中创建配置

figure 11.jpeg

然后我们利用ConfigMap类型卷将ConfigMap中的全部条目暴露为容器内的文件,这样容器就可以直接读取该配置文件了。示例配置如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ConfigMap通过卷传递给容器: 方式1: 暴露ConfigMap中全部条目
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-5
spec:
replicas: 1
selector:
matchLabels:
app: my-app-5
# Pod 模板
template:
metadata:
labels:
app: my-app-5
spec:
# 容器信息
containers:
- name: my-app-5
image: luksa/kubia
volumeMounts:
# 将名为my-volume-app-conf的卷挂载到容器内的指定路径
- name: my-volume-app-conf
mountPath: /AppConf
# 容器对该卷只读
readOnly: true
volumes:
# 名为my-volume-app-conf的ConfigMap类型卷
- name: my-volume-app-conf
configMap:
# ConfigMap类型卷引用的ConfigMap名称
name: my-configmap-6

效果如下所示,符合预期

figure 12.jpeg

此外还可以将ConfigMap中指定条目暴露为容器当中的文件,示例配置如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# ConfigMap通过卷传递给容器: 方式2: 暴露ConfigMap中指定条目

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-6
spec:
replicas: 1
selector:
matchLabels:
app: my-app-6
# Pod 模板
template:
metadata:
labels:
app: my-app-6
spec:
# 容器信息
containers:
- name: my-app-6
image: luksa/kubia
volumeMounts:
# 将名为my-volume-app-conf的卷挂载到容器内的指定路径
- name: my-volume-app-conf
mountPath: /AppConf
# 容器对该卷只读
readOnly: true
volumes:
# 名为my-volume-app-conf的ConfigMap类型卷
- name: my-volume-app-conf
configMap:
# ConfigMap类型卷引用的ConfigMap名称
name: my-configmap-6
items:
# 将my-configmap-6中Key为PostgreSQL.properties的配置 暴露作为 ConfigMap类型卷中的文件
# 并将文件重命名为 pg.properties
- key: PostgreSQL.properties
path: pg.properties

效果如下所示,符合预期

figure 13.jpeg

众所周知,在Linux系统当中将文件系统挂载到非空文件夹时,会导致该非空文件夹下原有的文件全部被隐藏。此时原有文件自然也无法正常访问使用。为避免该问题,我们可以将ConfigMap中的指定条目挂载到容器的指定文件中。具体地,通过subPath选项实现。但需要注意的是,此时会导致配置无法进行热更新。即ConfigMap中值更新后, 容器中的文件不会被更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# ConfigMap通过卷传递给容器: 方式3: 将指定条目挂载到指定文件中

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs-7
spec:
replicas: 1
selector:
matchLabels:
app: my-app-7
# Pod 模板
template:
metadata:
labels:
app: my-app-7
spec:
# 容器信息
containers:
- name: my-app-7
image: luksa/kubia
volumeMounts:
# 将名为my-volume-app-conf卷中的PostgreSQL.properties文件挂载到容器内的/usr/db.conf文件上
- name: my-volume-app-conf
mountPath: /usr/db.conf
subPath: PostgreSQL.properties
# 将名为my-volume-app-conf卷中的sex文件挂载到容器内的/usr/sex.conf文件上
- name: my-volume-app-conf
mountPath: /usr/sex.conf
subPath: sex
volumes:
# 名为my-volume-app-conf的ConfigMap类型卷
- name: my-volume-app-conf
configMap:
# ConfigMap类型卷引用的ConfigMap名称
name: my-configmap-6
# 设置ConfigMap类型卷中所有文件权限为742, 配置时不足四位长度时需要使用前导零
defaultMode: 0742

效果如下所示,符合预期

figure 14.jpeg

Note

当ConfigMap中的配置值为数字时,如果以环境变量的方式进行暴露。会出现cannot convert int64 to string错误。究极原因是对于环境变量而言,其值永远都是字符串类型。解决办法也很简单,就是对ConfigMap中相关配置的值添加双引号,使其成为字符串类型

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap-demo
data:
app.count: "2"
app.msg: Thank you

参考文献

  1. Kubernetes in Action中文版 Marko Luksa著
  2. 深入剖析Kubernetes 张磊著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝