一、什么是ConfigMap

       ConfigMap翻译过来即为“配置字典”,在实际的生产环境中,应用程序配置经常需要且又较为复杂,参数、config文件、变量等如果直接打包到镜像中,将会降低镜像的可移植性,因此期望有一种方式可以将配置从应用程序中解耦出来,ConfigMap正是在此背景下诞生的,它用于保存配置数据的键值(key-value)对,可以用来保存单个属性,也可以用来保存配置文件。ConfigMap 跟 secret 很类似,但它可以更方便地处理不包含敏感信息的字符串。

二、适用场景?

     适用于存储不包含敏感信息的变量、属性或文件。

     比如在一个集群中,可将应用程序所需的参数、变量、配置文件存放到ConfigMap中,在开发、测试和生产环境将同1个ConfigMap(前提是同1个namespace)挂载到Pod中,这样既可以实现配置和容器镜像的分离,也可方便的实现统一管理,比如可以利用其热更新的机制,修改完成后会同步到挂载了此ConfigMap的所有Pod中。

三、ConfigMap的创建

1、文件目录创建

支持以目录的方式创建ConfigMap,其中文件名将作为key,文件内容作为value,有几个文件将会存在几个键值对,如下图,先创建1个目录,此目录下存在两个文件

[root@k8s-master confdirtest]# ls
conftest1.txt  conftest.txt
[root@k8s-master consecret]# kubectl create configmap con-dir-test --from-file=confdirtest/
configmap/con-dir-test created

其格式为kubectl creat configmap "configmap的名字" --from-file="目录"/,这种适用于将某个目录下的所有文件作为key,文件内容作为value进行创建。

[root@k8s-master consecret]# kubectl get cm | grep con-dir-test
con-dir-test          2      20s
[root@k8s-master consecret]# kubectl describe cm con-dir-test
Name:         con-dir-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
conftest.txt:
----
name=this is test                #文件conftest.txt的内容

conftest1.txt:
----
name=this is test1               #文件conftest1.txt的内容

Events:  <none>

2、文件方式创建

     --from-file的方式除了用于目录的方式之外,同样还适用于指定文件的方式进行创建,其中文件名称将作为key名称,文件内容作为value,如下所示,先建立一个配置文件testconf.cfg

[root@k8s-master consecret]# cat testconf.cfg
listenport=8080
name=testconf
serverip=10.0.0.1

接下里将其生成1个configmap文件,如下所示,其格式为:kubectl create configmap "configmap名称" --from-file=“文件名”

[root@k8s-master consecret]# kubectl create configmap conf-file-test --from-file=testconf.cfg
configmap/conf-file-test created
[root@k8s-master consecret]# kubectl describe cm conf-file-test
Name:         conf-file-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
testconf.cfg:
----
listenport=8080
name=testconf
serverip=10.0.0.1

Events:  <none>

此方式适用于将某个或多个配置文件生成同一个configmap文件的场景

3、直接创建key-value 

 可直接指定需要data的key-value键值对进行创建,如下所示,使用"--from-literal"选项可在命令行直接创建configmap对象,若是创建多个,可重复多次进行创建,但是注意其key的名称不可重复,这种方式适用于直接指定key-value的方式进行创建。

[root@k8s-master consecret]# kubectl create configmap con-key-value --from-literal=key1=name1
configmap/con-key-value created
[root@k8s-master consecret]# kubectl describe cm con-key-value
Name:         con-key-value
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
key1:
----
name1
Events:  <none>
[root@k8s-master consecret]# kubectl create configmap con-key-value-test --from-literal=class.1=202 --from-literal=class.2=502
configmap/con-key-value-test created
[root@k8s-master consecret]# kubectl describe cm con-key-value-test
Name:         con-key-value-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
class.1:
----
202
class.2:
----
502
Events:  <none>

4、导入环境变量创建

 可将环境变量以--from-env-file的方式进行创建,其格式为:kubectl crete configmap "configmap的名称" --from-env=file=环境变量文件.env

