ConfigMap存储卷

若ConfigMap对象中的键值来源于较长的文件内容,那么使用环境变量将其导入会使得变量值占据过多内存空间而且不易处理。此类数据通常用于为容器应用提供配置文件,因此将其内容直接作为文件进行引用才是比较好的选择。其实现方式是,在定义Pod资源时,将此类ConfigMap对象配置问ConfigMap类型的存储卷,而后由容器将其挂载至特定的挂载点后直接进行访问

挂载整个存储卷

关联为Pod资源的存储卷时,ConfigMap对象中的每个键都对应的表现为一个文件,键名转为文件名,而键值则为相应文件的内容,即便是通过直接值创建的键值数据,也一样表现为文件试图。挂载于容器上之后,由键值数据表现出的文件位于挂载点目录中,容器中的进程可以直接读取这些文件的内容 配置Pod资源时,基于存储卷的方式引用ConfigMap对象的方法非常简单,仅需要指明存储卷名称以及需要引用的configmap对象名称即可 下面的示例是基于configmap-volume.yaml中定义的Pod资源,并且创建一个ConfigMap资源nginx-web-files,将Nginx的配置文件直接使用该资源挂载到Pod里面指定的目录下面。容器nginx-server将其挂载至应用程序nginx加载配置文件模块的目录/etc/nginx/conf.d/,具体如下 创建ConfigMap资源nginx-web-files,我们直接基于目录的方式创建configMap资源

  1. #准备Nginx配置文件,nginx的配置问都保存在宿主机的目录中,该目录存在三个配置文件,分别为myserver.conf,myserver-status.cfg和myserver-gzip.cfg,创建configmap资源时,他们会被分别存储为三个键值数据
  2. [root@k8s-master01 nginx-files]# kubectl create configmap nginx-web-files --from-file=/etc/kubernetes/nginx/configmap/nginx-files/
  3. configmap/nginx-web-files created
  4. [root@k8s-master01 nginx-files]#
  5. [root@k8s-master01 nginx-files]# kubectl get cm nginx-web-files
  6. NAME DATA AGE
  7. nginx-web-files 3 16s
  8. [root@k8s-master01 nginx-files]#
  9. [root@k8s-master01 configmap]# kubectl describe cm nginx-web-files
  10. Name: nginx-web-files
  11. Namespace: default
  12. Labels: <none>
  13. Annotations: <none>
  14. Data
  15. ====
  16. myserver-gzip.cfg:
  17. ----
  18. gzip on;
  19. gzip_comp_level 5;
  20. gzip_proxied expired no-cache no-store private auth;
  21. gzip_types text/plain text/css;
  22. myserver-status.cfg:
  23. ----
  24. location /status {
  25. stub_status on;
  26. access_log off;
  27. }
  28. myserver.conf:
  29. ----
  30. server {
  31. listen 80;
  32. server_name www.ilinux.io;
  33. include /etc/nginx/conf.d/myserver-*.cfg;
  34. location / {
  35. root /usr/share/nginx/html;
  36. }
  37. }
  38. Events: <none>
  39. [root@k8s-master01 configmap]#

创建Deployment控制器

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: configmap-volume-demo
  5. spec:
  6. replicas: 1
  7. selector:
  8. matchLabels:
  9. web: nginx
  10. template:
  11. metadata:
  12. labels:
  13. web: nginx
  14. spec:
  15. containers:
  16. - name: web-nginx
  17. image: docker.io/nginx:alpine
  18. volumeMounts:
  19. - name: ngxconfig
  20. mountPath: /etc/nginx/conf.d/
  21. readOnly: true
  22. volumes:
  23. - name: ngxconfig
  24. configMap:
  25. name: nginx-web-files

