大家好,我是腾意。

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

1.1 容器化应用配置方式

每个应用程序都是一个可执行程序文件,它包含操作码列表,CPU通过执行这些操作码来完成特定的操作。例如,cat命令是由/usr/bin/cat文件提供的,该文件含有机器指令的列表,在屏幕上显示指定文件的内容时需要使用这些机器指令。几乎每个程序的行为都可以通过其命令行选项及参数或配置文件来按需定制。实践中,人们通常不会以默认的配置参数运行应用程序,而是需要根据特定的环境或具体的需求定制其运行特性,对于复杂的服务类应用程序更是如此,如Nginx、Tomcat和HBase等,而且通过配置文件定义其配置通常是首选甚至是唯一的途径。

那么,如何为容器中的应用提供配置信息呢?例如,为Nginx配置一个特定Server或指定worker进程的数量,为Tomcat的JVM配置其堆内存的大小等。传统实践中,通常有这么几种途径:启动容器时直接向命令传递参数、将定义好的配置文件硬编码于(嵌入)镜像文件中、通过环境变量(Environment Variables)传递配置数据,以及基于Docker卷传送配置文件等。

1.通过命令行参数进行配置

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


docker run[OPTIONS]IMAGE[COMMAND][ARG...]

其中的[COMMAND]即为自定义运行的程序,[ARG]则是传递给程序的参数。若定义相关的镜像文件时使用了ENTRYPOINT指令,则[COMMAND]和[ARG]都会被当作命令行参数传递给ENTRYPOINT指令中指定的程序,除非为docker run命令额外使用--entrypoint选项覆盖ENTRYPOINT而指定运行其他程序。相关的使用详情请参考Docker的相关教程。

在Kubernetes系统上创建Pod资源时,也能够向容器化应用传递命令行参数,甚至指定运行其他应用程序,相关的字段分别为pods.spec.containers.command和pods.spec.containers.args。这一点在前面相关的章节中已有相关的使用说明。

2.将配置文件嵌入镜像文件

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

这种方式的优势在于对于用户来说简单易用,不用任何额外的设定就能启动符合需求的容器,用于Kubernetes环境亦无须多余的配置。但配置文件相关的任何额外的修改需求都不得不通过重新构建镜像文件来实现,路径长、效率低。

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

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

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

一般说来,容器的ENTRYPOINT脚本应该为这些环境变量提供默认值,以便在用户未为环境变量传值时也能基于此类需要环境变量的镜像启动容器。使用环境变量这种配置方式的优势在于配置信息的动态化供给,不过,有些应用程序的配置可能会复杂到无法通过key/value格式的环境变量完成。

另外,也可以让容器的ENTRYPOINT启动脚本通过网络中的K/V存储获取配置参数,常用的此类存储系统有Consul或etcd等。这种方式较之简单的环境变量能够提供更复杂的配置信息,因为KV存储系统支持多层级的嵌套数据结构,有一些应用广泛的数据抓取工具能够从KV存储中加载相关的数据并替换于配置文件中,甚至于像confd这类的工具还能在KV中的数据变化时自动将其重载至配置文件中,这一点实现了真正意义上的配置动态化。不过,这种方式为容器化应用引入了额外的依赖条件。

Kubernetes系统支持在为Pod资源配置容器时使用spec.containers.env为容器的环境变量传值从而完成应用的配置。如前所述,这种方式无法应付较复杂的配置场景,因此提供的配置能力也很有限。

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

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

5.借助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.2 通过命令行参数配置容器应用

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

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


apiVersion: v1
kind: Pod
metadata:
  name: command-demo
  labels:
    purpose: demonstrate-command
spec:
  containers:
  - name: command-demo-container
    image: busybox
    command: ["httpd"]
    args: ["-f"]
    ports:
    - containerPort: 80
  restartPolicy: OnFailure

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


spec:
  containers:
  - name: command-demo-container
    image: busybox
    args: ["httpd", "-f"]
    ports:
    - containerPort: 80