[root@k8s-master consecret]# echo -e "testenv1=1\testenv2=2" | tee configtest.env
testenv1=1    estenv2=2
[root@k8s-master consecret]# kubectl create configmap conf-env-test --from-env-file=configtest.env
configmap/conf-env-test created
[root@k8s-master consecret]# kubectl describe cm conf-env-test
Name:         conf-env-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
testenv1:
----
1        estenv2=2
Events:  <none>

四、ConfigMap的使用

1、热更新(目录形式挂载)

首先先成功创建一个ConfigMap

[root@k8s-master consecret]# cat conf-delete-test.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: zltest-conf-delete
  namespace: default
data:
  zltest.show: delete-test
[root@k8s-master consecret]# kubectl delete deployment dep-deleye-conf-test
deployment.apps "dep-deleye-conf-test" deleted
[root@k8s-master consecret]# kubectl create -f conf-delete-test.yaml
configmap/zltest-conf-delete created
[root@k8s-master consecret]# kubectl get cm | grep zltest-conf-delete
zltest-conf-delete    1      44s

将其以卷目录形式挂载到Pod容器中,创建一个deployment

[root@k8s-master zhanglei]# cat dep-delete-con.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-deleye-conf-test
  namespace: default
  labels:
    app: nginx-delete-conf-test
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-deployment-delete
  template:
    metadata:
      labels:
        app: nginx-deployment-delete
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - name: testconf
          containerPort: 80
        volumeMounts:
        - name: nginxconf
          mountPath: /data/deletetest
      volumes:
      - name: nginxconf
        configMap:
          name: zltest-conf-delete
[root@k8s-master zhanglei]# kubectl create -f dep-delete-con.yaml
deployment.apps/dep-deleye-conf-test created
[root@k8s-master zhanglei]# kubectl get deployment |grep dep-deleye-conf-test
dep-deleye-conf-test   2/2     2            2           56s

执行以上的步骤后,名为zltest-conf-delete的ConfigMap已被成功挂载到Pod的容器中,如果在运行的过程中,修改下ConfigMap的文件,会出现怎样的情况呢?先edit下ConfigMap,增加一个键值对

[root@k8s-master zhanglei]# kubectl describe cm zltest-conf-delete
Name:         zltest-conf-delete
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
zltest.show:
----
delete-test
Events:  <none>
[root@k8s-master consecret]# kubectl edit cm zltest-conf-delete

# 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:
  zltest.show: delete-test
  addkey: addvalue                      # 新增的键值对
kind: ConfigMap
metadata:
  creationTimestamp: "2020-07-23T11:29:11Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:zltest.show: {}
    manager: kubectl
    operation: Update
    time: "2020-07-23T11:29:11Z"
  name: zltest-conf-delete
  namespace: default

[root@k8s-master consecret]# kubectl edit cm zltest-conf-delete
configmap/zltest-conf-delete edited

验证下新增的键值对是否会同步热更新到Pod的容器中,通过exec的方式登录到容器中进行查看,新添加的键值对以同步更新到Pod的容器中,

不需要以容器进行额外的操作,这是卷目录的形式挂载的热更新,但是若是通过环境变量env或者通过文件的形式挂载到容器中,是不会支持热更新的,这里需要注意下。

[root@k8s-master consecret]# kubectl exec -it dep-deleye-conf-test-869747b77f-67w9h -- bash
root@dep-deleye-conf-test-869747b77f-67w9h:/# cd data
root@dep-deleye-conf-test-869747b77f-67w9h:/data# ls
deletetest
root@dep-deleye-conf-test-869747b77f-67w9h:/data# cd deletetest/
root@dep-deleye-conf-test-869747b77f-67w9h:/data/deletetest# ls
addkey    zltest.show

如果删除已将挂载到Pod中的容器的ConfigMap会出现怎样的情况呢,先删除ConfigMap,然后再次登录到容器中进行验证

[root@k8s-master consecret]# kubectl delete cm zltest-conf-delete
configmap "zltest-conf-delete" deleted
[root@k8s-master consecret]# kubectl exec -it dep-deleye-conf-test-869747b77f-67w9h -- bash
root@dep-deleye-conf-test-869747b77f-67w9h:/data/deletetest# ls
addkey    zltest.show

