Kubernetes之ConfigMap详解及实践

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

容器化应用配置方式

每个应用程序都是可执行程序文件,例如Nginx、Tomcat、MySQL等,但我们使用中,通常不会通过默认的配置参数来运行应用,一般都需要自定义符合我们场景的配置,那么就需要定义配置文件来完成。那我们的应用运行在容器中,应该如何定义配置信息呢?例如为Tomcat的JVM配置堆内存大小等,在容器中启动时,我们可以向容器命令传递参数,将定义好的配置文件嵌入镜像文件中、通过环境变量(Environment Variables)传递配置数据,以及基于Docker卷传送配置文件等。

以下将介绍向容器提供配置信息的几种方法。

通过命令行参数进行配置

在制作Docker镜像时,Dockerfile中的ENTRYPOINT和CMD指令可用于指定容器启动时要运行的程序及相关参数。CMD指令以列表的形式指定要运行的程序和相关参数,但是如果同时存在ENTRYPOINT指令,则CMD指令中列表的所有元素都将被作为由ENTRYPOINT指定程序的命令行参数。另外在基于某镜像使用Docker命令创建容器时,可以在命令行向ENTRYPOINT中的程序传递额外的自定义参数,甚至还可以修改要运行的应用程序本身,例如以下命令使用docker run创建并启动容器的格式为: docker run [OPTINS] IMAGE [COMMAND] [ARG]

COMMAND为自定义运行的程序,ARG为传递给程序的参数,假如定义相关镜像文件时使用了ENTRYPOINT指令,则COMMANDARG都会被当作命令行参数传递给ENTRYPOINT指令中指定的程序,除非运行docker run命令时额外使用--entrypoint选项来覆盖镜像文件中的ENTRYPOINT指定的程序。

在Kubernetes系统上创建Pod资源时,也能够向容器化应用传递命令行参数,甚至指定运行其它应用程序,相关的字段分别为pods.spec.containers.commandpods.spec.containers.args

将配置文件载入镜像文件

在通过Dockerfile制作镜像时,可以使用COPY或者ADD指定将定义好的配置文件直接复制到镜像文件系统上的相应位置,或者使用RUN指令调用sed或echo一类的命令修改配置文件从而达到为容器化应用提供自定义配置文件之目的。使用Docker Hub上的某镜像文件额外添加配置文件即能符合需要,则克隆其Dockerfile文件修改至符合需求之后再将之推送至GitHub,并由Docker Hub自动构建出镜像文件即可。

通过环境变量向容器注入配置信息

通过环境变量为容器提供配置信息是Docker Hub上最常见的使用方式,例如,使用MySQL官方提供的镜像文件启动MySQL容器时使用的MYSQL_ROOT_PASSWORD环境变量,它用于为MYSQL服务器的root用户设置登陆密码。

在基于此类镜像启动容器时,用户为 docker run 命令通过 -e 选项向环境变量传值即能实现应用配置,命令的使用格式为 docker run -e SETTING1=foo -e SETTING2=bar ... <image name>。启动时,容器的ENTRYPOINT启动脚本会抓取到这些环境变量,并在启动容器应用之前,通过sed或echo等一类的命令将变量值替换到配置文件中。

通过存储卷向容器注入配置信息

Docker存储卷(volumes)能够将宿主机之上的任何文件或目录映射到容器文件系统之上,因此,可以事先将配置文件放置于宿主机之上的某个路径中,而后在启动容器时进行加载。这种方式灵活易用,但也依赖于用户需要事先将配置数据提供在宿主机上的特定路径下,而且在多主机模型中,若容器存在被调度至任一主机运行的可能性时,用户还需要将配置共享到任一宿主机来确保容器能够正常地获取到它们。

借助Docker config进行容器配置

Docker swarm service自1.13版本起支持使用secret于容器之外保存二进制数据,包括口令、SSH私钥、SSL证书以及其它不建议通过网络传输或不应该在Dockerfile及程序源码中加载保存的机密数据,用户可使用secret集群化管理这类数据并将其关联至那些需要访问这些数据的容器中。

另外,Docker自17.06版本起为swarm service引入了允许用户容器之外存储非敏感信息(如配置文件)的组件service config,从而支持用户创建通过目的镜像文件,并且不再需要通过挂载存储卷或使用环境变量为容器提供配置文件。