由上述的应用示例可见,Kubernetes配置文件中的command对应于Dockerfile中的ENTRYPOINT,而配置文件中的args则对应于Dockerfile中的CMD。在Kubernetes中只给出command字段时,它会覆盖Dockerfile中的ENTRYPOINT和CMD,只给出args字段时,它仅覆盖CMD,而同时给出command和args时,会对应覆盖ENTRYPOINT和CMD。其应用生效的示例如图8-1所示。

通过命令行参数的方式向容器应用传递配置数据的操作比较简单,但其功能有限,难以为应用生成复杂配置,尤其是那些不支持通过命令行参数进行的配置将无法基于这种机制来实现。另外需要注意的是,在容器创建完成后,修改command和args并不会直接生效,除非重建Pod对象。

img

图8-1 command/args和ENTRYPOINT/CMD

1.3 利用环境变量配置容器应用

于运行时配置Docker容器中应用程序的第二种方式是在容器启动时向其传递环境变量。Docker原生的应用程序应该使用很小的配置文件,并且每一项参数都可由环境变量或命令行选项覆盖,从而能够在运行时完成任意的按需配置。然而,目前只有极少一部分应用程序为容器环境原生设计,毕竟为容器原生重构应用程序工程浩大,且旷日持久。好在有通过容器启动脚本为应用程序预设运行环境的方法可用,通行的做法是在制作Docker镜像时,为ENTRYPOINT指令定义一个脚本,它能够在启动容器时将环境变量替换至应用程序的配置文件中,而后再由此脚本启动相应的应用程序。基于这类镜像运行容器时,即可通过向环境变量传值的方式来配置应用程序。

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

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

·name :环境变量的名称,必选字段。

·value :环境变量的值,通过$(VAR_NAME)引用,逃逸格式为“$$(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、limits.memory、limits.ephemeral-storage、requests.cpu、requests.memory和requests.ephemeral-storage。

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


apiVersion: v1
kind: Pod
metadata:
  name: env-demo
  labels:
    purpose: demonstrate-environment-variables
spec:
  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对象env-demo,而后打印它的环境变量列表、命令及其结果如下:


~]$ kubectl exec env-demo printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=env-demo
MY_NODE_NAME=node02.ilinux.io
MY_NODE_IP=172.16.0.67
MY_POD_NAMESPACE=default
HELLO_WORLD=just a demo
……

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

1.4 应用程序配置管理及ConfigMap资源

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

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

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

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

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

1.4.1 创建ConfigMap对象

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


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

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

1.利用直接值创建

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


kubectl create configmap configmap_name --from-literal=key-name-1=value-1

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


~]$ kubectl create configmap special-config \
--from-literal=special.how=very --from-literal=special.type=charm

“get configmap”命令可用于查看创建的ConfigMap对象special-config的相关信息,例如,如下的命令及其结果:


$ kubectl get configmaps special-config -o yaml
apiVersion: v1
data:
  special.how: very
  special.type: charm
kind: ConfigMap
metadata:
  creationTimestamp: 2018-03-17T05:24:56Z
  name: special-config
  namespace: default
  resourceVersion: "465543"
  selfLink: /api/v1/namespaces/default/configmaps/special-config
  uid: 87b1bda2-29a3-11e8-b246-000c29be4e28

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

2.基于文件创建

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


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

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


~]$ kubectl create configmap nginx-config \
--from-file=./data/configs/nginx/conf.d/myserver.conf

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


~]$ kubectl get configmap nginx-config -o yaml
apiVersion: v1
data:
  myserver.conf: |
    server {
        listen 8080;
        server_name www.ilinux.io;

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

        location / {
            root /usr/share/nginx/html;
        }
    }
kind: ConfigMap
……

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


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

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

3.基于目录创建

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


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

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


~]$ kubectl create configmap nginx-config-files --from-file=./data/configs/
    nginx/conf.d/

