13.ConfigMap资源之该用-from-literal还是-from-file

1.什么是ConfigMap?

ConfigMap(简称CM)允许你把配置文件从image镜像本地中解耦出来,来保持集装箱化应用的便携性。
ConfigMap可以通过监听在配置文件、命令行参数、环境变量等其他配置的架构中,然后在你的pod容器和系统组件运行时,达到配置的加载功能。
同时ConfigMap也建议将那些,可以使得你更好的进行变更和管理配置文件的配置,从pod和系统组件中剥离出来。

ConfigMap存储配置信息是很有用的,由于它是非加密性的配置,我强烈建议大家只保存非敏感的配置信息。

2.应用场景定义

正如上面所说,ConfigMap是非加密性的配置保存,你应该牢记,切勿在ConfigMap中保存敏感信息;
如果你需要保存敏感信息,建议使用Secret。

下一章节讲secret

3.如何创建一个ConfigMap

和其他基础对象、控制器类似,你也可以通过两种方式来申明一个ConfigMap:

1)命令行方式:

kubectl create configmap [NAME] [DATA]:

例如:kubectl create cm filebeat-cfg -n config --from-literal=redis_host="redis.test.com" --from-literal=log_level=“info”

你需要注意的是,通过命令行申明的方式,内部也细分了两种:

从字面读取(–from-literal)和从目录(–from-file)读取,下面我将给你讲解他们是如何使用的。
1.–from-literal

从字面读取的好处就是快速和便捷,你可以通过关键字参数的形式(**kargs),将配置信息直接传递至configmap,然后在pod启动的时候进行加载;
同时这个方式的弊端就是,变量的更新是做不到的,毕竟环境变量的加载只有在容器运行时才起作用。

kubectl create cm filebeat-cfg -n config --from-literal=redis_host="redis.test.com" --from-literal=log_level=“info”

其中,redis_host是变量名,redis.test.com就是我们定义的变量值了,是不是一目了然了呢?

2.–from-file

从目录读取的好处就是,允许你更好的进行配置文件的管理。
你可以想象成传统架构下,我们是如何管理nginx的vhost的,又比如,在prometheus中,对于File SD(基于文件的服务发现)的使用,其实和此处都是一个道理。

kubectl create configmap nginx-cfg -n config --from-file=/root/mainfasts/conf.d/

--from-file指明需要从那个目录下面读取,目录下的文件只要按照一定格式就可以成功被加载到了。

你需要注意的是:

通过目录加载ConfigMap,其变量名是文件名,变量值是文件内容!!!

2)yaml配置文件方式:

当然,你也可以通过yaml的方式,像申明其他资源一样,去申明一个ConfigMap,这并没有什么不同,只要注意它的配置字段就好了。
你可以通过下面命令找到ConfigMap的配置字段信息:

kubectl explain configmap

我这里给了一个ingress的ConfigMap的参考配置文件:configmap-demo.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"ingress-nginx","app.kubernetes.io/part-of":"ingress-nginx"},"name":"nginx-configuration","namespace":"ingress-nginx"}}
  creationTimestamp: "2019-12-05T07:10:54Z"
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
  resourceVersion: "7097"
  selfLink: /api/v1/namespaces/ingress-nginx/configmaps/nginx-configuration
  uid: d9646c79-7c33-4afd-b3cc-03da176f6b53

4.如何使用一个ConfigMap

前面,我讲了很多都是如何去创建、申明一个ConfigMap,那创建好的配置,我们如何去使用呢?
接下来,我将为大家简单讲解,如何使用!最后会有更详细的实战环节。
1.–from-literal

如果你使用此方法进行申明,那你就得使用环境变量的方式,让容器读取到配置:

apiVersion: v1
kind: Pod
spec:
 containers:
 - name: nginx
   image: nginx
   env:
   -  name: nginx_host                
      valueFrom:
        configMapKeyRef:                 #从configmap中读取
          key: redis_host                #上面我们定义的变量名

具体字段配置,请查阅:

kubectl explain pod.spec.containers.env.name.valueFrom.configMapKeyRef

2.–from-file

通过目录的方式进行配置加载,你还需要配合使用volume进行配置的读取,具体如下所示

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-conf                 #自定义volumes的名字,这里是引用
      mountPath: /etc/nginx/conf.d/    #挂载到Pod中的路径
  volumes:
  - name: nginx-conf                 #自定义volumes的名字,这里是定义
    configMap:
      defaultMode: 0644
      name: nginx-cfg                 #configMap名
      items:
      - key: server1.conf                   #configMap中的变量名
        path: server1_new.conf                #期望以什么名字保存在Pod目录中