Docker swarm service secret和config为容器化应用的配置提供了极大的灵活性,不过,它们也只能应用于Docker swarm service环境中,而不能应用于单独运行的容器之上。

Kubernetes系统也有类似的组件,它们被称为Secret和ConfigMap,而且是Kubernetes系统上一等类别的资源对象,它们要么被Pod资源以存储卷的形式加载,要么由容器通过envFrom字段以变量的形式加载。

1. 通过命令行参数配置容器应用

创建Pod资源时,可以在容器中自定义要运行的命令以及选项和参数。在容器的配置上下文中,使用command字段指定要运行的程序,而args字段则可用于指定传递给程序的选项和参数。在配置文件中定义command和args会覆盖镜像文件中相关的默认设定,这类程序会被直接运行,而不会由shell解释器解释运行,因此与sehll相关的特性均不被支持,如命令行展开符号 {}、重定向等操作。

下面是定义在 command-demo.yaml 文件中的Pod资源示例,它在容器 command-demo-container中将busybox镜像文件中默认运行的命令 ["/bin/sh", "-c"]修改为["httpd"],并为其额外传递了["-f"]选项。

cat command-demo.yamlapiVersion: v1kind: Podmetadata:  name: command-demo  labels:    purpose: demonstrate-commdnspec:  containers: - name: command-demo-container    image: busybox    command: ["httpd"]    args: ["-f"]    ports: - containerPort: 80

资源清单编辑之后创建Pod对象,然后查看容器所运行的程序,结果如下父进程为 httpd -f

kubectl exec -it pods/command-demo -- ps auxPID   USER     TIME  COMMAND 1 root      0:00 httpd -f 22 root      0:00 ps aux

事实上,我们也可以只在容器配置的上下文提供 args 字段,以实现向默认运行的程序提供额外的参数,如果默认的命令为Shell解析器或 entrypoint 启动脚本,那么这些参数本身甚至还可以是要运行的命令及其参数,例如,下面的容器配置,表示要运行的程序为/bin/sh -c httpd -f,实现了shell解释器解释运行执行的程序之目的。

cat command-demo.yamlapiVersion: v1kind: Podmetadata:  name: command-demo  labels:    purpose: demonstrate-commdnspec:  containers: - name: command-demo-container    image: busybox    args: ["httpd","-f"]    ports: - containerPort: 80

创建Pod对象,并查看容器的父进程

kubectl exec -it pods/command-demo -- ps auxPID   USER     TIME  COMMAND 1 root      0:00 httpd -f 8 root      0:00 ps aux

由上述的示例可见,Kubernetes配置文件中的command对应于Dockerfile中的ENTRYPOINT,而配置文件的args则对应于Dockerfile中的CMD。在Kubernetes中只给出command字段时,他会覆盖Dockerfile中的ENTRYPOINT和CMD,只给出args字段时,它仅覆盖CMD,而同时给出command和args时,它会对应覆盖ENTRYPOINT和CMD。

2. 利用环境变量配置容器应用

在Kubernetes中使用镜像启动容器时,可以在Pod资源或Pod模版资源为容器配置使用env参数来定义所使用的环境变量列表,即便容器中的应用本身不处于环境变量,也一样可以向容器传递环境变量,只不过它不被使用罢了。

环境变量配置容器化应用时,需要在容器配置段中嵌套使用env字段,它的值是一个由环境变量构建的列表。环境变量由name和value(或valueFrom)字段构成。

name<string>:环境变量的名称,必须字段。•value<string>:环境变量的值,通过 ${VAR_NAME} 引用,默认值为空。•valueFrom<Object>:环境变量值的引用源,例如,当前Pod资源的名称、名称空间、标签等,不能与非空值的value字段同时使用,即环境变量的值要么源于value字段,要么源于valueFrom字段,二者不可同时提供数据。valueFrom字段可引用的值有多种来源,包括当前Pod资源的属性值,容器相关的系统资源配置、ConfigMap对象中的Key以及Secret对象中的Key,它们应分别使用不同的嵌套字段进行定义。•fieldRef<Object>:当前Pod资源的指定字段,目前支持使用的字段包括 metadata.name、metadata.namespace、metadata.labels、metadata.annotations、spec.nodeName、spec.serviceAccountName、status.hostIP和status.podIP。•configMapKeyRef<Object>:ConfigMap对象中的特定Key。•secretKeyRef<Object>:Secret对象中的特定Key。•resourceFieldRef<Object>:当前容器的特定系统资源的最小值(配额)或最大值(限额),目前支持的引用包括limits.cpu、limts.ephemeral-storage、requests、cpu、requests.memory、requests.ephemeral-storage。