此目录中包含myserver.conf、myserver-status.cfg和myserver-gzip.cfg三个配置文件。创建ConfigMap资源时,它们会被分别存储为三个键值数据,如下面的命令及其结果所示:


~]$ kubectl get cm nginx-config-files -o yaml
apiVersion: v1
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;
        }
    }
kind: ConfigMap
……

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

4.使用清单创建

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


apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap-demo
  namespace: default
data:
  log_level: INFO
  log_file: /var/log/test.log

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

1.4.2 向Pod环境变量传递ConfigMap对象键值数据

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


valueFrom:
  configMapKeyRef:
    key:
    name:
      optional:

其中,字段name的值为要引用的ConfigMap对象的名称,字段key可用于指定要引用ConfigMap对象中某键的键名,而字段optional则用于为当前Pod资源指明此引用是否为可选。此类环境变量的使用方式与直接定义的环境变量并无区别,它们可被用于容器的启动脚本或直接传递给容器应用等。

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


apiVersion: v1
kind: ConfigMap
metadata:
  name: busybox-httpd-config
  namespace: default
data:
  httpd_port: "8080"
  verbose_level: "-vv"
---
apiVersion: v1
kind: Pod
metadata:
  name: configmap-env-demo
  namespace: default
spec:
  containers:
  - image: busybox
    name: busybox-httpd
    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

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


~]$ kubectl exec configmap-env-demo ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -p 8080 -vv

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

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


spec:
  containers:
  - image: some-image
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进行配置的效果并无不同:


apiVersion: v1
kind: Pod
metadata:
  name: configmap-envfrom-demo
  namespace: default
spec:
  containers:
  - image: busybox
    name: busybox-httpd
    command: ["/bin/httpd"]
    args: ["-f","-p","$(HTCFG_httpd_port)","$(HTCFG_verbose_level)"]
    envFrom:
    - prefix: HTCFG_
      configMapRef:
        name: busybox-httpd-config
        optional: false

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


~]$ 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

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

1.4.3 ConfigMap存储卷

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

1.挂载整个存储卷

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

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


apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: nginx-server
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: ngxconfig
    configMap:
      name: nginx-config-files

此Pod资源引用的nginx-config-files中包含三个配置文件,其中myserver.conf定义了一个虚拟主机www.ilinux.io ,并通过include指令包含/etc/nginx/conf.d/目录下以“myserver-”为前缀且以.cfg为后缀的所有配置文件,例如在nginx-config-files中包含的myserver-status.cfg和myserver-gzip.cfg。具体的配置内容请参考1.4.1节下第3小节中命令的输出。ConfigMap存储卷中的文件如图8-2所示。

img

图8-2 ConfigMap存储卷中的文件

创建此Pod资源后于Kubernetes集群中的某节点直接向Pod IP的8080端口发起访问请求,即可验证由nginx-config-files资源提供的配置信息是否生效,例如,通过/nginx-status访问其内建的stub status:


~]$ POD_IP=$(kubectl get pods configmap-volume-demo -o go-template={{.status.
    podIP}})
~]$ curl http://${POD_IP}:8080/nginx-status
Active connections: 1
server accepts handled requests
 3 3 3
Reading: 0 Writing: 1 Waiting: 0

当然,我们也可以直接于Pod资源的相应容器上执行命令来确认文件是否存在于挂载点目录中:


~]$ kubectl exec configmap-volume-demo ls /etc/nginx/conf.d/
myserver-gzip.cfg
myserver-status.cfg
myserver.conf

进一步地,还可以于容器中运行Nginx的配置测试及打印命令,确认由ConfigMap资源提供的配置信息已然生效:


~]$ kubectl exec  configmap-volume-demo -- nginx -T
……
# configuration file /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;
    }
}

# configuration file /etc/nginx/conf.d/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;

# configuration file /etc/nginx/conf.d/myserver-status.cfg:
location /nginx-status {
    stub_status on;
    access_log off;
}

由上面两个命令的结果可见,nginx-config-files中的三个文件都被添加到了容器中,并且实现了由容器应用Nginx加载并生效。

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

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


apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo-2
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    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

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

·key :要引用的键名称,必选字段。

·path :对应的键于挂载点目录中生成的文件的相对路径,可以不同于键名称,必选字段。

·mode:文件的权限模型,可用范围为0到0777。

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

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

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

事实上,此种需求可以通过此前第7章中曾经于容器的volumeMounts字段中使用的subPath字段来解决,它可以支持用户从存储卷挂载单个文件或单个目录而非整个存储卷。例如,下面的示例就于/etc/nginx/conf.d目录中单独挂载了两个文件,而保留于了目录下原有的文件。为了以示区别并在后文中便于引用及说明问题,这里将其保存于单独的配置文件configmap-volume-demo-3.yaml中,并将Pod资源命名为configmap-volume-demo-3:


apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo-3
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/myserver.conf
      subPath: myserver.conf
      readOnly: true
    - name: ngxconfig
      mountPath: /etc/nginx/conf.d/myserver-status.cfg
      subPath: myserver-status.cfg
      readOnly: true
  volumes:
  - name: ngxconfig
    configMap:
      name: nginx-config-files

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


~]$ kubectl exec configmap-volume-demo-3 ls /etc/nginx/conf.d
default.conf
myserver-gzip.cfg
myserver.conf

1.4.4 容器应用重载新配置

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

你或许已经发现,挂载ConfigMap存储卷的挂载点目录中的文件都是符号链接,它们指向了当前目录中的“..data”,而“..data”也是符号链接,它指向了名字形如“..2018_03_17_14_41_41.256460087”的目录,这个目录才是存储卷的真正挂载点。例如,查看1.4.3节下第1小节中创建的Pod资源容器中的挂载点中的文件列表,它将显示出类似如下结果:


~]$ kubectl exec -it configmap-volume-demo -- ls -lA /etc/nginx/conf.d
total 0
drwxr-xr-x  2 root   root   79 Mar 17 14:41 ..2018_03_17_14_41_41.256460087
lrwxrwxrwx  1 root   root   31 Mar 17 14:41 ..data -> ..2018_03_17_14_41_41.256460087
lrwxrwxrwx  1 root   root   24 Mar 17 10:19 myserver-gzip.cfg -> ..data/myserver-
    gzip.cfg
lrwxrwxrwx  1 root   root   26 Mar 17 10:19 myserver-status.cfg -> ..data/myserver-
    status.cfg
lrwxrwxrwx  1 root   root   20 Mar 17 10:19 myserver.conf -> ..data/myserver.conf

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


~]$ kubectl exec -it configmap-volume-demo -- ls -lA /etc/nginx/conf.d
total 0
rwxr-xr-x    2 root     root  79 Mar 17 15:17 ..2018_03_17_15_17_11.170451948
lrwxrwxrwx   1 root   root  31 Mar 17 15:17 ..data -> ..2018_03_17_15_17_11.170451948
lrwxrwxrwx   1 root   root  24 Mar 17 10:19 myserver-gzip.cfg -> ..data/myserver-
    gzip.cfg
lrwxrwxrwx   1 root   root  26 Mar 17 10:19 myserver-status.cfg -> ..data/myserver-
    status.cfg
lrwxrwxrwx   1 root   root  20 Mar 17 10:19 myserver.conf -> ..data/myserver.conf

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


~]$ kubectl exec configmap-volume-demo -- nginx -s reload
2018/08/17 15:18:19 [notice] 222#222: signal process started

此时,如果于此容器之外的位置访问/nginx-status页面的请求是被拒绝的,则表明新配置已然生效,如下面的命令及其结果所示:


~]$ curl http://${POD_IP}:8080/nginx-status
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.13.9</center>
</body>
</html>

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

另外,使用1.4.3下第3小节中的方式独立挂载存储卷中的文件的容器,其挂载配置文件的方式并非是以两级链接的方式进行的,因此存储卷无法确保所有挂载的文件可以被同时更新至容器中,因此为了确保配置信息的一致性,目前这种类型的挂载不支持文件更新操作。可对此自行进行验证。

