配置容器应用ConfigMap

  ConfigMap和Secret是kubernetes系统上两种特殊类型的存储卷,ConfigMap对象用于为容器中的应用提供配置数据以定制程序的行为,不过敏感的配置信息,如密钥、证书等通常由Secret对象来进行配置。它们将相应的配置信息保存于对象中,而后在pod资源上以存储卷的形式将其挂载并获取相关的配置,以实现配置与镜像文件的解耦。

一、ConfigMap概述

1. ConfigMap是什么?

  ConfigMap是kubernetes中的资源对象,用于保存非机密性的配置的,数据可以用key/value键值对的形式保存,也可通过文件的形式保存。Kubernetes基于ConfigMap对象实现了将配置文件从容器镜像中解耦,从而增强了容器应用的可移植性。简单的说,一个ConfigMap对象就是一系列配置数据的集合,这些数据可“注入”到pod对象中,并为容器应用所使用,注入方式有挂载为存储卷和传递为环境变量两种。

2. ConfigMap能解决哪些问题?

  在部署服务的时候,每个服务都有自己的配置文件,如果一台服务器上部署多个服务:nginx、tomcat、apache等,那么这些配置都存在这个节点上,假如一台服务器不能满足线上高并发的要求,需要对服务器扩容,扩容之后的服务器还是需要部署多个服务:nginx、tomcat、apache,新增加的服务器上还是要管理这些服务的配置,如果有一个服务出现问题,需要修改配置文件,每台物理节点上的配置都需要修改,这种方式肯定满足不了线上大批量的配置变更要求。 所以,k8s中引入了Configmap资源对象,可以当成volume挂载到pod中,实现统一的配置管理。

1)Configmap是k8s中的资源, 相当于配置文件,可以有一个或者多个Configmap;

2)Configmap可以做成Volume,pod启动之后,通过 volume 形式映射到容器内部指定目录上;

3)容器中应用程序按照原有方式读取容器特定目录上的配置文件;

4)在容器看来,配置文件就像是打包在容器内部特定目录,整个过程对应用没有任何侵入

3. ConfigMap应用场景

1)使用k8s部署应用,将应用配置写进代码中,更新配置时也需要打包到镜像,ConfigMap可以将配置信息和docker镜像解耦,以便实现镜像的可移植性和可复用性,因为一个configMap其实就是一系列配置信息的集合,可直接注入到Pod中给容器使用。configmap注入方式有两种,一种将configMap做为存储卷,一种是将configMap通过env中configMapKeyRef注入到容器中。

2)使用微服务架构的话,存在多个服务共用配置的情况,如果每个服务中单独一份配置的话,那么更新配置就很麻烦,使用configmap可以友好的进行配置共享。

4. 局限性

  ConfigMap在设计上不是用来保存大量数据的。在ConfigMap中保存的数据不可超过1 MiB。如果你需要保存超出此尺寸限制的数据,可以考虑挂载存储卷或者使用独立的数据库或者文件服务。

二、创建ConfigMap对象

  kubernetes的不少资源可以使用kubectl create命令创建,也可以使用清单创建,例如namespace。ConfigMap是另一个两种创建方式都比较常用的资源。而且,通过使用“kubectl create configmap”命令,用户可以根据目录、文件或直接值创建ConfigMap对象。命令的语法格式如下:

kubectl create configmap <map-name> <data-source>

  其中,<map-name>即为ConfigMap对象的名称,而<data-source>是数据源,它可以通过直接值、文件或目录来获取。无论哪一种数据源供给方式,它都要转换为ConfigMap对象中的Key-Value数据,其中key由用户在命令行给出或是文件数据源的文件名,它仅能由字母、数字、连接号和点号组成,而Value则是直接值或文件数据源的内容。

1. 利用直接值创建

  为“kubectl create configmap”命令使用“--from-literal” 选项可在命令行直接给出键值对来创建ConfigMap对象,重复使用此选项则可传递多个键值对。格式如下:

kubectl create configmap NAME --from-literal=key1=value1

  例如,下面的命令创建tomcat-config时传递了两个键值对:

[root@k8s-master1 ~]# kubectl create configmap tomcat-config --from-literal=tomcat_port=8080 --from-literal=server_name=myapp.tomcat.com
configmap/tomcat-config created
[root@k8s-master1 ~]# kubectl get configmap
NAME               DATA   AGE
kube-root-ca.crt   1      70d
tomcat-config      2      21s

  通过“kubectl describe configmap tomcat-config” 命令查看创建的ConfigMap对象tomcat-config的相关信息:

[root@k8s-master1 ~]# kubectl describe configmap tomcat-config
Name:         tomcat-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
server_name:
----
myapp.tomcat.com
tomcat_port:
----
8080
Events:  <none>

  或者通过“kubectl get configmaps tomcat-config -o yaml”命令查看ConfigMap对象tomcat-config的相关信息:

[root@k8s-master1 ~]# kubectl get configmaps tomcat-config -o yaml
apiVersion: v1
data:
  server_name: myapp.tomcat.com
  tomcat_port: "8080"
kind: ConfigMap
metadata:
  creationTimestamp: "2022-10-10T14:18:27Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:server_name: {}
        f:tomcat_port: {}
    manager: kubectl-create
    operation: Update
    time: "2022-10-10T14:18:27Z"
  name: tomcat-config
  namespace: default
  resourceVersion: "680030"
  selfLink: /api/v1/namespaces/default/configmaps/tomcat-config
  uid: f808b2af-042c-474a-b351-235528870532

  此类方式提供的数据量有限,一般是在仅通过有限的几个数据项即可为pod资源提供足够的配置信息时使用。

2. 基于文件创建

  为“kubectl create configmap”命令使用“--from-file” 选项即可基于文件内容来创建ConfigMap对象,可以重复多次使用“--from-file”选项以传递多个文件内容。格式如下:

kubectl create configmap NAME --from-file=<path-to-file>

  例如,下面的命令可以把事先准备好的Nginx配置文件模板保存于ConfigMap对象nginx-config中:

[root@k8s-master1 ~]# mkdir configmap
[root@k8s-master1 ~]# cd configmap/
[root@k8s-master1 configmap]# cat nginx.conf
server {
  server_name www.nginx.com;
  listen 80;
  root /home/nginx/www/
}
[root@k8s-master1 configmap]# kubectl create configmap nginx-config --from-file=./nginx.conf
configmap/nginx-config created
[root@k8s-master1 configmap]# kubectl get configmaps nginx-config
NAME           DATA   AGE
nginx-config   1      31s

  这种方式创建的ConfigMap对象,其数据存储的键为文件路径的基名,值为文件内容。例如下面命令显示的nginx-config对象信息:

[root@k8s-master1 configmap]# kubectl get configmaps nginx-config -o yaml
apiVersion: v1
data:
  nginx.conf: |
    server {
      server_name www.nginx.com;
      listen 80;
      root /home/nginx/www/
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2022-10-10T14:30:56Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:nginx.conf: {}
    manager: kubectl-create
    operation: Update
    time: "2022-10-10T14:30:56Z"
  name: nginx-config
  namespace: default
  resourceVersion: "682180"
  selfLink: /api/v1/namespaces/default/configmaps/nginx-config
  uid: 6c16a082-c08a-4967-8fbc-f4576c721037
#同样使用"describe"命令也可以查看
[root@k8s-master1 configmap]# kubectl describe configmap nginx-config
Name:         nginx-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
nginx.conf:
----
server {
  server_name www.nginx.com;
  listen 80;
  root /home/nginx/www/
}

Events:  <none>

  如果需要自行指定键名,则可在“--from-file”选项中直接指定自定义的键,命令格式如下:

kubectl create configmap NAME  --from-file=<key-name>=<path-to-file>

  通过这种方式创建的ConfigMap资源可以直接以键值形式收纳应用程序的完整配置信息,多个文件可分别存储于不同的键值当中。另外需要说明的是,基于直接值和基于文件创建的方式也可以混编使用。命令格式如下:

kubectl create configmap NAME --from-file=[key=]source --from-literal=key1=value1

3. 基于目录创建

  如果配置文件数量较多且存储于有限的目录中时,kubectl还提供了基于目录直接将多个文件分别收纳为键值数据的ConfigMap资源创建方式。将“--from-file”选项后面所跟的路径指向一个目录路径就能将目录下的所有文件一同创建于同一个ConfigMap资源中,命令格式如下:

kubectl create configmap NAME  --from-file=<path-to-directory>

  如下面命令,将/data/configs目录下的所有文件都保存于mysql-config-files对象中:

[root@k8s-master1 ~]# kubectl create configmap mysql-config-files --from-file=/data/configs
configmap/mysql-config-files created
[root@k8s-master1 ~]# kubectl get configmaps mysql-config-files
NAME                 DATA   AGE
mysql-config-files   2      23s

  此目录中包含了my-server.cnf和my-slave.cnf两个配置文件。创建ConfigMap对象时,它们会被分别存储为两个键值数据,如下面的命令机器结果所示:

[root@k8s-master1 ~]# kubectl get configmap mysql-config-files -o yaml
apiVersion: v1
data:
  my-server.cnf: |
    server-id=1
  my-slave.cnf: |
    server-id=2
kind: ConfigMap
metadata:
  creationTimestamp: "2022-10-11T00:44:52Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:my-server.cnf: {}
        f:my-slave.cnf: {}
    manager: kubectl-create
    operation: Update
    time: "2022-10-11T00:44:52Z"
  name: mysql-config-files
  namespace: default
  resourceVersion: "690828"
  selfLink: /api/v1/namespaces/default/configmaps/mysql-config-files
  uid: a4841e2a-5804-4cc6-8dcd-f46137e6d916

  使用“kubectl describe configmap mysql-config-files”也可以看到相应的键值对:

[root@k8s-master1 ~]# kubectl describe configmap mysql-config-files
Name:         mysql-config-files
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
my-slave.cnf:
----
server-id=2

my-server.cnf:
----
server-id=1

Events:  <none>

   kubectl describe命令和kubectl get configmap命令都可以显示由文件创建而成的键及其值,不过二者使用的键和值之间的分隔符不同。

4. 使用清单创建

  基于配置文件创建ConfigMap资源时,它所使用的字段包括通常的apiVersion、kind和metadata字段,以及用于存储数据的关键字段“data”。例如下面的示例代码:

[root@k8s-master1 configmap]# vim mysql-config.yaml
[root@k8s-master1 configmap]# cat mysql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
  labels:
    app: mysql
data:
  master.cnf: |
    [mysqld]
    log-bin
    log_bin_trust_function_creators=1
    lower_case_table_names=1
  slave.cnf: |
    [mysqld]
    super-read-only
    log_bin_trust_function_creators=1
#创建ConfigMap对象
[root@k8s-master1 configmap]# kubectl apply -f mysql-config.yaml
configmap/mysql-config created
#查看ConfigMap对象信息 [root@k8s-master1 configmap]# kubectl get configmaps mysql-config NAME DATA AGE mysql-config 2 19s [root@k8s-master1 configmap]# kubectl get configmaps mysql-config -o yaml apiVersion: v1 data: master.cnf: | [mysqld] log-bin log_bin_trust_function_creators=1 lower_case_table_names=1 slave.cnf: | [mysqld] super-read-only log_bin_trust_function_creators=1 kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"master.cnf":"[mysqld]\nlog-bin\nlog_bin_trust_function_creators=1\nlower_case_table_names=1\n","slave.cnf":"[mysqld]\nsuper-read-only\nlog_bin_trust_function_creators=1\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"mysql"},"name":"mysql-config","namespace":"default"}} creationTimestamp: "2022-10-11T01:21:02Z" labels: app: mysql managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:data: .: {} f:master.cnf: {} f:slave.cnf: {} f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} f:labels: .: {} f:app: {} manager: kubectl-client-side-apply operation: Update time: "2022-10-11T01:21:02Z" name: mysql-config namespace: default resourceVersion: "695802" selfLink: /api/v1/namespaces/default/configmaps/mysql-config uid: 52b69f84-e44b-4b65-80de-37bf728f64fb

  如果其值来自于文件内容时,则使用配置文件创建ConfigMap资源的便捷性还不如直接通过命令行的方式,因此,建议直接使用命令行加载文件或目录的方式进行创建。为了便于配置留存,可以在创建完成后使用get -o yaml命令获取相关信息后再进行编辑留存。

三、ConfigMap的应用

1. 环境变量传递ConfigMap对象键值数据

  Pod资源的环境变量值的获取方式之一包括引用ConfigMap对象中的数据,这一点通过在env字段中为valueFrom内嵌configMapKeyRef对象即可实现,其使用格式如下:

      valueFrom: 
        configMapKeyRef:
          name:
          key:

  清单字段的说明如下:

[root@k8s-master1 configmap]# kubectl explain pod.spec.containers.env
KIND:     Pod
VERSION:  v1

RESOURCE: env <[]Object>

DESCRIPTION:
     List of environment variables to set in the container. Cannot be updated.

     EnvVar represents an environment variable present in a Container.

FIELDS:
   name <string> -required-
     Name of the environment variable. Must be a C_IDENTIFIER.

   value        <string>
     Variable references $(VAR_NAME) are expanded using the previous defined
     environment variables in the container and any service environment
     variables. If a variable cannot be resolved, the reference in the input
     string will be unchanged. The $(VAR_NAME) syntax can be escaped with a
     double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
     regardless of whether the variable exists or not. Defaults to "".

   valueFrom    <Object>
     Source for the environment variable's value. Cannot be used if value is not
     empty.

You have new mail in /var/spool/mail/root
[root@k8s-master1 configmap]# kubectl explain pod.spec.containers.env.valueFrom
KIND:     Pod
VERSION:  v1

RESOURCE: valueFrom <Object>

DESCRIPTION:
     Source for the environment variable's value. Cannot be used if value is not
     empty.

     EnvVarSource represents a source for the value of an EnvVar.

FIELDS:
   configMapKeyRef      <Object>
     Selects a key of a ConfigMap.

   fieldRef     <Object>
     Selects a field of the pod: supports metadata.name, metadata.namespace,
     `metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`, spec.nodeName,
     spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.

   resourceFieldRef     <Object>
     Selects a resource of the container: only resources limits and requests
     (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu,
     requests.memory and requests.ephemeral-storage) are currently supported.

   secretKeyRef <Object>
     Selects a key of a secret in the pod's namespace

[root@k8s-master1 configmap]# kubectl explain pod.spec.containers.env.valueFrom.configMapKeyRef
KIND:     Pod
VERSION:  v1

RESOURCE: configMapKeyRef <Object>

DESCRIPTION:
     Selects a key of a ConfigMap.

     Selects a key from a ConfigMap.

FIELDS:
   key  <string> -required-   #可用于指定要引用ConfigMap对象中某键的键名
     The key to select.

   name <string> #要引用的ConfigMap对象的名称
     Name of the referent. More info:
     https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names

   optional     <boolean> #用于为当前pod资源指明此引用是否为可选
     Specify whether the ConfigMap or its key must be defined

  此类环境变量的使用方式与直接定义的环境变量并无区别,它们可被用于容器的启动脚本或直接传递给容器应用等。

  下面是保存于配置文件configmap-env.yaml的资源定义示例,它包含了两个资源,彼此之间使用“---”相分隔。第一个资源是名为busybox-httpd-config的ConfigMap对象,它包含了两个键值数据;第二个资源是名为configmap-env-demo的pod对象,它通过环境变量引用了busybox-httpd-config对象中的键值数据,并将其直接传递给了自定义运行的容器应用httpd:

[root@k8s-master1 configmap]# vim busy-httpd.yaml
[root@k8s-master1 configmap]# cat busy-httpd.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: busybox-httpd-config
data:
    httpd_port: "8080"
    verbose_level: "-vv"
---
apiVersion: v1
kind: Pod
metadata:
  name: configmap-env-demo
spec:
  containers:
  - name: busybox-httpd
    image: busybox
    imagePullPolicy: IfNotPresent
    command: [ "/bin/httpd" ]
    args: [ "-f", "-p", "$(HTTPD_PORT)", "$(HTTPD_LOG_VERBOSE)" ]
    env:
    - name: HTTPD_PORT
      valueFrom:
        configMapKeyRef:
          name: busybox-httpd-config
          key: httpd_port
    - name: HTTPD_LOG_VERBOSE
      valueFrom:
        configMapKeyRef:
          name: busybox-httpd-config
          key: verbose_level
          optional: true
[root@k8s-master1 configmap]# kubectl apply -f busy-httpd.yaml
configmap/busybox-httpd-config created
pod/configmap-env-demo created
[root@k8s-master1 configmap]# kubectl get pods -o wide
NAME                                     READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
configmap-env-demo                       1/1     Running   0          11s   10.244.36.86     k8s-node1   <none>           <none>
[root@k8s-master1 configmap]# kubectl get configmaps -o wide
NAME                   DATA   AGE
busybox-httpd-config   2      26s
kube-root-ca.crt       1      70d

  注:在command或args字段中引用环境变量要使用“$(VAR_NAME)”的格式。资源创建完成后,可以通过如下命令验证pod资源监听的端口等配置信息是否为busybox-httpd-config中定义的内容:

[root@k8s-master1 configmap]# kubectl exec configmap-env-demo -- ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -p 8080 -vv
   13 root      0:00 ps aux
[root@k8s-master1 configmap]#

  注:创建引用了ConfigMap资源的Pod对象时,被引用的资源必须事先存在,否则将无法启动相应的容器,直到被依赖的资源创建完成为止。另外,CnfigMap是名称空间级别的资源,它必须与引用它的pod资源在同一空间中。

  假设存在这么一种情形,某ConfigMap资源中存在较多的键值数据,而全部或大部分的这些键值数据都需要容器来引用。此时,为容器逐一配置相应的环境变量将是一件颇为费神的事情,而且极易出错。对此,Pod资源支持在容器中使用envFrom字段直接将ConfigMap资源中的所有键值一次性地完成导入。它的使用格式如下:

spec:
  containers:
  - name: container_name
    image: image_name
    envFrom:
    - prefix <string>
      configMapRef:
        name  <string>
        optional  <boolean>

  envFrom字段值是对象列表,可用于同时从多个ConfigMap对象导入键值数据。为了避免从多个ConfigMap引用键值数据时产生键名的冲突,可以在每个引用中将被导入的键使用prefix字段指定一个特定的前缀,如“HTCFG_”一类的字符串,于是,ConfigMap对象中的httpd_port将成为Pod资源中名为HTCFG_httpd_port的变量。

  注:如果键名中使用了连接线“-”,那么在转换为变量名时,连接线将被自动替换为下划线“_”。

  例如:把上面示例中的pod资源转为如下形式的定义(configmap-envfrom-pod.yaml配置文件)后,其引用ConfigMap进行配置的效果并无不同:

[root@k8s-master1 configmap]# vim configmap-envfrom-pod.yaml
[root@k8s-master1 configmap]# cat configmap-envfrom-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-envfrom-demo
spec:
  containers:
  - name: busybox-httpd
    image: busybox
    imagePullPolicy: IfNotPresent
    command: [ "/bin/httpd" ]
    args: [ "-f", "-p", "$(HTCFG_httpd_port)", "$(HTCFG_verbose_level)" ]
    envFrom:
    - prefix: HTCFG_
      configMapRef:
        name: busybox-httpd-config
        optional: false
[root@k8s-master1 configmap]# kubectl apply -f configmap-envfrom-pod.yaml
pod/configmap-envfrom-demo created
[root@k8s-master1 configmap]# kubectl get pods configmap-envfrom-demo -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
configmap-envfrom-demo   1/1     Running   0          57s   10.244.36.88   k8s-node1   <none>           <none>

  创建pod资源后,可通过查看其环境变量验证其导入的结果:

[root@k8s-master1 configmap]# kubectl exec configmap-envfrom-demo -- printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=configmap-envfrom-demo
HTCFG_httpd_port=8080
HTCFG_verbose_level=-vv
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
HOME=/root

  从ConfigMap对象导入资源时,prefix为可选字段,省略时,所有变量名同ConfigMap中的键名。如果不存在键名冲突的可能性,例如从单个ConfigMap对象导入变量或在ConfigMap对象中定义键名时已然添加了特定的前缀,那么省略前缀的定义既不会导致键名冲突,又能保持变量的简洁。

2. ConfigMap存储卷  

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

1)挂载整个存储卷

  关联为Pod资源的存储卷时,ConfigMap对象中的每个键都对应地表现为一个文件,键名转为文件名,而键值则为相应文件的内容,即便是通过直接值创建的键值数据,也一样表现为文件视图。挂载于容器上之后,由键值数据表现出的文件位于挂载点目录中,容器中的进程可直接读取这些文件的内容。

  配置Pod资源时,基于存储卷的方式引用ConfigMap对象的方法非常简单,仅需要指明存储卷名称及要引用的ConfigMap对象名称即可。下面是于配置文件configmap-volume-pod.yaml中定义的pod资源,它引用了创建的ConfigMap对象nginx-config,容器nginx-server将其挂载至应用程序Nginx加载配置文件模块的目录下/etc/nginx/conf.d中,具体如下:

  创建ConfigMap对象如下:

[root@k8s-master1 configmap]# cat nginx.conf
server {
    listen 8080;
    server_name www.ilinux.io;

    location / {
        root /usr/share/nginx/html;
    }
}
[root@k8s-master1 configmap]# kubectl create configmap nginx-config --from-file=./nginx.conf
configmap/nginx-config created
[root@k8s-master1 configmap]# kubectl get configmaps
NAME                   DATA   AGE
kube-root-ca.crt       1      71d
nginx-config           1      3s

  创建pod资源,挂载ConfigMap对象

[root@k8s-master1 configmap]# cat configmap-volume-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo
spec:
  containers:
  - name: nginx-server
    image: nginx:alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: ngxconfig
    configMap:
      name: nginx-config
You have new mail in /var/spool/mail/root
[root@k8s-master1 configmap]# kubectl apply -f configmap-volume-pod.yaml
pod/configmap-volume-demo created
[root@k8s-master1 configmap]# kubectl get pods configmap-volume-demo
NAME                    READY   STATUS    RESTARTS   AGE
configmap-volume-demo   1/1     Running   0          4s

  在pod资源的相应容器上执行命令确认文件是否存在于挂载点的目录中:

[root@k8s-master1 configmap]# kubectl exec -it configmap-volume-demo -- /bin/sh
/ # ls /etc/nginx/conf.d/
nginx.conf
/ # cat /etc/nginx/conf.d/nginx.conf
server {
    listen 8080;
    server_name www.ilinux.io;

    location / {
        root /usr/share/nginx/html;
    }
}
/ #

  可以看到文件被添加到了容器中