下面是定义在配置文件 env-demo.yaml 中的Pod资源,其通过环境变量引用当前Pod资源及其所在节点的相关属性值配置容器,fieldRef字段的值是一个对象,它一般由apiVersion(创建当前Pod资源的API版本)或fidldPath嵌套字段所定义:

cat env-demo.yamlapiVersion: v1kind: Podmetadata:  name: env-demo  labels:    purpose: demonstrate-environment-variablesspec:  containers: - name: env-demo-container    image: busybox    command: [ "httpd" ]    args: [ "-f" ]    env: - name: HELLO_WORLD value: just a demo - name: MY_NODE_NAME      valueFrom:        fieldRef:          fieldPath: spec.nodeName - name: MY_NODE_IP      valueFrom:        fieldRef:          fieldPath: status.hostIP - name: MY_POD_NAMESPACE      valueFrom:        fieldRef:          fieldPath: metadata.namespace  restartPolicy: OnFailure

创建Pod对象

kubectl apply -f env-demo.yaml

然后打印它的环境变量列表

kubectl exec -it pods/env-demo -- printenvHELLO_WORLD=just a demo                            #在清单中定义的HELLO_WORLD变量与值MY_NODE_NAME=k8s-node03                            #在清单中定义的MY_NODE_NAME变量与从spec.nodeName获取到的值MY_NODE_IP=192.168.31.233 #在清单中定义的MY_NODE_IP变量与从status.hostIP获取的值MY_POD_NAMESPACE=default #在清单中定义的MY_POD_NAMESPACE变量与从metadata.namespec获取到的值

容器的启动脚本或应用程序调用或处理这些环境变量、即可实现容器化应用的配置。相较于命令行参数的方式来说,使用环境变量的配置方式更清晰、易懂,尤其是对于首次使用相关容器的用户来说,这种方式能够快速了解容器的配置方式,不过这两种配置方式有一个共同缺陷:无法在容器应用运行过程中更新环境变量从而达到更新应用目的。这通常意味着用户不得不为production、development和qa等不同的环境分别配置Pod资源。好在,用户还有ConfigMap资源可用。

3. ConfigMap介绍之各姿势创建ConfigMap

ConfigMap诞生的原因

分布式环境中,基于负载、容错性等需求的考虑,几乎所有的服务都需要在不同的机器节点上部署不止一个实例。随着程序功能的日益复杂,程序的配置日益增多,而且配置文件的修改频率通常远远大于代码本身,这种情况下,有时仅仅是一个配置内容的修改,就不得不重新将代码提交到SVN/Git、打包、分发上线的流程。部署规则较大的场景中,分发上线工作即繁杂又沉重。

究其根本,所有的这些麻烦都是由于配置和代码在管理和发布的过程中不加区分所致。配置本身源于代码,是为了提高代码的灵活性而提取出来的一些经常变化的或需要定制的内容,而正是配置的这种天生变化特性为部署过程中带来了不小的麻烦,也最终催生了分布式系统配置管理系统,将配置内容从代码中完全分离出来,及时可靠高效地提供配置访问和更新服务。

提示:国内分布式配置中心相关的开源项目有 Diamond(阿里)、Apollo(携程)、Qconf(奇虎360)和disconf(百度)等。

作为分布式系统的Kubernetes也提供了统一配置管理方案——ConfigMap。Kubernetes基于ConfigMap对象实现了将配置文件从容器镜像中解耦,从而增强了容器应用的可移植性。简单来说,一个ConfigMap对象就是一系列配置数据的集合,这些数据可“注入”到Pod对象中,并为容器应用所使用,注入方式有挂载为存储卷和传递为环境变量两种。

ConfigMap介绍