1.4.5 使用ConfigMap资源的注意事项

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

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

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

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

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

1.5 Secret资源

Secret资源的功能类似于ConfigMap,但它专用于存放敏感数据,例如密码、数字证书、私钥、令牌和SSH key等。

1.5.1 Secret概述

Secret对象存储数据的方式及使用方式类似于ConfigMap对象,以键值方式存储数据,在Pod资源中通过环境变量或存储卷进行数据访问。不同的是,Secret对象仅会被分发至调用了此对象的Pod资源所在的工作节点,且只能由节点将其存储于内存中。另外,Secret对象的数据的存储及打印格式为Base64编码的字符串,因此用户在创建Secret对象时也要提供此种编码格式的数据。不过,在容器中以环境变量或存储卷的方式访问时,它们会被自动解码为明文格式。

需要注意的是,在Master节点上,Secret对象以非加密的格式存储于etcd中,因此管理员必须加以精心管控以确保敏感数据的机密性,必须确保etcd集群节点间以及与API Server的安全通信,etcd服务的访问授权,还包括用户访问API Server时的授权,因为拥有创建Pod资源的用户都可以使用Secret资源并能够通过Pod中的容器访问其数据。

Secret对象主要有两种用途,一是作为存储卷注入到Pod上由容器应用程序所使用,二是用于kubelet为Pod里的容器拉取镜像时向私有仓库提供认证信息。不过,后面使用ServiceAccount资源自建的Secret对象是一种更具安全性的方式。通过ConfigMap和Secret配置容器的方式如图8-3所示。

img

图8-3 通过ConfigMap和Secret配置容器

Secret资源主要由四种类型组成,具体如下。

·Opaque:自定义数据内容;base64编码,用来存储密码、密钥、信息、证书等数据,类型标识符为generic。

·kubernetes.io/service-account-token:Service Account的认证信息,可在创建Service Accout时由Kubernetes自动创建。

·kubernetes.io/dockerconfigjson:用来存储Docker镜像仓库的认证信息,类型标识为docker-registry。

·kubernetes.io/tls:用于为SSL通信模式存储证书和私钥文件,命令式创建时类型标识为tls。

注意 base64编码并非加密机制,其编码的数据可使用“base64--decode”一类的命令进行解码。

1.5.2 创建Secret资源

手动创建Secret对象的方式有两种:通过kubectl create命令和使用Secret配置文件。

1.命令式创建

不少场景中,Pod中的应用需要通过用户名和密码访问其他服务,例如访问数据库系统等。创建此类的Secret对象,可以使用“kubectl create secret generic--from-literal=key=value”命令直接进行创建,不过为用户认证之需进行创建时,其使用的键名通常是username和password。例如下面的命令,以“root/ikubernetes”分别为用户名和密码创建了一个名为mysql-auth的Secret对象:


~]$ kubectl create secret generic mysql-auth --from-literal=username=root \
--from-literal=password=ikubernetes

而后即可查看新建资源的属性信息,由下面的命令及其输出结果可以看出,以generic标识符创建的Secret对象是为Opaque类型,其键值数据会以Base64的编码格式进行保存和打印:


~]$ kubectl get secrets mysql-auth -o yaml
apiVersion: v1
data:
  password: aWt1YmVybmV0ZXM=
  username: cm9vdA==
kind: Secret
metadata:
……
type: Opaque

不过,Kubernetes系统的Secret对象的Base64编码的数据并非加密格式,许多相关的工具程序均可轻松完成解码,如下面所示的Base64命令:


~]$ echo aWt1YmVybmV0ZXM= | base64 -d
ikubernetes

对于本身业己存储于文件中的数据,也可以在创建generic格式Secret对象时使用“--from-file”选项从文件中直接进行加载,例如创建用于SSH认证的Secret对象时,如果尚且没有认证信息文件,则需要首先使用命令生成一对认证文件:


~]$ ssh-keygen -t rsa -P '' -f  ${HOME}/.ssh/id_rsa

而后使用“kubectl create secret generic--from-file[=KEY1]=/PATH/TO/FILE”命令加载文件内容并生成为Secret对象:


~]$ kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=${HOME}/.
    ssh/id_rsa --from-file=ssh-publickey=${HOME}/.ssh/id_rsa.pub

另外,若要基于私钥和数字证书文件创建用于SSL/TLS通信的Secret对象,则需要使用“kubectl create secret tls--cert=--key=”命令来进行,注意其类型标识符为TLS。例如,假设需要为Nginx测试创建SSL虚拟主机,用户首先使用了类似如下的命令生成了私钥和自签证书:


~]$ (umask 077; openssl genrsa -out nginx.key 2048)
~]$ openssl req -new -x509 -key nginx.key -out nginx.crt \
-subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=www.ilinux.io

而后即可使用如下命令将这两个文件创建为Secret对象。需要注意的是,无论用户提供的证书和私钥文件使用的是什么名称,它们一律会被转换为分别以tls.key(私钥)和tls.crt(证书)为其键名:


~]$ kubectl create secret tls nginx-ssl --key=./nginx.key --cert=./nginx.crt
secret "nginx-ssl" created

注意其类型应该为“kubernetes.io/tls”,例如下面命令结果中的显示:


~]$ kubectl get secrets nginx-ssl
NAME        TYPE                DATA      AGE
nginx-ssl   kubernetes.io/tls   2         37s

由上述操作过程可见,命令式创建Secret对象与ConfigMap对象的方式几乎没有明显区别。

2.清单式创建

Secret资源是标准的Kubernetes API对象,除了标准的apiVersion、kind和metadata字段,它可用的其他字段具体如下。

·data:“key:value”格式的数据,通常是敏感信息,数据格式需是以Base64格式编码的字符串,因此需要用户事先完成编码。

·stringData:以明文格式(非Base64编码)定义的“key:value”数据;无须用户事先对数据进行Base64编码,而是在创建为Secret对象时自动进行编码并保存于data字段中;stringData字段中的明文不会被API Server输出,不过若是使用“kubectl apply”命令进行的创建,那么注解信息中还是可能会直接输出这些信息的。

·type:仅是为了便于编程方式处理Secret数据而提供的类型标识。

下面是保存于配置文件secret-demo.yaml中的Secret资源定义示例,其使用stringData提供了明文格式的键值数据,从而免去了事先进行手动编码的麻烦:


apiVersion: v1
kind: Secret
metadata:
  name: secret-demo
stringData:
  username: redis
  password: redisp@ss
type: Opaque

Secret对象也是Kubernetes系统的“一等公民”,因此,使用标准资源创建命令即可完成其创建。相比较来说,基于清单文件将保存于文件中的敏感信息创建Secret对象时,用户首先需要将敏感信息读出,转为Base64编码格式,而后再将其创建为清单文件,过程烦琐,反不如命令式创建来得便捷。不过,如果存在多次创建或重构之需,那么将其保存为配置清单也是情势所需。

1.5.3 Secret存储卷

类似于Pod消费ConfigMap对象的方式,Secret对象可以注入为环境变量,也可以存储为卷形式挂载使用。不过,容器应用通常会在发生错误时将所有环境变量保存于日志信息中,甚至有些应用在启动时即会将运行环境打印到日志中;另外,容器应用调用第三方程序为子进程时,这些子进程能够继承并使用父进程的所有环境变量。有鉴于此,使用环境变量引用Secret对象中的敏感信息实在算不上明智之举。

在Pod中使用Secret存储卷的方式,除了其类型及引用标识要替换为Secret及secretName之外,几乎完全类似于ConfigMap存储卷,包括支持使用挂载整个存储卷、只挂载存储卷中的指定键值以及独立挂载存储卷中的键等使用方式。