2)挂载存储卷中的部分键值

  有时候,用户很可能不期望在容器中挂载某ConfigMap存储卷后挂载点目录导出所有的文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见。例如,创建一个ConfigMap对象,包含三个配置文件myserver-gzip.cfg,myserver-status.cfg和myserver.conf,分别存储为三个键值数据,用户可能只期望在容器中挂载该ConfigMap存储卷后只导出其中的两个myserver-gzip.cfg,myserver.conf,只提供页面传输压缩功能,而不输出nginx stub status信息。

  创建包含三个配置文件myserver-gzip.cfg,myserver-status.cfg和myserver.conf的ConfigMap对象:

[root@k8s-master1 ~]# cd /data/configs/
You have new mail in /var/spool/mail/root
[root@k8s-master1 configs]# ll
total 12
-rw-r--r-- 1 root root 175 Oct 12 20:40 myserver.conf
-rw-r--r-- 1 root root 147 Oct 12 20:36 myserver-gzip.cfg
-rw-r--r-- 1 root root  67 Oct 12 20:38 myserver-status.cfg
[root@k8s-master1 configs]# cat myserver-gzip.cfg
gzip on;
gzip_comp_level 5;
gzip_proxied   expired no-cache no-store private auth;
gzip_types text/plain text/css application/xml text/javascript;
[root@k8s-master1 configs]# cat myserver-status.cfg
location /nginx-status {
    stub_status on;
    access_log off;
}
[root@k8s-master1 configs]# cat myserver.conf
server {
    listen 8080;
    server_name www.ilinux.io;

    include /etc/nginx/conf.d/myserver-*.cfg;

    location / {
        root  /usr/share/nginx/html;
    }
}
[root@k8s-master1 configmap]# kubectl create configmap nginx-config-files --from-file=/data/configs/
configmap/nginx-config-files created
[root@k8s-master1 configmap]# kubectl describe cm nginx-config-files
Name:         nginx-config-files
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
myserver-gzip.cfg:
----
gzip on;
gzip_comp_level 5;
gzip_proxied   expired no-cache no-store private auth;
gzip_types text/plain text/css application/xml text/javascript;