ConfigMap对象将配置数据以键值对的形式进行存储,这些数据可以在Pod对象中使用或者为系统组件提供配置,例如控制器对象等。不过无论应用程序如何使用ConfigMap对象中的数据,用户都完全可以通过在不同的环境中创建名称相同但内容不同的ConfigMap对象,从而为不同环境中同一功能的Pod资源提供不同的配置信息,实现应用与配置的灵活勾兑。

ConfigMap对象创建

ConfigMap创建的方式与其它资源一样有两种:

•kubectl create configmap 命令直接创建•通过资源配置清单创建 以上两种对于ConfigMap都算是比较常用的创建方式,通过kubectl create configmap命令,用户可以根据目录、文件或者直接创建ConfigMap对象,命令的语法格式如下: kubectl create configmap <map-name> <data-source>

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

通过键值创建ConfigMap

利用kubectl create configmap命令使用--from-literal选项可在命令行直接给出键值对来创建ConfigMap对象,重复使用此选项则可以传递多个键值对,命令格式如下: kubectl create configmap configmap_name --from-literal=key-name01=value-1

例如下面用命令创建special-config configmap时传递来两个键值对:键值对第一个key为mysql_ip值为172.16.0.3 键值对第二个key为mysql_port值为3306

kubectl create configmap special-config \--from-literal=mysql_ip=172.16.0.3 --from-literal=mysql_port=3306

查看创建的ConfigMap

kubectl get configmapNAME             DATA   AGEspecial-config   2 55s

#使用kubectl describe可以看到configmap中的原始数据kubectl describe configmap/special-configName:         special-configNamespace: defaultLabels: <none>Annotations: <none>
Data====mysql_ip:----172.16.0.3mysql_port:----3306Events: <none>

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

通过文件创建ConfigMap

利用kubectl create configmap命令使用--from-file选项可基于文件内容来创建ConfigMap对象,它的命令格式如下: kubectl create configmap <configmap_name> --from-file=<[key=]source>

1.准备配置文件 我们先准备好要载入容器的配置文件,等下通过kubectl create configmap config_name --from-file 来指定我们的配置文件即可创建configmap,以下准备了一个elasticsearch.yaml的配置文件

cat elasticsearch.yamlcluster.name: elasticsearchnode.name: elasticpath.data: /usr/local/elastic7.4/datapath.logs: /usr/local/elastic7.4/logsbootstrap.memory_lock: truenetwork.host: 0.0.0.0network.tcp.no_delay: truenetwork.tcp.keep_alive: truenetwork.tcp.reuse_address: truenetwork.tcp.send_buffer_size: 256mbnetwork.tcp.receive_buffer_size: 256mbtransport.tcp.port: 9300transport.tcp.compress: truehttp.max_content_length: 200mbhttp.cors.enabled: truehttp.cors.allow-origin: "*"http.port: 9200cluster.initial_master_nodes: ["127.0.0.1:9300"]xpack.security.enabled: truexpack.license.self_generated.type: basicxpack.security.transport.ssl.enabled: truexpack.monitoring.collection.enabled: true

2.通过文件创建configMap 如果指定文件创建configmap的时候没有指定key,那么kubernetes则以文件名称为key

kubectl create configmap elastic-configmap --from-file=./elasticsearch.yaml

3.查看创建的ConfigMap

kubectl describe configmap elastic-configmapName:         elastic-configmapNamespace: defaultLabels: <none>Annotations: <none>
Data====elasticsearch.yaml: #我们没有指定key,默认以文件名称为key----cluster.name: elasticsearchnode.name: elasticpath.data: /usr/local/elastic7.4/datapath.logs: /usr/local/elastic7.4/logsbootstrap.memory_lock: truenetwork.host: 0.0.0.0network.tcp.no_delay: truenetwork.tcp.keep_alive: truenetwork.tcp.reuse_address: truenetwork.tcp.send_buffer_size: 256mbnetwork.tcp.receive_buffer_size: 256mbtransport.tcp.port: 9300transport.tcp.compress: truehttp.max_content_length: 200mbhttp.cors.enabled: truehttp.cors.allow-origin: "*"http.port: 9200cluster.initial_master_nodes: ["127.0.0.1:9300"]xpack.security.enabled: truexpack.license.self_generated.type: basicxpack.security.transport.ssl.enabled: truexpack.monitoring.collection.enabled: true
Events: <none>

