十六、K8s 配置管理中心 ConfigMap 实现微服务配置管理
16.1 ConfigMap 相关概念及cm字段
16.1.1 ConfigMap 概述
Configmap 是 k8s 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value键值对 的形式保存,也可通过 文件 的形式保存。
- Configmap 是 k8s 中的资源, 相当于配置文件,可以有一个或者多个 Configmap;
- Configmap 可以做成 Volume,k8s pod 启动之后,通过 volume 形式映射到容器内部指定目录上;
- 容器中应用程序按照原有方式读取容器特定目录上的配置文件;
- 在容器看来,配置文件就像是打包在容器内部特定目录,整个过程对应用没有任何侵入。
configmap 注入方式有两种:
- 第一种:将 configMap 做为存储卷;
- 第二种:将 configMap 通过 env字段 中 configMapKeyRef变量 注入到容器中。
16.1.2 ConfigMap 能解决什么问题?
我们在部署服务的时候,每个服务都有自己的配置文件,如果一台服务器上部署多个服务:nginx、tomcat、apache等,那么这些配置都存在这个节点上,假如一台服务器不能满足线上高并发的要求,需要对服务器扩容,扩容之后的服务器还是需要部署多个服务:nginx、tomcat、apache,新增加的服务器上还是要管理这些服务的配置,如果有一个服务出现问题,需要修改配置文件,每台物理节点上的配置都需要修改,这种方式肯定满足不了线上大批量的配置变更要求。 所以,k8s 中引入了 Configmap 资源对象,可以当成 volume 挂载到 pod中,实现统一的配置管理。
16.1.3 ConfigMap 应用场景
16.1.3.1 场景1-部署应用
使用 k8s 部署应用,当你将应用配置写进代码中,更新配置时也需要打包镜像,configmap 可以将配置信息和 docker镜像 解耦,以便实现镜像的可移植性和可复用性,因为一个 configMap 其实就是一系列配置信息的集合,可直接注入到 Pod 中给容器使用。
16.1.3.2 场景2-使用微服务架构
使用微服务架构的话,存在多个服务共用配置的情况 ,如果每个服务中单独一份配置的话,那么更新配置就很麻烦,使用configmap 可以友好的进行配置共享。
16.1.4 ConfigMap 局限性
ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。如果你需要保存超出此尺寸限制的数据,可以考虑挂载存储卷或者使用独立的数据库或者文件服务。
16.1.5 cm字段
bash
]# kubectl explain cm
apiVersion <string>
kind <string>
metadata <ObjectMeta>
data <map[string]string>
# 以键值对的形式存储非二进制配置数据(适用于普通文本配置)
binaryData <map[string]string>
# 以Base64编码的形式存储二进制数据(适用于图片、证书等非文本文件)
immutable <boolean>
# 将 ConfigMap 标记为不可变,默认为 true,反之为 false
# `data` `binaryData`两者不可包含相同的键名,否则创建时会报错
16.2 ConfigMap 创建方式
16.2.1 方式1:命令行直接创建
bash
[root@k8s-master1 ~]# kubectl create configmap tomcat-cm --from-literal=tomcat_port=8080 --from-literal=server_name=tomcat
[root@k8s-master1 ~]# kubectl get cm
tomcat-cm 2 6s
[root@k8s-master1 ~]# kubectl describe cm tomcat-cm
server_name:
----
tomcat
tomcat_port:
----
8080

16.2.2 方式2:命令行指定文件创建
bash
[root@k8s-master1 ~]# kubectl create cm nginx-vhosts-conf --from-file=nginx=./nginx_vhosts.conf
[root@k8s-master1 ~]# kubectl get cm
nginx-vhosts-conf 1 14s
[root@k8s-master1 ~]# kubectl describe cm nginx-vhosts-conf
nginx:
----
server {
listen 80;
server_name www.kaser.com;
root /usr/local/nginx/conf/conf.d/
}