myserver-status.cfg:
----
location /nginx-status {
    stub_status on;
    access_log off;
}

myserver.conf:
----
server {
    listen 8080;
    server_name www.ilinux.io;
    include /etc/nginx/conf.d/myserver-*.cfg;
    location / {
        root  /usr/share/nginx/html;
    }
}


Events:  <none>

  创建对应的pod资源,在容器中挂载上述ConfigMap存储卷后只导出myserver-gzip.cfg,myserver.conf,此时的volumes配置段修改如下:

[root@k8s-master1 configmap]# vim configmap-volume-pod-1.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 configmap]# cat configmap-volume-pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo-2
spec:
  containers:
  - name: web-server
    image: nginx:alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: ngxconfig
    configMap:
      name: nginx-config-files
      items:
      - key: myserver.conf
        path: myserver.conf
        mode: 0644
      - key: myserver-gzip.cfg
        path: myserver-compression.cfg
[root@k8s-master1 configmap]# kubectl apply -f configmap-volume-pod-1.yaml
pod/configmap-volume-demo-2 created
[root@k8s-master1 configmap]# kubectl get pods configmap-volume-demo-2
NAME                      READY   STATUS    RESTARTS   AGE
configmap-volume-demo-2   1/1     Running   0          16s

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

[root@k8s-master1 configmap]# kubectl explain pod.spec.volumes.configMap.items
KIND:     Pod
VERSION:  v1

RESOURCE: items <[]Object>

DESCRIPTION:
     If unspecified, each key-value pair in the Data field of the referenced
     ConfigMap will be projected into the volume as a file whose name is the key
     and content is the value. If specified, the listed keys will be projected
     into the specified paths, and unlisted keys will not be present. If a key
     is specified which is not present in the ConfigMap, the volume setup will
     error unless it is marked optional. Paths must be relative and may not
     contain the '..' path or start with '..'.

     Maps a string key to a path within a volume.

FIELDS:
   key  <string> -required-  #要引用的键名称,必选字段
     The key to project.

   mode <integer>   #文件的权限模型,可用范围为0到0777
     Optional: mode bits used to set permissions on this file. Must be an octal
     value between 0000 and 0777 or a decimal value between 0 and 511. YAML
     accepts both octal and decimal values, JSON requires decimal values for
     mode bits. If not specified, the volume defaultMode will be used. This
     might be in conflict with other options that affect the file mode, like
     fsGroup, and the result can be other mode bits set.

   path <string> -required-   #对应的键于挂载点目录中生成的文件的相对路径,可以不同于键名称,必选字段
     The relative path of the file to map the key to. May not be an absolute
     path. May not contain the path element '..'. May not start with the string
     '..'.

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

  可以直接于pod资源的相应容器上执行命令来确认是否仅挂载了对应的两个myserver-compression.cfg和myserver.conf文件于挂载点目录中:

[root@k8s-master1 configmap]# kubectl exec -it configmap-volume-demo-2 -- /bin/sh
/ # ls /etc/nginx/conf.d/
myserver-compression.cfg  myserver.conf
/ #

  由上述命令的结果可见,myserver-compression.cfg和myserver.conf文件被添加到了容器中,达到了仅挂载部分文件到容器的目的。

 3)独立挂载存储卷中的键值

  无论是装载所有文件还是部分文件,挂载点目录下原有的文件都会被隐藏。对于期望将ConfigMap对象提供的配置文件补充挂载点目录下的需求来说,这种方式显然难以如愿。例如,/etc/nginx/conf.d目录中原本就存在一些文件(如:default.conf),用户期望将nginx-cnfig-files中的全部或部分文件装载进此目录中,而不影响其原有的文件。

  事实上,此种需求可以通过使用容器的volumeMounts字段中使用的subPath字段来解决,它可以支持用户从存储卷挂载单个文件或单个目录而非整个存储卷。例如,下面的示例就于/etc/nginx/conf.d目录中单独挂载了两个文件,而保留了原目录下原有文件。

[root@k8s-master1 configmap]# vim configmap-volume-pod-2.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 configmap]# cat configmap-volume-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo-3
spec:
  containers:
  - name: web-server
    image: nginx:alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/myserver.conf
      subPath: myserver.conf
      readOnly: true
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/myserver-gzip.cfg
      subPath: myserver-gzip.cfg
      readOnly: true

  volumes:
  - name: ngxconfig
    configMap:
      name: nginx-config-files
[root@k8s-master1 configmap]# kubectl apply -f configmap-volume-pod-2.yaml
pod/configmap-volume-demo-3 created
[root@k8s-master1 configmap]# kubectl get pods configmap-volume-demo-3
NAME                      READY   STATUS    RESTARTS   AGE
configmap-volume-demo-3   1/1     Running   0          15s

  基于上述配置创建pod资源后,即可通过命令验证/etc/nginx/conf.d目录中的原有文件确实能够得以保留,如下面的命令及其结果所示:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-3 -- ls /etc/nginx/conf.d/
default.conf
myserver-gzip.cfg
myserver.conf  

 四、容器应用重载新配置

  相较于环境变量来说,使用ConfigMap资源为容器应用提供配置的优势之一在于其支持容器应用动态更新其配置:用户直接更新COnfigMap对象,而后由容器应用重载其配置文件即可。

  之前创建的pod资源,挂载ConfigMap存储卷的挂载点目录中的文件都是符号链接,它们指向了当前目录中的“..data”,而“..data”也是符号链接,它指向了名字形如“..2022_10_12_13_05_28.183541001”的目录,这个目录才是存储卷的真正挂载点。例如,查看创建的pod资源configmap-volume-demo-2容器中的挂载点中的文件列表,它将显示出类似如下的结果:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-2 -- ls -la /etc/nginx/conf.d/
total 0
drwxrwxrwx    3 root     root           112 Oct 12 13:05 .
drwxr-xr-x    3 root     root           152 Oct  6 23:11 ..
drwxr-xr-x    2 root     root            59 Oct 12 13:05 ..2022_10_12_13_05_28.183541001
lrwxrwxrwx    1 root     root            31 Oct 12 13:05 ..data -> ..2022_10_12_13_05_28.183541001
lrwxrwxrwx    1 root     root            31 Oct 12 13:05 myserver-compression.cfg -> ..data/myserver-compression.cfg
lrwxrwxrwx    1 root     root            20 Oct 12 13:05 myserver.conf -> ..data/myserver.conf

  这样两级符号链接设定的好处在于,在引用ConfigMap对象中的数据发生变化时,它将被重新挂载至一个新的临时目录下,而后“..data”将指向此新的挂载点,便达到了同时更新存储卷上所有文件数据的目的。例如,使用kubectl edit命令直接在ConfigMap对象nginx-config-files中的myserver.conf配置段修改端口号为8090;server_name为www.nginx.io:

[root@k8s-master1 ~]# kubectl edit cm nginx-config-files
configmap/nginx-config-files edited
You have new mail in /var/spool/mail/root
[root@k8s-master1 ~]# kubectl describe cm nginx-config-files
Name:         nginx-config-files
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
myserver-gzip.cfg:
----
gzip on;
gzip_comp_level 5;
gzip_proxied   expired no-cache no-store private auth;
gzip_types text/plain text/css application/xml text/javascript;