具体字段配置,请查阅:

kubectl explain pod.spec.volumes.configMap

5.实战-from-literal

再次提醒:此方式的环境变量,Pod生命周期内的配置变更是不会热加载的。

1.首先我们在config的namespace中,创建一个名为filebeat-cfg的configmap用于测试,具体如下

kubectl create cm filebeat-cfg -n config --from-literal=redis_host="redis.test.com" --from-literal=log_level=“info”

2.然后,检查CM(configmap简称)配置情况,是否被成功读取

[root@centos-1 dingqishi]# kubectl get cm filebeat-cfg -n config -o yaml
apiVersion: v1
data:
  log_level: info
  redis_host: redis.test.com
kind: ConfigMap
metadata:
  creationTimestamp: "2019-12-04T08:17:32Z"
  name: filebeat-cfg
  namespace: config
  resourceVersion: "70462"
  selfLink: /api/v1/namespaces/config/configmaps/filebeat-cfg
  uid: 2cc00197-60b6-4850-9c24-ef4f385ae058

3.编辑nginx-cmcmd.yaml,并使用valueFrom的方式来加载我们的配置变量

apiVersion: v1
kind: Pod
metadata:
 name: nginx
 namespace: config
spec:
 containers:
 - name: nginx
   image: nginx
   env:
   -  name: nginx_host
      valueFrom:
        configMapKeyRef:                 #从configmap中读取
          key: redis_host
          name: filebeat-cfg
   -  name: nginx_log_level
      valueFrom:
        configMapKeyRef:
          key: log_level
          name: filebeat-cfg
   -  name: nginx_static_value            #静态配置
      value: "123123"

4.最后apply -f启动Pod,并检查变量加载情况,发现已经能成功加载到变量了(nginx_host、nginx_log_level和nginx_static_value)

[root@centos-1 mainfasts]# kubectl exec -it nginx -n config -- /bin/sh
# printenv
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
nginx_host=redis.test.com
nginx_log_level=info
HOSTNAME=nginx
HOME=/root
PKG_RELEASE=1~buster
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
NGINX_VERSION=1.17.6
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
nginx_static_value=123123
KUBERNETES_PORT_443_TCP_PORT=443
NJS_VERSION=0.3.7
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/

5.接着,我们尝试修改变量值,看一下pod生命周期内,是否真的不会被读取到呢?
编辑(edit)配置文件,修改对应变量(redis_host: www.baidu.com)。

[root@centos-1 dingqishi]# kubectl edit cm filebeat-cfg -n config -o yaml
[root@centos-1 dingqishi]# kubectl get cm filebeat-cfg -n config -o yaml
apiVersion: v1
data:
  log_level: warning
  redis_host: www.baidu.com
kind: ConfigMap
metadata:
  creationTimestamp: "2019-12-04T08:17:32Z"
  name: filebeat-cfg
  namespace: config
  resourceVersion: "76435"
  selfLink: /api/v1/namespaces/config/configmaps/filebeat-cfg
  uid: 2cc00197-60b6-4850-9c24-ef4f385ae058

6.继续观察Pod内变量,发现真的没有改变,和我们预期一致,测试成功!

# printenv
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
nginx_host=redis.test.com
nginx_log_level=info
HOSTNAME=nginx
HOME=/root
PKG_RELEASE=1~buster
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
NGINX_VERSION=1.17.6
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
nginx_static_value=123123
KUBERNETES_PORT_443_TCP_PORT=443
NJS_VERSION=0.3.7
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/

7.删除Pod,重新apply,发现配置才会更新!

[root@centos-1 mainfasts]# kubectl delete -f pod-cfg.yaml 
pod "nginx" deleted
[root@centos-1 mainfasts]# kubectl apply -f pod-cfg.yaml 
pod/nginx created

[root@centos-1 mainfasts]# kubectl exec -it nginx -n config -- /bin/sh
# printenv
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
nginx_host=www.baidu.com
nginx_log_level=warning
HOSTNAME=nginx
HOME=/root
PKG_RELEASE=1~buster
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
NGINX_VERSION=1.17.6
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
nginx_static_value=123123

8.结论:

环境变量只会在Pod生成时加载,修改configMap,并不会被热加载到Pod中,需要重新生成Pod才行

9.如何解决Pod中配置热加载的问题呢?请看下面的例子,这时候就需要用–from-file了!

6.实战-from-file

1.首先我们先准备好所需的文件和目录:创建nginx所需目录和虚拟主机配置