下面是定义在配置文件secret-volume-pod.yaml中的Secret资源使用示例,它将nginx-ssl关联为Pod资源的名为nginxcert的Secret存储卷,而后由容器web-servrer挂载至/etc/nginx/ssl目录下:


apiVersion: v1
kind: Pod
metadata:
  name: secret-volume-demo
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: nginxcert
      mountPath: /etc/nginx/ssl/
      readOnly: true
  volumes:
  - name: nginxcert
    secret:
      secretName: nginx-ssl

将上面资源清单文件中定义的资源创建于Kubernetes系统上,而后再查看容器挂载点目录中的文件,以确认其挂载是否成功完成。下面命令的结果显示,私钥文件tls.key和证书文件tls.crt已经成功保存于挂载点路径之下:


~]$ kubectl exec secret-volume-demo ls /etc/nginx/ssl/
tls.crt
tls.key

此时,通过ConfigMap对象为容器应用Nginx提供HTTPS虚拟主机配置,它只要使用由Secret对象生成的私钥和证书文件,即可定义出容器化运行的Nginx服务。

1.5.4 imagePullSecret资源对象

imagePullSecret资源可用于辅助kubelet从需要认证的私有镜像仓库获取镜像,它通过将Secret提供的密码传递给kubelet从而在拉取镜像前完成必要的认证过程。

使用imagePullSecret的方式有两种:一是创建docker-registry类型的Secret对象,并在定义Pod资源时明确通过“imagePullSecrets”字段给出;另一个是创建docker-registry类型的Secret对象,将其添加到某特定的ServiceAccount对象中,那些使用该ServiceAccount资源创建的Pod对象,以及默认使用该ServiceAccount的Pod对象都将会直接使用imagePullSecrets中的认证信息。由于尚未介绍到ServiceAccount资源,因此这里先介绍第一种方式的用法。

创建docker-registry类型的Secret对象时,要使用“kubectl create secret docker-registry--docker-user=--docker-password=--docker-email=”的命令格式,其中的用户名、密码及邮件信息是在使用docker login命令登录时使用的认证信息。例如,下面的命令创建了名为local-registry的image pull secret对象:


~]$ kubectl create secret docker-registry local-registry --docker-username=Ops \
--docker-password=Opspass --docker-email=ops@ilinux.io

此类Secret对象打印的类型信息为“kubernetes.io/dockerconfigjson”,如下面的命令结果所示:


~]$ kubectl get secrets local-registry
NAME             TYPE                             DATA      AGE
local-registry   kubernetes.io/dockerconfigjson   1         7s

而后,使用相应的私有registry中镜像的Pod资源的定义,即可通过imagePullSecrets字段使用此Secret对象,使用示例如下面的配置清单所示:


apiVersion: v1
kind: Pod
metadata:
  name: secret-imagepull-demo
  namespace: default
spec:
  imagePullSecrets:
  - name: local-registry
  containers:
  - image: registry.ilinux.io/dev/myimage
    name: myapp

上面的配置清单仅是一个示例,付诸运行时,需要将其Secret中的内容及清单资源的镜像等信息的定义修改为实际可用的信息。

试想,正在运行的多数容器的镜像均来自于私有仓库时,为每个Pod资源显式定义imagePullSecrets实在不是一个好主意。好在还有基于ServiceAccount的image pull secret可用。

1.6 本篇小结

本篇的核心目标在于说明配置容器应用的常用方式,这在将同一Pod资源分别以不同的配置运行于不同的环境中时特别有用。本篇主要描述了如下几种配置容器化应用的方式。

·自定义命令行选项,为容器化应用传递特定参数。

·通过环境变量向容器注入自定义数据。

·基于ConfigMap对象,以环境变量或存储卷的形式向容器提供配置信息。

·借助Secret对象,以存储卷的形式向容器应用提供敏感信息。

版权声明:如无特殊说明,文章均为本站原创,版权所有,转载需注明本文链接

本文链接:http://www.bianchengvip.com/article/Kubernetes-provides-config-information-to-POD/