此控制器资源引用了nginx-web-files中包含的三个配置文件,其中myserver.conf定义了一个虚拟主机www.ilinux.io,并通过include指令包含/etc/nginx/conf.d/目录下以myserver-*.cfg的所有配置文件 创建Pod资源

  1. [root@k8s-master01 configmap]# kubectl apply -f configmap-volume.yaml
  2. deployment.apps/configmap-volume-demo created
  3. [root@k8s-master01 configmap]#
  4. [root@k8s-master01 configmap]# kubectl get pods -l web=nginx -o wide
  5. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  6. configmap-volume-demo-669ff5888f-mq2v7 1/1 Running 0 62s 10.244.59.11 172.18.15.113 <none> <none>
  7. [root@k8s-master01 configmap]#

此Pod创建完成后,可以直接在相应的容器上执行命令来确认挂载是否成功

  1. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 ls /etc/nginx/conf.d/
  2. myserver-gzip.cfg
  3. myserver-status.cfg
  4. myserver.conf
  5. [root@k8s-master01 configmap]#

还可以在容器中运行Nginx的配置测试及打印命令,确认由ConfigMap资源提供的配置信息是否生效

  1. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 -- nginx -T
  2. ...
  3. ...
  4. ...
  5. # configuration file /etc/nginx/conf.d/myserver.conf:
  6. server {
  7. listen 80;
  8. server_name www.ilinux.io;
  9. include /etc/nginx/conf.d/myserver-*.cfg;
  10. location / {
  11. root /usr/share/nginx/html;
  12. }
  13. }
  14. # configuration file /etc/nginx/conf.d/myserver-gzip.cfg:
  15. gzip on;
  16. gzip_comp_level 5;
  17. gzip_proxied expired no-cache no-store private auth;
  18. gzip_types text/plain text/css;
  19. # configuration file /etc/nginx/conf.d/myserver-status.cfg:
  20. location /status {
  21. stub_status on;
  22. access_log off;
  23. }
  24. nginx: configuration file /etc/nginx/nginx.conf test is successful
  25. [root@k8s-master01 configmap]#

或者直接从集群中某节点访问nginx内建的stub status

  1. [root@k8s-node02 redis]# curl http://10.244.59.11/status
  2. Active connections: 1
  3. server accepts handled requests
  4. 2 2 2
  5. Reading: 0 Writing: 1 Waiting: 0
  6. [root@k8s-node02 redis]#

挂载存储卷中的部分键值

有时候,用户很可能不期望在容器中挂载某ConfigMap存储卷后于挂载点目录导出所有的文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见。例如前面的示例中,用户可能只期望在容器挂载ConfigMap存储卷后只”导出”其中的myserver.conf和myserver-gzip.cfg,只提供页面传输压缩功能,而不输出nginx stub status信息,此时将其volumes配置段改为如下所示的内容即可。为了方便区别并在后面的文档中便于引用,这里将其保存于单独的配置文件configmap-volume2.yaml中,并且控制器名称改为configmap-volume-demo2

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: configmap-volume-demo2
  5. spec:
  6. replicas: 1
  7. selector:
  8. matchLabels:
  9. web: nginx-2
  10. template:
  11. metadata:
  12. labels:
  13. web: nginx-2
  14. spec:
  15. containers:
  16. - name: web-nginx-2
  17. image: docker.io/nginx:alpine
  18. volumeMounts:
  19. - name: ngxconfig
  20. mountPath: /etc/nginx/conf.d/
  21. readOnly: true
  22. volumes:
  23. - name: ngxconfig
  24. configMap:
  25. name: nginx-web-files
  26. items:
  27. - key: myserver.conf
  28. path: myserver.conf
  29. mode: 0644
  30. - key: myserver-gzip.cfg
  31. path: myserver-compression.cfg

configMap存储卷的items字段的值是一个对象列表,可嵌套使用的字段有三个,具体如下

  • .spec.containers.volumes.configMap.items.key <string>:需要引用的键名称,必须字段
  • .spec.containers.volumes.configMap.items.path <>:对应的键在挂载点目录中生成的文件的相对路径,可以不同于键名称,必选字段
  • .spec.containers.volumes.configMap.items.mode <integer>:文件的权限模型,可用范文为0到0777.