[root@centos-1 conf.d]# pwd
/root/mainfasts/conf.d

[root@centos-1 conf.d]# cat server1.conf 
server {
    listen       80;
    server_name  www.baidu.com;

    location / {
        root   /server1.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

[root@centos-1 conf.d]# cat server2.conf 
server {
    listen       80;
    server_name  www.nginx.com;

    location / {
        root   /server2.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

2.然后,以–from-file的形式导入至configmap

kubectl create configmap nginx-cfg -n config --from-file=/root/mainfasts/conf.d/

3.为了养成一个好习惯,检查configmap配置载入情况还是很有必要的。

你有没有发现:

文件名成了变量名,文件内容成了变量值,他们以|进行分割

[root@centos-1 conf.d]# kubectl get cm -n config -o yaml
apiVersion: v1
items:
- apiVersion: v1
  data:
    log_level: warning
    redis_host: www.baidu.com
  kind: ConfigMap
  metadata:
    creationTimestamp: "2019-12-04T08:17:32Z"
    name: filebeat-cfg
    namespace: config
    resourceVersion: "76435"
    selfLink: /api/v1/namespaces/config/configmaps/filebeat-cfg
    uid: 2cc00197-60b6-4850-9c24-ef4f385ae058
- apiVersion: v1
  data:
    server1.conf: |
      server {
          listen       80;
          server_name  www.baidu.com;

          location / {
              root   /server1.html;
              index  index.html index.htm;
          }

          error_page   500 502 503 504  /50x.html;
          location = /50x.html {
              root   /usr/share/nginx/html;
          }
      }
    server2.conf: |
      server {
          listen       80;
          server_name  www.nginx.com;

          location / {
              root   /server2.html;
              index  index.html index.htm;
          }

          error_page   500 502 503 504  /50x.html;
          location = /50x.html {
              root   /usr/share/nginx/html;
          }
      }
  kind: ConfigMap
  metadata:
    creationTimestamp: "2019-12-04T10:11:23Z"
    name: nginx-cfg
    namespace: config
    resourceVersion: "81456"
    selfLink: /api/v1/namespaces/config/configmaps/nginx-cfg
    uid: 86374cc1-605c-4b4a-abfa-769df0a4a94d
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

4.这里使用volumeMounts的方式,将configMap的资源挂载到pod内,编辑nginx-cmfiles-volumes.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: config
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-conf               #自定义volumes的名字
      mountPath: /etc/nginx/conf.d/    #挂载到Pod中的路径
  volumes:
  - name: nginx-conf                 #自定义volumes的名字
    configMap:
      defaultMode: 0644
      name: nginx-cfg                 #configMap名
      items:
      - key: server1.conf                   #configMap中的变量名
        path: server1_new.conf                #期望以什么名字保存在Pod目录中
      - key: server2.conf
        path: server2_new.conf

5.apply -f配置文件,并进入Pod查看配置读取和挂载情况,是不是很神奇!
配置文件已经通过configmap的方式形成了。

[root@centos-1 mainfasts]# kubectl exec -it  nginx -n config -- /bin/sh
# cd /etc/nginx/conf.d
# ls
server1_new.conf  server2_new.conf
# cat server1_new.conf
server {
    listen       80;
    server_name  www.baidu.com;

    location / {
        root   /server1.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
# cat server2_new.conf
server {
    listen       80;
    server_name  www.nginx.com;

    location / {
        root   /server2.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

6.这里我们还是要来测试一下热更新的问题。
修改configmap参数(分别将server_name修改成taobao和jingdong)

[root@centos-1 conf.d]# kubectl get cm nginx-cfg -n config -o yaml
apiVersion: v1
data:
  server1.conf: |
    server {
        listen       80;
        server_name  www.taobao.com;

        location / {
            root   /server1.html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
  server2.conf: |
    server {
        listen       80;
        server_name  www.jingdong.com;

        location / {
            root   /server2.html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2019-12-04T10:11:23Z"
  name: nginx-cfg
  namespace: config
  resourceVersion: "87316"
  selfLink: /api/v1/namespaces/config/configmaps/nginx-cfg
  uid: 86374cc1-605c-4b4a-abfa-769df0a4a94d

7.几秒后Pod的配置文件也自动的进行了热更新

# cat server1_new.conf
server {
    listen       80;
    server_name  www.taobao.com;

    location / {
        root   /server1.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
# cat server2_new.conf
server {
    listen       80;
    server_name  www.jingdong.com;

    location / {
        root   /server2.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

现在,你是否已经掌握ConfigMap的使用细节了呢?