若未对Pod进行删除的操作,删除ConfigMap成功,但是挂载到容器的目录依然会存在,并不会一起被删除,此时若删除Pod,副本控制器会重新拉

起1个Pod,会创建成功吗?接下来验证下

[root@k8s-master consecret]# kubectl get pod | grep dep-deleye-conf-test
dep-deleye-conf-test-869747b77f-kgzqt   1/1     Running             0          134m
dep-deleye-conf-test-869747b77f-mf6vc   0/1     ContainerCreating   0          2m13s

重新拉起Pod一直会是ContainerCreating状态,详细查看下原因

[root@k8s-master consecret]# kubectl describe pod dep-deleye-conf-test-869747b77f-mf6vc
Name:           dep-deleye-conf-test-869747b77f-mf6vc
Namespace:      default
Priority:       0
Node:           k8s-master/192.168.126.129
Start Time:     Thu, 23 Jul 2020 21:47:00 +0800
Labels:         app=nginx-deployment-delete
                pod-template-hash=869747b77f
Annotations:    <none>
Status:         Pending
IP:
IPs:            <none>
Controlled By:  ReplicaSet/dep-deleye-conf-test-869747b77f
Containers:
  nginx:
    Container ID:
    Image:          nginx:latest
    Image ID:
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /data/deletetest from nginxconf (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-74s86 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  nginxconf:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      zltest-conf-delete
    Optional:  false
  default-token-74s86:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-74s86
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason       Age                From                 Message
  ----     ------       ----               ----                 -------
  Normal   Scheduled    <unknown>          default-scheduler    Successfully assigned default/dep-deleye-conf-test-869747b77f-mf6vc to k8s-master
  Warning  FailedMount  11s (x8 over 75s)  kubelet, k8s-master  MountVolume.SetUp failed for volume "nginxconf" : configmap "zltest-conf-delete" not found

可以看到是FailedMount,volume异常了,K8S中删除的时候是不会判断这个依赖关系的,因此在生产的实际使用中,这点还是需要注意。

2、通过环境变量引入

    除了通过volume的方式挂载到容器中使用之外,还可将configmap作为环境变量的方式引入到容器中,这里以con-key-value-test作为例子

[root@k8s-master consecret]# kubectl get cm |grep con-key-value-test
con-key-value-test    2      34m
[root@k8s-master zhanglei]# cat dep-conf-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-conf-env-test
  namespace: default
  labels:
    app: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        #  command: [ "/bin/bash","-c","env" ]
        ports:
        - containerPort: 80
        env:
        - name: test-env1-conf                        # 环境变量名称1
          valueFrom:
            configMapKeyRef:
              name: con-key-value-test
              key: class.1                            # 指定key名称即可
        - name: test-env2-conf                        # 环境变量名称2
          valueFrom:
            configMapKeyRef:
              name: con-key-value-test                # 引入configmap
              key: class.2
[root@k8s-master zhanglei]# kubectl create -f dep-conf-test.yaml
deployment.apps/nginx-conf-env-test created
[root@k8s-master zhanglei]# kubectl get pod |grep env-test
nginx-conf-env-test-56f5c9b88-xt86x        1/1     Running            0          34s

 可以看到Pod处于running的状态,登入到容器验证下以存在环境变量,如下所示,验证成功,值得注意的是,这种方式相当于新生成的环境变量指向configmap中的key所对应的value

root@nginx-conf-env-test-56f5c9b88-xt86x:/# ls
bin   docker-entrypoint.d   home   media  proc    sbin  tmp
boot  docker-entrypoint.sh  lib    mnt      root    srv   usr
dev   etc            lib64  opt      run    sys   var
root@nginx-conf-env-test-56f5c9b88-xt86x:/# env
test-env2-conf=502                   # 环境变量=原class.1的value
test-env1-conf=202

五、总结

 configmap是K8s中重要的配置管理方式,本文介绍了configmap的概念、使用场景、多种创建的方式、最后介绍了如何进行使用,希望能给大家的学习带来一点帮助。

 作者简介:云计算容器\Docker\K8s\Serverless方向产品经理,学点技术,为更好地设计产品。

08-28 04:50