也可以以yaml格式显示configmap信息

kubectl get configmap elastic-configmap -o yaml

如果需要指定键名称,如下在文件前面写入key名称即可

kubectl create configmap elastic-configmap --from-file=elastic=./elasticsearch.yaml

如下再查看key名称则变为了我们所指定的elastic

kubectl get configmap elastic-configmap -o yamlapiVersion: v1data:  elastic: |    cluster.name: elasticsearch    node.name: elastic    path.data: /usr/local/elastic7.4/data    path.logs: /usr/local/elastic7.4/logs    bootstrap.memory_lock: true    network.host: 0.0.0.0    network.tcp.no_delay: true    network.tcp.keep_alive: true    network.tcp.reuse_address: true    network.tcp.send_buffer_size: 256mb    network.tcp.receive_buffer_size: 256mb    transport.tcp.port: 9300    transport.tcp.compress: true    http.max_content_length: 200mb    http.cors.enabled: true    http.cors.allow-origin: "*"    http.port: 9200    cluster.initial_master_nodes: ["127.0.0.1:9300"]    xpack.security.enabled: true    xpack.license.self_generated.type: basic    xpack.security.transport.ssl.enabled: true    xpack.monitoring.collection.enabled: truekind: ConfigMapmetadata:  creationTimestamp: "2020-06-05T07:00:45Z"  managedFields: - apiVersion: v1    fieldsType: FieldsV1    fieldsV1:      f:data: .: {}        f:elastic: {}    manager: kubectl    operation: Update    time: "2020-06-05T07:00:45Z"  name: elastic-configmap namespace: default  resourceVersion: "418525"  selfLink: /api/v1/namespaces/default/configmaps/elastic-configmap  uid: 16e8074a-4d4e-4e6e-b704-339876357951

通过目录创建ConfigMap

如果配置文件数量较多时,kubectl还提供了基于目录直接将多个文件分别收纳为键值数据的ConfigMap资源创建方式,将--from-file选项后所跟的路径指向一个目录路径就能把目录下的所有文件一同创建同一哥个 ConfigMap 资源中,命令格式如下: kubectl create configmap <configmap_name> --from-file=<path-to-directory>

如下命令所示,将/data/configs/nginx/conf.d目录下的所有文件都保存于nginx-config-files对象中

ls /data/configs/nginx/conf.d/myserver.conf  myserver-gzip.cfg  myserver-status.cfg
kubectl create configmap nginx-config-files --from-file=/data/configs/nginx/conf.d/

查看创建的configmap对象

注意:kubectl describe 和 kubectl get -o yaml 命令都可以显示由文件创建的键值,不过两者使用的键和值之间的分隔符不同

kubectl describe configmap nginx-config-filesName:         nginx-config-filesNamespace: defaultLabels: <none>Annotations: <none>
Data====myserver-gzip.cfg:----gzip on;gzip_comp_level 5;
myserver-status.cfg:----     location /ngx_status     {    stub_status on;    access_log off;    allow 127.0.0.1;    deny all; }
myserver.conf:----server {  listen 8080;  server_name www.k8sops.cn;
 include /etc/nginx/conf.d/myserver-*.cfg;  location / {      root /usr/lshare/nginx/html; }}
Events: <none>
kubectl get configmap nginx-config-files -o yamlapiVersion: v1data:  myserver-gzip.cfg: |    gzip on;    gzip_comp_level 5;  myserver-status.cfg: "     location /ngx_status     {\n  \tstub_status on;\n  \taccess_log    off;\n\tallow 127.0.0.1;\n \tdeny all;\n \t}\n"  myserver.conf: "server {\n\tlisten 8080;\n\tserver_name www.k8sops.cn;\n\n\tinclude /etc/nginx/conf.d/myserver-*.cfg;\n\tlocation / {\n\t    root /usr/lshare/nginx/html;\n\t}\n}\n"kind: ConfigMapmetadata:  creationTimestamp: "2020-06-05T07:44:16Z"  managedFields: - apiVersion: v1    fieldsType: FieldsV1    fieldsV1:      f:data: .: {}        f:myserver-gzip.cfg: {}        f:myserver-status.cfg: {}        f:myserver.conf: {}    manager: kubectl    operation: Update    time: "2020-06-05T07:44:16Z"  name: nginx-config-files namespace: default  resourceVersion: "426082"  selfLink: /api/v1/namespaces/default/configmaps/nginx-config-files  uid: d83d033c-c478-495f-b9c6-6fba829ab52a