基于configmap-volume2.yaml的清单文件创建出相应的控制器资源

  1. [root@k8s-master01 configmap]# kubectl apply -f configmap-volume2.yaml
  2. deployment.apps/configmap-volume-demo2 created
  3. [root@k8s-master01 configmap]#
  4. [root@k8s-master01 configmap]# kubectl get pods -l web=nginx-2
  5. NAME READY STATUS RESTARTS AGE
  6. configmap-volume-demo2-7d494ffd7f-qhdjp 1/1 Running 0 7m16s
  7. [root@k8s-master01 configmap]#
  8. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo2-7d494ffd7f-qhdjp -- nginx -T
  9. ...
  10. ...
  11. ...
  12. # configuration file /etc/nginx/conf.d/myserver.conf:
  13. server {
  14. listen 80;
  15. server_name www.ilinux.io;
  16. include /etc/nginx/conf.d/myserver-*.cfg;
  17. location / {
  18. root /usr/share/nginx/html;
  19. }
  20. }
  21. # configuration file /etc/nginx/conf.d/myserver-compression.cfg:
  22. gzip on;
  23. gzip_comp_level 5;
  24. gzip_proxied expired no-cache no-store private auth;
  25. gzip_types text/plain text/css;
  26. nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
  27. nginx: configuration file /etc/nginx/nginx.conf test is successful
  28. [root@k8s-master01 configmap]#

上面的示例中,myserver-gzip.cfg映射成了myserver-compression.cfg文件,而myserver.conf则保持了与键名同名,并且明确的指定了使用了0644的权限,从而达成了仅装载部分文件至容器的目的

独立挂载存储卷中的键值

无论是挂载所有文件还是部分我呢见,挂载点目录下原有的文件都会隐藏。对于期望将ConfigMap对象提供的配置文件补充于挂载点目录下的需求来说,这种方式显然难以如愿。例如,/etc/nginx/conf.d/目录中原本就存在一些文件(如default.conf),用户期望将nginx-web-files中的全部或部分文件装载进此目录而不影响其原有的文件。 事实上,此种需求可以通过之前的文档中关于容器的voulumeMounts字段中所使用的subPath字段来解决,它可以支持用户从存储卷挂载单个文件或者单个目录而非整个存储卷。例如,下面的示例就于/etc/nginx/conf.d/目录中单独挂载了两个文件,而保留了目录下原有的文件。为了便于区分和后面文章中便于引用及说明问题,这里将其保存于单独的配置文件configmap-volume-demo3.yaml中,并将控制器名称命名为configmap-volume-demo3

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: configmap-volume-demo3
  5. spec:
  6. replicas: 1
  7. selector:
  8. matchLabels:
  9. web: nginx-3
  10. template:
  11. metadata:
  12. labels:
  13. web: nginx-3
  14. spec:
  15. containers:
  16. - name: web-nginx-3
  17. image: docker.io/nginx:alpine
  18. volumeMounts:
  19. - name: ngxconfig
  20. mountPath: /etc/nginx/conf.d/myserver.conf
  21. subPath: myserver.conf
  22. readOnly: true
  23. - name: ngxconfig
  24. mountPath: /etc/nginx/conf.d/myserver-status.cfg
  25. subPath: myserver-status.cfg
  26. readOnly: true
  27. volumes:
  28. - name: ngxconfig
  29. configMap:
  30. name: nginx-web-files

基于上述配置创建了Pod资源之后,即可通过命令验证/etc/nginx/conf.d/目录中的原有文件是否存在

  1. #可以看到我们之前创建的资源,其/etc/nginx/conf.d/目录下面,nginx自带的原有文件不会被保存
  2. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo2-7d494ffd7f-qhdjp -- ls /etc/nginx/conf.d/
  3. myserver-compression.cfg
  4. myserver.conf
  5. [root@k8s-master01 configmap]#
  6. #再次查看独立挂载键值的方式其原有文件是否会被保存
  7. [root@k8s-master01 configmap]# kubectl get pods -l web=nginx-3
  8. NAME READY STATUS RESTARTS AGE
  9. configmap-volume-demo3-565cf5cf7-p899x 1/1 Running 0 2m26s
  10. [root@k8s-master01 configmap]#
  11. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo3-565cf5cf7-p899x -- ls /etc/nginx/conf.d/
  12. default.conf
  13. myserver-status.cfg
  14. myserver.conf
  15. [root@k8s-master01 configmap]#