16.2.3 方式3:命令行指定目录创建
bash
[root@k8s-master1 ~]# mkdir cm_test
[root@k8s-master1 ~]# cd cm_test/
[root@k8s-master1 cm_test]# echo "server-id=1" > sql_master.cnf
[root@k8s-master1 cm_test]# echo "server-id=2" > sql_slave.cnf
[root@k8s-master1 cm_test]# ls
sql_master.cnf sql_slave.cnf
[root@k8s-master1 cm_test]# kubectl create cm sql-config-dir --from-file=/root/cm_test
[root@k8s-master1 cm_test]# kubectl get cm
sql-config-dir 2 26s
[root@k8s-master1 cm_test]# kubectl describe cm sql-config-dir
sql_master.cnf:
----
server-id=1
sql_slave.cnf:
----
server-id=2

16.2.4 方式4:编写 yaml 文件创建
bash
[root@k8s-master1 ~]# vim mysql-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
test_sql_master.cnf: | # 写文件,后面必须要有 |
[mysqld]
log-bin
log_bin_trust_function_creators=1
lower_case_table_names=1
test_sql_slave.cnf: |
[mysqld]
super-read-only
log_bin_trust_function_creators=1
[root@k8s-master1 ~]# kubectl apply -f mysql-cm.yaml
[root@k8s-master1 ~]# kubectl get cm
mysql 2 6s
[root@k8s-master1 ~]# kubectl describe cm mysql
test_sql_master.cnf:
----
[mysqld]
log-bin
log_bin_trust_function_creators=1
lower_case_table_names=1
test_sql_slave.cnf:
----
[mysqld]
super-read-only
log_bin_trust_function_creators=1

