相信很多朋友都听说过配置中心这种东西,应用在运行的时候会从配置中心读取不同环境的配置信息,以达到相同的应用在不同环境运行的目的。这种低耦合的应用运行方式也在k8s中得到了应用。这一节我们就来学习k8s中的配置中心:ConfigMap。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
文章目录
ConfigMap
ConfigMap是k8s集群中一个单独的资源,用来存储键值对形式的配置信息。通过将业务代码和配置信息隔离开来,可以减少容器的复杂度,同时让容器能动态部署到不同环境(开发,测试,生产),以及在不影响容器运行下进行配置的热更新。
但是因为ConfigMap中的数据都是用明文存储,不应该存放密码等私密信息。私密信息可以通过下一节要学习的Secret来进行存储。
下面这个动态图是一个很简单的ConfigMap工作原理的说明
- 首先,需要准备多个不同ConfigMap,这里是一个环境一份
- 然后,根据需要,将合适环境的ConfigMap加入到k8s集群中
- 最后,pod中的容器去ConfigMap中检索需要的内容
生成ConfigMap
这里分两种情况来看。如果是类似nginx.conf这种大篇的配置信息文件,考虑用文件或者文件夹去生成ConfigMap,方便后期维护,这时ConfigMap的key是文件名,value是文件内容;如果只是三两个环境变量,考虑用yaml文件或者直接命令行生成ConfigMap,简单快捷,这时ConfigMap是键值对和yaml文件或者命令行中的一致。
这里将这两种方式都演示一下。
根据文件或者文件夹生成ConfigMap
创建如下两个文件
[root@k8s-master Configs]# ll
total 8
-rw-r--r--. 1 root root 30 May 10 10:42 file-1
-rw-r--r--. 1 root root 30 May 10 10:42 file-2
[root@k8s-master Configs]# cat file-1
config1=value1
config2=value2
[root@k8s-master Configs]# cat file-2
config3=value3
config4=value4
然后通过下面的命令根据单个文件或者文件夹生成ConfigMap
kubectl create configmap NAME --from-file=xxx
例如单个文件
[root@k8s-master Configs]# kubectl create configmap test-cm-1 --from-file=file-1
configmap/test-cm-1 created
[root@k8s-master Configs]# kubectl get configmap
NAME DATA AGE
test-cm-1 1 12s
或者根据文件夹
[root@k8s-master Configs]# kubectl create configmap test-cm-2 --from-file=.
configmap/test-cm-2 created
[root@k8s-master Configs]# kubectl get configmap
NAME DATA AGE
test-cm-1 1 85s
test-cm-2 2 3s
之后就可以去查看ConfigMap的具体信息了,可以用describe命令
[root@k8s-master Configs]# kubectl describe cm test-cm-2
Name: test-cm-2
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
file-1:
----
config1=value1
config2=value2
file-2:
----
config3=value3
config4=value4
Events: <none>
或者是直接用yaml输出的方式
[root@k8s-master Configs]# kubectl get configmap test-cm-2 -o yaml
apiVersion: v1
data:
file-1: |
config1=value1
config2=value2
file-2: |
config3=value3
config4=value4
kind: ConfigMap
metadata:
creationTimestamp: "2020-05-10T02:47:33Z"
name: test-cm-2
namespace: default
resourceVersion: "1385209"
selfLink: /api/v1/namespaces/default/configmaps/test-cm-2
uid: bcee7bcb-4453-4f26-9a84-39bb5f18eac6
当然也可以用这种方法去对集群中其余资源查看yaml信息,例如 kubectl get pod xxx -o yaml
可以看到data
字段下声明了ConfigMap中存储的信息,key是文件名,value是文件的内容
根据yaml文件或命令行生成ConfigMap
上面我们将ConfigMap信息用yaml格式展示出来,也可以用相同的格式去自定义自己的yaml文件。
创建yaml文件test-configmap.yaml
如下
apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm-3
data:
database: mongodb
database_uri: mongodb://localhost:27017
keys: |
image.public.key=771
rsa.public.key=42
创建出新的ConfigMap
[root@k8s-master Configs]# vim test-configmap.yaml
[root@k8s-master Configs]# kubectl apply -f test-configmap.yaml
configmap/test-cm-3 created
[root@k8s-master Configs]# kubectl describe cm test-cm-3
Name: test-cm-3
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","data":{"database":"mongodb","database_uri":"mongodb://localhost:27017","keys":"image.public.key=771\nrsa.public.key=42...
Data
====
database:
----
mongodb
database_uri:
----
mongodb://localhost:27017
keys:
----
image.public.key=771
rsa.public.key=42
Events: <none>
也可以直接用下面的命令行格式去直接生成ConfigMap中的键值对
kubectl create configmap NAME --from-literal=KEY=VALUE --from-literal=KEY=VALUE ...
例如
[root@k8s-master Configs]# kubectl create configmap test-cm-4 --from-literal=name=xiaofu --from-literal=password=123456
configmap/test-cm-4 created
[root@k8s-master Configs]# kubectl get cm
NAME DATA AGE
test-cm-1 1 19m
test-cm-2 2 18m
test-cm-3 3 6m13s
test-cm-4 2 10s
查询一下
[root@k8s-master Configs]# kubectl describe cm test-cm-4
Name: test-cm-4
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
name:
----
xiaofu
password:
----
123456
Events: <none>
容器使用ConfigMap
创建出来的ConfigMap是为了给pod内的容器使用。下面由浅入深,看看容器使用ConfigMap的三种方式。
以下所有yaml文件托管在我的Github仓库
使用ConfigMap代替环境变量
以上面创建的test-cm-3
和test-cm-4
为例,将键值对添加到容器的环境变量。
通过yaml文件test-yaml-env.yaml
来创建测试pod
apiVersion: v1
kind: Pod
metadata:
name: test-configmap-env
spec:
containers:
- name: cm-container-2
image: alpine
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "env" ]
env:
- name: DATABASE
valueFrom:
configMapKeyRef:
name: test-cm-3
key: database
- name: DATABASE_URI
valueFrom:
configMapKeyRef:
name: test-cm-3
key: database_uri
envFrom:
- configMapRef:
name: test-cm-4
restartPolicy: Never
这里把两种方式都集中在了一个yaml文件中:
- 第一种是利用
env
字段,分开指明每个环境变量的名字和内容。configMapKeyRef下配置ConfigMap的名字和key,返回的值赋值给env下的name字段做为容器的环境变量 - 第二种是利用
envFrom
字段,直接将ConfigMap内容全部导入环境变量
成功创建pod
[root@k8s-master Configs]# kubectl apply -f test-configmap-env.yaml
pod/test-configmap-env created
[root@k8s-master Configs]# kubectl get pod test-configmap-env
NAME READY STATUS RESTARTS AGE
test-configmap-env 0/1 Completed 0 21s
如果不清楚这里为什么是Completed状态而不是Running的,或者出现CrashLoopBackOff报错的,可以参考我的这篇博客
之后查看pod的日志,可以看到shell命令打印的环境变量
[root@k8s-master Configs]# kubectl logs test-configmap-env
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=test-configmap-env
SHLVL=1
HOME=/root
MYNGINX_SERVICE_SERVICE_PORT_HTTP=8080
DATABASE=mongodb
MYNGINX_SERVICE_SERVICE_HOST=10.97.205.233
MYNGINX_SERVICE_PORT_8080_TCP_ADDR=10.97.205.233
MYNGINX_SERVICE_PORT_8080_TCP_PORT=8080
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
MYNGINX_SERVICE_PORT_8080_TCP_PROTO=tcp
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYNGINX_SERVICE_PORT=tcp://10.97.205.233:8080
MYNGINX_SERVICE_SERVICE_PORT=8080
KUBERNETES_PORT_443_TCP_PORT=443
password=123456
KUBERNETES_PORT_443_TCP_PROTO=tcp
MYNGINX_SERVICE_PORT_8080_TCP=tcp://10.97.205.233:8080
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
name=xiaofu
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
DATABASE_URI=mongodb://localhost:27017
这里可以看到前面配置的下面四个环境变量
- DATABASE=mongodb
- DATABASE_URI=mongodb://localhost:27017
- name=xiaofu
- password=123456
使用ConfigMap设置命令行参数
既然可以将ConfigMap的内容传递给容器,那么就可以在运行容器内命令的时候使用这些环境变量。
通过yaml文件test-configmap-cmd.yaml
创建一个pod
apiVersion: v1
kind: Pod
metadata:
name: test-configmap-cmd
spec:
containers:
- name: cm-container-2
image: alpine
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "echo please login to ${DATABASE_URI} with username ${name} and password ${password}" ]
env:
- name: DATABASE
valueFrom:
configMapKeyRef:
name: test-cm-3
key: database
- name: DATABASE_URI
valueFrom:
configMapKeyRef:
name: test-cm-3
key: database_uri
envFrom:
- configMapRef:
name: test-cm-4
restartPolicy: Never
注意这里的echo和后面的打印内容不要分开,不要写成[“echo”,“xxx”]
创建好后查看日志
[root@k8s-master Configs]# kubectl apply -f test-configmap-cmd.yaml
pod/test-configmap-cmd created
[root@k8s-master Configs]# kubectl logs test-configmap-cmd
please login to mongodb://localhost:27017 with username xiaofu and password 123456
成功在命令中使用了环境变量。
这就为我们针对不同环境在相同容器中运行不同配置的命令提供了可能,只需要将有差异的配置项单独传递给容器的环境变量,然后跑命令的时候引用环境变量即可。
通过ConfigMap热更新配置文件
下面来进行最难,也是最实用得一部分。
因为这里要使用到数据卷Volume,后面我们会详细学习。不过接触过Docker得朋友对Volume应该不陌生,就是将容器内得一个目录和本地得一个目录进行绑定,修改的内容两边实时更新。k8s中得volume和Docker中的作用一样。
通过yaml文件test-configmap-volume.yaml
将前面用文件形式创建的ConfigMap用volume的形式对应到容器中
apiVersion: v1
kind: Pod
metadata:
name: test-configmap-volume
spec:
containers:
- name: cm-container-2
image: alpine
command: ["/bin/sh","-c","ping 8.8.8.8"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: test-cm-2
restartPolicy: Never
这里是用test-cm-2
这个ConfigMap创建一个叫config-volume
的volume,其对应到容器内的/etc/config
这个目录。ConfigMap的键值对,也就是文件名和其内容,都会同步到容器内的/etc/config
目录下。
这里一直 ping 8.8.8.8 就是为了让容器不退出,方便后面我们进入容器查看文件内容
创建好后进入容器,查看文件
[root@k8s-master Configs]# kubectl apply -f test-configmap-volume.yaml
pod/test-configmap-volume created
[root@k8s-master Configs]# kubectl exec test-configmap-volume -it -- /bin/sh
/ # cd /etc/config
/etc/config # ls -al
total 0
drwxrwxrwx 3 root root 87 May 10 06:13 .
drwxr-xr-x 1 root root 20 May 10 06:13 ..
drwxr-xr-x 2 root root 34 May 10 06:13 ..2020_05_10_06_13_28.533741647
lrwxrwxrwx 1 root root 31 May 10 06:13 ..data -> ..2020_05_10_06_13_28.533741647
lrwxrwxrwx 1 root root 13 May 10 06:13 file-1 -> ..data/file-1
lrwxrwxrwx 1 root root 13 May 10 06:13 file-2 -> ..data/file-2
/etc/config # cat file-1
config1=value1
config2=value2
/etc/config # cat file-2
config3=value3
config4=value4
这里先不用管软连接,后面学习k8s中的volume时候再详细说。这里的效果就是将本地的文件file-1和file-2同步到集群的ConfigMap,然后通过volume同步到容器的某个文件。
此时我直接用下面的命令格式编辑ConfigMap
kubectl edit configmap NAME
将value1和value2变为value3和value4,然后:wq
保存
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
file-1: |
config1=value3
config2=value4
file-2: |
config3=value3
config4=value4
kind: ConfigMap
metadata:
creationTimestamp: "2020-05-10T02:47:33Z"
name: test-cm-2
namespace: default
resourceVersion: "1385209"
selfLink: /api/v1/namespaces/default/configmaps/test-cm-2
uid: bcee7bcb-4453-4f26-9a84-39bb5f18eac6
此时容器内的文件内容直接热更新了
/etc/config # cat file-1
config1=value3
config2=value4
当然也可以直接本地更新好文件,同步到ConfigMap,这里就不演示了,大家可以自己尝试一下。
但是值得说明的是,虽然配置文件可以热更新,并不代表容器内的应用也支持热更新。例如nginx的配置文件修改了,还是需要重启一下nginx才会去重新读取配置信息。这就是具体应用的问题了,和我们这里的ConfigMap无关。
同时还要注意,修改了ConfigMap,其对应的容器内环境变量并不会热更新。
总结
本节的知识点整理一下
- 4总创建ConfigMap的方式:文件/文件夹/yaml/命令行
- 3种典型的ConfigMap使用场景:环境变量/命令参数/volume热更新
需要注意
- 修改ConfigMap,环境变量不会热更新,只有volume对应的文件会热更新
- 文件热更新不代表容器内应用会去实时读取新的文件,要具体应用具体分析
这一节学习了ConfigMap,这是一种明文方式存储信息的k8s组件资源。下一节我们一起来学习下secret,来用加密的方式保存一些密码等敏感信息。