通过资源配置清单创建

基于配置文件创建ConfigMap时,它所使用的字段通常包括apiVersionkind和metadata字段,以及用于存储数据的关键字段data。下面就使用配置清单创建一个ConfigMap

cat configmap.yamlapiVersion: v1kind: ConfigMapmetadata:  name: configmap-demodata:  log_level: INFO  log_file: /var/log/test.log
kubectl describe configmap configmap-demoName:         configmap-demoNamespace: defaultLabels: <none>Annotations:Data====log_file:----/var/log/test.loglog_level:----INFOEvents: <none>

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

4. ConfigMap载入Pod方式之环境变量

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

valueFrom:  configMapKeyRef:    key:    name:    optional:

字段name值为要引用的ConfigMap对象的名称 字段key可用于指定要引用ConfigMap对象中某键的键名 字段optional用于为当前Pod资源指明此引用是否为可选

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

传递ConfigMap中的单个Key

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

cat busybox.yamlapiVersion: v1kind: ConfigMapmetadata:  name: busybox-httpd-config namespace: defaultdata:  httpd_port: "8080"  verbose_level: "-vv"---apiVersion: v1kind: Podmetadata:  name: configmap-env-demo namespace: defaultspec:  containers: - image: busybox    name: busybox-httpd    command: [ "/bin/httpd" ]    args: [ "-f","-p","$(HTTPD_PORT)","$(HTTPD_LOG_VERBOSE)" ]    env: - name: HTTPD_PORT                            #定义第一个变量名称      valueFrom: #引用变量        configMapKeyRef: #引用来自configMap的变量          name: busybox-httpd-config            #指定ConfigMap的名称          key: httpd_port                                  #指定ConfigMap中要引用的key名称 - name: HTTPD_LOG_VERBOSE      valueFrom:        configMapKeyRef:          name: busybox-httpd-config          key: verbose_level          optional: true

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

kubectl apply -f busybox.yamlconfigmap/busybox-httpd-config createdpod/configmap-env-demo created
kubectl exec pods/configmap-env-demo -- ps auxPID   USER     TIME  COMMAND 1 root      0:00 /bin/httpd -f -p 8080 -vv 8 root      0:00 ps aux
kubectl exec pods/configmap-env-demo -- env | grep HTTPDHTTPD_PORT=8080HTTPD_LOG_VERBOSE=-vv

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

传递ConfigMap中的所有Key

如果我们要引入的变量有很多呢?此时,为容器依次配置相应的环境变量是一件很烦躁的事情,而且容易出错,对此,Pod资源支持在容器中使用envFrom字段直接将ConfigMap资源中的所有键值一次性地完整导入。格式如下:

spec:  containers: - name: busybox    image: busybox    envFrom: - prefix: HTCPG_      configMapRef:        name: configmap_name        optional: true

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

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

如下:

cat envFrom.yamlapiVersion: v1kind: ConfigMapmetadata:  name: busybox-httpd-config namespace: defaultdata:  httpd_port: "8080"  verbose_level: "-vv"---apiVersion: v1kind: Podmetadata:  name: configmap-env-demo namespace: defaultspec:  containers: - image: busybox    name: busybox-httpd    command: [ "/bin/httpd" ]    args: [ "-f", "-p", "$(HTCPG_httpd_port)", "$(HTCPG_verbose_level)" ]    envFrom: - prefix: HTCPG_      configMapRef:        name: busybox-httpd-config        optional: false

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

kubectl apply -f envFrom.yaml
kubectl exec pods/configmap-env-demo -- ps auxPID   USER     TIME  COMMAND 1 root      0:00 /bin/httpd -f -p 8080 -vv 7 root      0:00 ps aux
kubectl exec pods/configmap-env-demo -- printenv | grep HTCPGHTCPG_verbose_level=-vvHTCPG_httpd_port=8080

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

5. ConfigMap载入Pod方式之存储卷

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