16.3 ConfigMap 使用方式
16.3.1 方式1:环境变量configMapKeyRef注入
优先使用
env+valueFrom
- 显式声明依赖的配置项,提高可读性和可维护性
- 避免因 ConfigMap 新增无关键导致意外行为
bash
]# kubectl explain pod.spec.containers.env
name <string> -required-
value <string>
valueFrom <EnvVarSource>
# value 和 valueFrom 不能同时使用,二者互斥。
[root@k8s-master1 ~]# kubectl explain pod.spec.containers.env.valueFrom
configMapKeyRef <ConfigMapKeySelector>
fieldRef <ObjectFieldSelector>
resourceFieldRef <ResourceFieldSelector>
secretKeyRef <SecretKeySelector>
[root@k8s-master1 ~]# kubectl explain pod.spec.containers.env.valueFrom.configMapKeyRef
key <string> -required-
name <string>
optional <boolean>
# 是否可选:
# • false(默认):ConfigMap 或键不存在时,Pod 创建失败
# • true:ConfigMap/键不存在时忽略该环境变量,Pod 仍可创建
bash
[root@k8s-master1 ~]# vim cmkr-sql-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cmkr-sql
labels:
app: mysql
data:
log: "1"
lower: "1"
[root@k8s-master1 ~]# kubectl apply -f cmkr-sql-cm.yaml
[root@k8s-master1 ~]# kubectl get cm
cmkr-sql 2 27s
[root@k8s-master1 ~]# kubectl describe cm cmkr-sql
log:
----
1
lower:
----
1
[root@k8s-master1 ~]# vim mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 3600"]
env:
- name: log_bin
valueFrom:
configMapKeyRef:
name: cmkr-sql
key: log
- name: lower_bin
valueFrom:
configMapKeyRef:
name: cmkr-sql
key: lower
[root@k8s-master1 ~]# kubectl apply -f mysql-pod.yaml
[root@k8s-master1 ~]# kubectl get pods | grep mysql
mysql-pod 1/1 Running
[root@k8s-master1 ~]# kubectl exec mysql-pod -it -c mysql -- /bin/sh
/ # printenv | grep log
log_bin=1
/ # printenv | grep lower
lower_bin=1
16.3.2 方式2:环境变量envfrom注入
谨慎使用 envFrom:
- 仅当确认 ConfigMap 所有键均需导入且命名合法时使用
- 建议配合 prefix 避免命名冲突:
bash
]# kubectl explain pod.spec.containers.env
name <string> -required-
value <string>
valueFrom <EnvVarSource>
# value 和 valueFrom 不能同时使用,二者互斥。
[root@k8s-master1 ~]# kubectl explain pod.spec.containers.env.valueFrom
configMapKeyRef <ConfigMapKeySelector>
fieldRef <ObjectFieldSelector>
resourceFieldRef <ResourceFieldSelector>
secretKeyRef <SecretKeySelector>
[root@k8s-master1 ~]# kubectl explain pod.spec.containers.envFrom
configMapRef <ConfigMapEnvSource>
prefix <string>
secretRef <SecretEnvSource>
bash
[root@k8s-master1 ~]# vim cmkr-sql-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cmkr-sql
labels:
app: mysql
data:
log: "1"
lower: "1"
[root@k8s-master1 ~]# kubectl apply -f cmkr-sql-cm.yaml
[root@k8s-master1 ~]# kubectl get cm
cmkr-sql 2 27s
[root@k8s-master1 ~]# vim envfrom-mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 3600"]
envFrom:
- configMapRef:
name: cmkr-sql
[root@k8s-master1 ~]# kubectl apply -f envfrom-mysql-pod.yaml
[root@k8s-master1 ~]# kubectl get pods
mysql-pod 1/1 Running 0 23s
[root@k8s-master1 ~]# kubectl exec -it mysql-pod -c mysql -- /bin/sh
/ # printenv | grep log
log=1
/ # printenv | grep lower
lower=1
16.3.3 方式3:volume卷挂载注入
bash
[root@k8s-master1 ~]# vim volume-sql-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: volume-sql
labels:
app: mysql
data:
log: "1"
lower: "1"
my.cnf: |
[HelloWord]
xixixixixi
[root@k8s-master1 ~]# kubectl apply -f volme-sql-cm.yaml
[root@k8s-master1 ~]# kubectl get cm
volume-sql 3 3s
[root@k8s-master1 ~]# kubectl describe cm volume-sql
log:
----
1
lower:
----
1
my.cnf:
----
[HelloWord]
xixixixixi
[root@k8s-master1 ~]# vim volume-cm-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-cm-pod
spec:
containers:
- name: buxybox
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 3600"]
volumeMounts:
- name: vol-cm
mountPath: /tmp/config
volumes:
- name: vol-cm
configMap:
name: volume-sql
[root@k8s-master1 ~]# kubectl apply -f volume-cm-pod.yaml
[root@k8s-master1 ~]# kubectl get pods
volume-cm-pod 1/1 Running 0 4s
[root@k8s-master1 ~]# kubectl exec -it volume-cm-pod -c buxybox -- /bin/sh
/ # ls -l /tmp/config/
total 0
lrwxrwxrwx 1 root root 10 Jan 29 01:26 log -> ..data/log
lrwxrwxrwx 1 root root 12 Jan 29 01:26 lower -> ..data/lower
lrwxrwxrwx 1 root root 13 Jan 29 01:26 my.cnf -> ..data/my.cnf
/ # cat /tmp/config/log
1/ #
/ # cat /tmp/config/lower
1/ #
/ # cat /tmp/config/my.cnf
[HelloWord]
xixixixixi
/ #
16.4 ConfigMap 热更新
volume卷挂载注入的方式才可
bash
[root@k8s-master1 ~]# kubectl edit cm volume-sql
......
apiVersion: v1
data:
log: "6" # 1 --> 6
lower: "123123" # 2 --> 123123
......
:wq # 保存退出
[root@k8s-master1 ~]# kubectl get pods
volume-cm-pod 1/1 Running 0 7m32s
# 进入容器查看是否改变
[root@k8s-master1 ~]# kubectl exec -it volume-cm-pod -- /bin/sh
/ # cat /tmp/config/log
6/ #
/ # cat /tmp/config/lower
123123/ #
/ #
注意:
- 更新 ConfigMap 后:
- 使用该 ConfigMap 挂载的 Env 不会同步更新
- 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新