通过上述命令信息可以看到,其自带的default.cong配置文件被保留下来

容器应用重载新配置

相比较环境变量来说,使用configMap资源为容器应用提供配置的优势之一在于其支持容器应用动态更新其配置:用户直接更新configmap对象,而后由容器应用重载其配置文件即可 挂载ConfigMap存储卷的挂载点目录的文件其实都是符号链接,他们指向了当前目录中的”..data”而”..data”也是符号链接,它指向了名字形如”….2020_07_16_07_53_20.922502038”的目录,这个目录才是存储卷的真正挂载点。例如,查看之前章节中创建的Pod资源容器中的挂载点中的文件列表

  1. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 -- ls -lA /etc/nginx/conf.d/
  2. total 0
  3. drwxr-xr-x 2 root root 79 Jul 16 07:53 ..2020_07_16_07_53_20.922502038
  4. lrwxrwxrwx 1 root root 31 Jul 16 07:53 ..data -> ..2020_07_16_07_53_20.922502038
  5. lrwxrwxrwx 1 root root 24 Jul 16 07:53 myserver-gzip.cfg -> ..data/myserver-gzip.cfg
  6. lrwxrwxrwx 1 root root 26 Jul 16 07:53 myserver-status.cfg -> ..data/myserver-status.cfg
  7. lrwxrwxrwx 1 root root 20 Jul 16 07:53 myserver.conf -> ..data/myserver.conf
  8. [root@k8s-master01 configmap]#

这样两级符号链接设定的好处在于,在引用的configmap对象中的数据发生改变时,它将被重新挂载至一个新的临时目录下,而后,”..data”将指向此新的挂载点,便达到了同时存储卷上的所有文件数据的目的。例如,使用kubectl edit命令直接在configmap对象nginx-web-files中的myserver-status.cfg配置段中增加”allow 127.0.0.0/8”和”deny all”这两行,而后再次查看configmap-volume-demo中的容器挂载点目录中的文件列表,结果会显示其挂载点已经指向了新的位置

  1. [root@k8s-master01 configmap]# kubectl edit cm nginx-web-files
  2. configmap/nginx-web-files edited
  3. [root@k8s-master01 configmap]#
  4. ···
  5. ···
  6. myserver-status.cfg: |
  7. location /status {
  8. stub_status on;
  9. access_log off;
  10. allow 127.0.0.0/8;
  11. deny all;
  12. }
  13. ····
  14. ····
  15. [root@k8s-master01 configmap]# kubectl edit cm nginx-web-files
  16. configmap/nginx-web-files edited
  17. [root@k8s-master01 configmap]#
  18. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 -- ls -lA /etc/nginx/conf.d/
  19. total 0
  20. drwxr-xr-x 2 root root 79 Jul 16 09:11 ..2020_07_16_09_11_58.721505591
  21. lrwxrwxrwx 1 root root 31 Jul 16 09:11 ..data -> ..2020_07_16_09_11_58.721505591
  22. lrwxrwxrwx 1 root root 24 Jul 16 07:53 myserver-gzip.cfg -> ..data/myserver-gzip.cfg
  23. lrwxrwxrwx 1 root root 26 Jul 16 07:53 myserver-status.cfg -> ..data/myserver-status.cfg
  24. lrwxrwxrwx 1 root root 20 Jul 16 07:53 myserver.conf -> ..data/myserver.conf
  25. [root@k8s-master01 configmap]#