挂载整个存储卷

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

配置Pod资源时,基于存储卷的方式引用ConfigMap对象的方法非常简单,仅需要指明卷名称及要应用的ConfigMap对象名称即可。

1.创建配置文件 我们下面创建三个Nginx配置文件,然后将这三个配置文件挂载到Nginx的配置目录中

在我们的宿主机上创建我们的文件目录

k8sops@k8s-master01:~$ mkdir data/nginx/{conf.d,html} -p

创建配置文件vhost.conf

cat data/nginx/conf.d/vhost.confserver {        listen       80;    server_name  www.k8sops.cn;
   include /etc/nginx/conf.d/*.conf;        location  / {            root   /usr/share/nginx/html;            index index.html index.htm; }}

创建配置文件gzip.conf

cat data/nginx/conf.d/gzip.cfg    gzip on;    gzip_min_length 1k;    gzip_buffers 16 64K;    gzip_http_version 1.1;    gzip_comp_level 6;    gzip_types text/plain application/x-javascript text/css application/xml application/javascript;    gzip_vary on;    gzip_proxied any;    underscores_in_headers on;    proxy_ignore_client_abort on;

创建配置文件ngx-status.conf

cat data/nginx/conf.d/ngx-status.cfg     location /ngx_status     {        stub_status on;        access_log off;        allow 127.0.0.1;        deny all; }

2.创建ConfigMap对象

基于目录创建nginx-config-files对象

kubectl create configmap nginx-config-files --from-file=./data/nginx/conf.d/

查看创建的configmap

kubectl describe configmap nginx-config-files

3.创建Pod资源清单来引用ConfigMap对象并将其挂载至相应指定的目录中

cat configmap-volume-pod.yamlapiVersion: v1kind: Podmetadata:  name: configmap-volume-pod namespace: defaultspec:  containers: - name: configmap-containers    image: nginx:latest    volumeMounts: #卷挂载配置 - name: ngx-config                                #卷名称      mountPath: /etc/nginx/conf.d/ #挂载到容器中的路径目录      readOnly: true #是否只读  volumes: #卷配置 - name: ngx-config                                    #定义一个卷名称    configMap: #configMap配置      name: nginx-config-files                            #指定configMap名称

4.创建Pod对象

kubectl apply -f configmap-volume-pod.yaml

5.查看Pod状态

kubectl get pods -o wide | grep configmap-volume-podconfigmap-volume-pod          1/1 Running 0 48s 172.20.1.14   k8s-node01   <none> <none>

6.查看Pod挂载状态

kubectl describe pods/configmap-volume-pod | grep -A 2 Mounts Mounts: /etc/nginx/conf.d/ from ngx-config (ro) /var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro)

7.验证挂载

查看配置

kubectl exec pods/configmap-volume-pod -- ls /etc/nginx/conf.dgzip.cfgngx-status.cfgvhost.conf

测试Nginx配置打印出配置

kubectl exec pods/configmap-volume-pod -- nginx -T

访问stub_status模块

kubectl exec pods/configmap-volume-pod -- curl 2>/dev/null  http://127.0.0.1/ngx_statusActive connections: 1server accepts handled requests 8 8 8Reading: 0 Writing: 1 Waiting: 0

如上可见,nginx-config-files中的三个文件都被添加到来容器中,并实现了由容器应用Nginx加载并生效

挂载存储卷中的部分键值

有时候,用户很可能不期望在容器中挂载某ConfigMap存储卷中的所有文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见。例如前面示例中,用户可能只期望在容器中挂载ConfigMap存储卷后只“导出”其中出vhost.conf和gzip.cfg文件,只提供给传输压缩功能,而不输出 nginx stub status信息,此时将其 volumes 配置段改为如下所示的内容即可。

apiVersion: v1kind: Podmetadata:  name: configmap-volume-pod namespace: defaultspec:  containers: - name: configmap-containers    image: nginx:latest    volumeMounts: #卷挂载配置 - name: ngx-config                    #挂载的卷名称      mountPath: /etc/nginx/conf.d/ #挂载在容器中的目标路径      readOnly: true #是否为只读  volumes: #卷配置 - name: ngx-config                        #定义一个卷名称    configMap: #卷从configMap中取值      name: nginx-config-files                #指定要挂载的configMap名称      items: #nginx-config-files ConfigMap中的键 - key: vhost.conf                                #指定要挂载键        path: vhost.conf                            #挂载的路径        mode: 0644 #挂载后的文件权限 - key: gzip.cfg        path: compression.cfg        mode: 0644

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

key:要引用的键名称,必选字段。•path:对应的键于挂载点目录中生成的文件相对路径,可以不同于键名称,必选字段。•mode:文件的权限模型,可用范围为0到0777。

1.将原来的Pod删除,然后重新创建Pod

kubectl delete pods/configmap-volume-podkubectl apply -f configmap-volume-pod.yaml

2.验证Nginx配置文件

kubectl exec pods/configmap-volume-pod -- nginx -Tkubectl exec pods/configmap-volume-pod -- ls /etc/nginx/conf.dcompression.cfgvhost.conf

独立挂载存储卷中的键值

上面的两种方式中,无论是装载所有文件还是部分文件,挂载点目录下原有的文件都会被隐藏或者称为覆盖,在我们没有挂载的时候,/etc/nginx/conf.d目录下有default.conf文件,当我们挂载之后default.conf就被隐藏或者说覆盖掉了,但有时候我们希望挂载进的文件不覆盖相应目录下的其它文件,这个时候就可以通过volumeMounts属性中的subPath字段来解决,它可以支持用户从存储卷挂载单个文件或者单个目录而非整个存储卷。

例如下面示例 /etc/nginx/conf.d 目录中挂载一个 vhost.conf 文件,并且不覆盖原来目录下的 default.conf 文件。/usr/share/nginx/html目录中挂载一个configmap.html文件,并且不覆盖原来目录下的 index.html 文件。

1.删除原来创建的ConfigMap和Pod

kubectl delete configmap nginx-config-fileskubectl delete pods/configmap-volume-pod

2.创建两个ConfigMap,分别用于nginx配置文件和nginx网页文件

#创建nginx配置文件configmapkubectl create configmap nginx-config-files --from-file=./data/nginx/conf.d/
#写一个网页文件cat data/nginx/html/configmap.html<h1>ConfigMap-Volume-Pod-Mounts</h1>
#创建nginx网页文件configmapkubectl create configmap nginx-html-files --from-file=./data/nginx/html/configmap.html

3.创建Pod资源配置清单

apiVersion: v1kind: Podmetadata:  name: configmap-volume-pod namespace: defaultspec:  containers: - name: configmap-containers    image: nginx:latest    volumeMounts: - name: ngx-config      mountPath: /etc/nginx/conf.d/vhost.conf      subPath: vhost.conf      readOnly: true - name: ngx-html      mountPath: /usr/share/nginx/html/configmap.html      subPath: configmap.html      readOnly: true  volumes: - name: ngx-config    configMap:      name: nginx-config-files - name: ngx-html    configMap:      name: nginx-html-files

4.创建Pod对象

kubectl apply -f configmap-volume-pod.yaml

5.查看Pod对象挂载状态

kubectl describe pods/configmap-volume-pod | grep -A 3 Mounts Mounts: /etc/nginx/conf.d/vhost.conf from ngx-config (ro,path="vhost.conf") /usr/share/nginx/html/configmap.html from ngx-html (ro,path="configmap.html") /var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro)

6.验证Pod Nginx配置

kubectl exec pods/configmap-volume-pod -- ls /etc/nginx/conf.ddefault.confvhost.conf
kubectl exec pods/configmap-volume-pod -- nginx -T

7.验证Pod网页文件挂载配置

#查看文件是否存在kubectl exec pods/configmap-volume-pod -- ls -lrth /usr/share/nginx/htmltotal 12K-rw-r--r-- 1 root root 612 May 26 15:00 index.html-rw-r--r-- 1 root root 494 May 26 15:00 50x.html-rw-r--r-- 1 root root  37 Jun 9 07:02 configmap.html
#访问configmap网页文件curl http://172.20.1.17/configmap.html<h1>ConfigMap-Volume-Pod-Mounts</h1>

6. 使用ConfigMap资源的注意事项

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

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


上一篇:两种方法,轻松上手ConfigMap!


下一篇:Kubernetes 在本来生活网的落地实践