myserver-status.cfg:
----
location /nginx-status {
    stub_status on;
    access_log off;
}

myserver.conf:
----
server {
    listen 8090;
    server_name www.nginx.io;
    include /etc/nginx/conf.d/myserver-*.cfg;
    location / {
        root  /usr/share/nginx/html;
    }
}


Events:  <none>

  而后再次查看configmap-volume-demo-2中的容器的挂载点目录中的文件列表,结果是其挂载点已经指向了新的位置:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-2 -- ls -la /etc/nginx/conf.d/
total 0
drwxrwxrwx    3 root     root           112 Oct 12 14:18 .
drwxr-xr-x    3 root     root           152 Oct  6 23:11 ..
drwxr-xr-x    2 root     root            59 Oct 12 14:18 ..2022_10_12_14_18_31.561234874
lrwxrwxrwx    1 root     root            31 Oct 12 14:18 ..data -> ..2022_10_12_14_18_31.561234874
lrwxrwxrwx    1 root     root            31 Oct 12 13:05 myserver-compression.cfg -> ..data/myserver-compression.cfg
lrwxrwxrwx    1 root     root            20 Oct 12 13:05 myserver.conf -> ..data/myserver.conf

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

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-2 -- nginx -s reload
2022/10/12 14:22:56 [notice] 51#51: signal process started

  可以通过查看监听端口检测新配置是否生效:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-2 -- netstat -lntup|grep 8090
tcp        0      0 0.0.0.0:8090            0.0.0.0:*               LISTEN      1/nginx: master pro

  也可以通过查看新配置文件,查看配置文件是否发生变化:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-2 -- cat /etc/nginx/conf.d/myserver.conf
server {
    listen 8090;
    server_name www.nginx.io;
    include /etc/nginx/conf.d/myserver-*.cfg;
    location / {
        root  /usr/share/nginx/html;
    }
}

  然而,对于不支持配置文件重载操作的容器应用来说,只有那些在ConfigMap对象更新后创建的pod资源中的容器会应用到新配置,此时如果不重启旧有的容器,则会导致配置不一致的问题。即使对于支持重载操作的应用来说,由于新的配置信息并非同步推送进所有容器中,而且各容器的重载操作也未必能同时进行,因此在更新时,短时间内仍然会出现配置不一致的现象。

  另外,对于独立挂载存储卷中的文件的容器,其挂载配置文件的方式并非是以两级链接的方式进行的,因此,存储卷无法确保所有挂载的文件可以被同时更新到容器中。

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-3 -- ls -la /etc/nginx/conf.d/
total 12
drwxr-xr-x    1 root     root            72 Oct 12 13:47 .
drwxr-xr-x    1 root     root            20 Oct  6 23:11 ..
-rw-r--r--    1 root     root          1093 Oct 12 13:47 default.conf
-rw-r--r--    1 root     root           147 Oct 12 13:47 myserver-gzip.cfg
-rw-r--r--    1 root     root           169 Oct 12 13:47 myserver.conf

  进入到pod资源的容器中查看配置文件信息,发现并未更新:

[root@k8s-master1 configmap]# kubectl exec configmap-volume-demo-3 -- cat /etc/nginx/conf.d/myserver.conf
server {
    listen 8080;
    server_name www.ilinux.io;
    include /etc/nginx/conf.d/myserver-*.cfg;
    location / {
        root  /usr/share/nginx/html;
    }
}

  因此,这种类型的挂载不支持文件更新操作。

五、使用ConfigMap资源注意事项

在pod资源中调用ConfigMap对象时需要注意以下几个问题:

1. 以存储卷方式引用的ConfigMap必须先于pod存在,除非在pod中将它们全部标记为“optional”,否则将会导致pod无法正常启动错误;同样,即使存在ConfigMap,在引用的键不存在是,也会导致一样的错误。

2. 当以环境变量方式注入的ConfigMap中的键不存在时会被忽略,pod可以正常启动,但是错误引用的信息会以“Invalid Variable Names ”事件记录于日志中。

3. ConfigMap是名称空间级资源,因此,引用它的Pod必须处于同一名称空间中。

4. kubelet不支持引用kubernetes API server 上不存在的ConfigMap,这包括那些通过kubelet 的“--manifest-url” 或“--config”选项,以及kubelet REST API创建的Pod。

posted @ 2022-10-12 23:19  出水芙蓉·薇薇  阅读(601)  评论(0编辑  收藏  举报