此时,若需要使用容器中应用程序的新配置文生效,则需要于Pod资源的相应容器上执行配置重载操作。例如,Nginx可以通过其”nginx -s reload”命令完成配置文件重载,如下面的命令所示

  1. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 -- nginx -s reload
  2. 2020/07/16 09:15:13 [notice] 48#48: signal process started
  3. [root@k8s-master01 configmap]#

此时,如果在容器外部访问/status页面的请求会显示403状态码,则表明配置已经生效

  1. [root@k8s-node02 redis]# curl http://10.244.59.11/status
  2. <html>
  3. <head><title>403 Forbidden</title></head>
  4. <body>
  5. <center><h1>403 Forbidden</h1></center>
  6. <hr><center>nginx/1.19.1</center>
  7. </body>
  8. </html>
  9. [root@k8s-node02 redis]#
  10. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo-669ff5888f-mq2v7 -- nginx -T
  11. ...
  12. ....
  13. ....
  14. # configuration file /etc/nginx/conf.d/myserver-status.cfg:
  15. location /status {
  16. stub_status on;
  17. access_log off;
  18. allow 127.0.0.0/8;
  19. deny all;
  20. }
  21. [root@k8s-master01 configmap]#

然而,需要注意的是,对于不支持配置文件重载的容器应用来说,只有那些在ConfigMap对象更新后创建的Pod资源中的容器会应用到新配置,此时如果不重启旧的容器,则会导致配置不一致的问题。即使对于支持重载操作的应用来说,由于新的配置信息并非同步推送进所有容器中,而且各容器的重载操作也未必能同时进行,因此在更新时,短时间内仍然会存在配置不一致的现象 另外,例如前面章节中的方式,独立挂载存储卷中的文件的容器,其挂载配置文件的方式并非是以两级链接的方式进行的,因此存储卷无法确保所有挂载的文件可以被同时更新至容器,因此为了,确保配置信息的一致性,目前这种类型的挂载不支持文件更新操作 例如,我们重载之前的示例当中的Pod应用configmap-volume-demo3,查看该Pod资源的配置文件是否被更新

  1. [root@k8s-master01 configmap]# kubectl get pods -l web=nginx-3
  2. NAME READY STATUS RESTARTS AGE
  3. configmap-volume-demo3-565cf5cf7-p899x 1/1 Running 0 33m
  4. [root@k8s-master01 configmap]#
  5. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo3-565cf5cf7-p899x -- nginx -s reload
  6. 2020/07/16 09:29:44 [notice] 61#61: signal process started
  7. [root@k8s-master01 configmap]#
  8. [root@k8s-master01 configmap]# kubectl exec configmap-volume-demo3-565cf5cf7-p899x -- nginx -T
  9. ...
  10. ....
  11. # configuration file /etc/nginx/conf.d/myserver-status.cfg:
  12. location /status {
  13. stub_status on;
  14. access_log off;
  15. }
  16. nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
  17. nginx: configuration file /etc/nginx/nginx.conf test is successful
  18. [root@k8s-master01 configmap]#

通过上面的信息可以看到,该Pod里面的容器配置并未被更新,因为目前这种类型的挂载不支持文件更新操作

使用configmap资源的注意事项

在Pod资源中调用configmap对象时需要注意以下几个问题

  • 以存储卷方式引用的configmap必须要在Pod被创建之前存在,除非在Pod中将他们全部标记为”optional”,否则将会导致Pod无法正常启动的错误;同样,即使存在configmap,在引用的键不存在时,也会导致一样的错误。
  • 当以环境变量方式注入的configmap中的键不存在时会被忽略,Pod可以正常启动,但是错误引用的信息会以”InvalidVariableName”事件记录在日志当中
  • ConfigMap是名称空间级别的资源,因此,引用它的Pod必须处于同一名称空间当中
  • kubelet不支持引用K8S API Server上不存在的ConfigMap,这包括那些通过kubelet的”manifest-url”或”—config”选项,以及kubelet REST API创建的Pod