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

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

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

  • name: 环境变量的名称,必选;
  • value:环境变量的值,通过$(VAR_NAME)引用,逃逸格式为$$(VAR_NAME),默认值为空;
  • valueFrom: 环境变量的引用源,例如,当前Pod资源的名称、名称空间、标签等,不能与非空值的value字段同时使用,即环境变量的值要么来源于value字段,要么源于valuefrom字段,二者不可同时提供数据。

valueFrom宇段可引用的值有多种来源,包括当前Pod资源的属性值,容器相关的系统资源配置、ConfigMap对象中的Key以及Secret对象中的Key,它们应分别使用不同的嵌套字段进行定义:

  • fieldRef: 当前Pod资源的指定字段,目前仅支持使用的字段包括:metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs;
  • configMapKeyRef: ConfigMap对象中的特定Key;
  • secretKeyRef: Secret对象中的特定Key;
  • resourceFieldRef: 当前容器的特定系统资源的最小值或最大值,目前支持的引用包括:limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage;

下面示例文件env-dome,yaml的Pod资源,其通过环境变量引用当前Pod资源及其所在的节点的相关属性值配置容器:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: env-demo
  5. labels:
  6. purpose: demo-env-variables
  7. spec:
  8. containers:
  9. - name: env-demo
  10. image: busybox
  11. command: ["http"]
  12. args: ["-f"]
  13. env:
  14. - name: HELLO_WORLD
  15. value: demo
  16. - name: MY_NODE_NAME
  17. valueFrom:
  18. fieldRef:
  19. fieldPath: spec.nodeName
  20. - name: MY_NODE_IP
  21. valueFrom:
  22. fieldRef:
  23. fieldPath: status.hostIP
  24. - name: MY_POD_NAMESPACE
  25. valueFrom:
  26. fieldRef:
  27. fieldPath: metadata.namespace
  28. restartPolicy: OnFailure
  1. # kubectl exec env-demo printenv
  2. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  3. HOSTNAME=env-demo
  4. HELLO_WORLD=demo
  5. MY_NODE_NAME=k8s-test02
  6. MY_NODE_IP=172.17.20.28
  7. MY_POD_NAMESPACE=default

不过,这种配置方式有一个缺陷:无法在容器应用运行过程中更新环境变量从而达到更新应用之目的。这通常意味着用户不得不为production、development和qa等不同的环境分别配置Pod资源。好在,用户还有ConfigMap资源可用。

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

一个ConfigMap对象就是一系列配置数据的集合,这些数据可“注入”到Pod对象中,并为容器应用所使用,注入方式有挂载为存储卷和传递为环境变量两种。

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

2.1 创建ConfigMap对象

  1. 利用直接值创建
    kubectl create configmap命令使用--from-literal选项可在命令行直接给出键值对来创建ConfigMap对象,重复使用此选项则可以传递多个键值对。命令格式如下:
  1. kubectl create configmap configmap-name --from-literal=special.type=charm --from-literal=special.host=very # 注意configmap-name不要用_

get configmap命令可用于查看创建的ConfigMap对象special-config的相关信息:

  1. # kubectl get configmaps configmap-name -o yaml
  2. apiVersion: v1
  3. data:
  4. special.host: very
  5. special.type: charm
  6. kind: ConfigMap
  7. metadata:
  8. creationTimestamp: "2020-12-25T11:31:38Z"
  9. name: configmap-name
  10. namespace: default
  11. resourceVersion: "23717274"
  12. selfLink: /api/v1/namespaces/default/configmaps/configmap-name
  13. uid: 8dacbbb2-4ca5-48a2-afcc-2e44c88956a5
  1. 基于文件创建
    kubectl create configmap命令使用--from-file选贤即可基于文件内容来创建ConfigMap对象,可以重复多次使用—from-file选贤传递多个文件内容:
  1. kubectl create configmap <configmap-name> --from-file=<path-to-file1> --from-file=<path-to-file2>

如果需要自行指定键名,则可在—from-file选贤中直接指定自定义的健:kubectl create configmap <configmap-name> --from-file=<my-key-name>=<path-to-file>

  1. # kubectl create configmap nginx-config \
  2. --from-file= ./data/configs/nginx/conf.d/myserver.conf
  3. # kubectl get configmap nginx-config -o yaml
  4. apiVersion: v1
  5. data :
  6. myserver.conf: |
  7. server {
  8. listen 8080;
  9. server_name ilinux.io;
  10. include /etc/nginx/conf.d/myserver-*.cfg;
  11. location / {
  12. root /usr/share/nginx/html;
  13. }
  14. }
  15. kind: ConfigMap
  16. .....
  1. 基于目录创建
    如果配置文件数量较多且存储于有限的目录中时, kubectl还提供了基于目录直接将多个文件分别收纳为键值数据的ConfigMap资源创建方式:
  1. # kubectl create configmap <configmap-name> --from-file=<path-to-directory>

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

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

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

  1. # kubectl get cm nginx-config-files -o yaml
  2. apiVersion: v1
  3. data:
  4. myserver-gzip.cfg: |
  5. gzip on;
  6. gzip_comp_level 5;
  7. gzip_proxied expired on-cache no-store private auth;
  8. gzip_types text/plain text/css application/xml text/javascript;
  9. myserver-status.cfg: |
  10. location /nginx-status {
  11. stub_status on;
  12. access_log off;
  13. }
  14. myserver.conf: |
  15. server {
  16. listen 8080;
  17. server_name www.ilinux.io;
  18. include /etc/nginx/conf.d/myserver-*.cfg;
  19. location / {
  20. root /usr/share/nginx/html;
  21. }
  22. }
  23. kind: ConfigMap
  1. 使用清单创建
    基于配置文件创建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

2.2 Pod中使用ConfigMap

Pod资源引用ConfigMap对象中的数据,这一点通过在env宇段中为valueFrom内嵌configMapKeyRef对象即可实现:

  • name: 要引用的ConfigMap对象的名称;
  • key: 要引用ConfigMap对象中某键的键名,必选;
  • optional : 当前Pod资源指明此引用是否可选;

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

apiVersion: v1
kind: ConfigMap
metadata:
  name: busybox-httpd-config
  namespace: default
data:
  http_port: "8080"
  verbose_leverl: "-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
# kubectl exec configmap-env-demo ps aux
PID   USER     TIME  COMMAND
1 root      0:00 /bin/httpd -f -p 8080 -vv

某ConfigMap资源中存在较多的键值数据,而全部或大部分的这些键值数据都需要由容器来引用。对此,Pod资源支持在容器中使用envFrom字段直接将ConfigMap资源中的所有键值一次性地完成导人。

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

例如,把上面示例中的Pod资源转为如下形式的定义(configmap-envfrom-pod.yaml):

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

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

2.3 ConfigMap存储卷

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

2.3.1 挂载整个存储卷

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

下面示例,容器nginx-server将由nginx-config-files创建的ConfigMap对象挂载至应用程序Nginx加载配置文件模块的目录/etc/nginx/conf.d:

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

2.3.2 挂载存储卷中的部分键值

有时候,不期望在容器中挂载某ConfigMap存储卷后于挂载点目录导出所有的文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见。

configMap存储卷的items字段的值是一个对表列表,可嵌套使用的字段有三个:

  • key: 要引用的键名称,必选;
  • path:对应的键于挂载点目录中生成的文件的相对路径,可以不同于键名称,必选;
  • mode:文件的权限模型;

例如前面的示例中,用户可能只期望在容器中挂载ConfigMap存储卷后只“导出”其中的myserver.conf和myserver-gzip.cfg,只提供页面传输压缩功能,而不输出nginx stub status信息,此时将其volumes配置段改为如下所示的内容即可:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-demo-2
spec:
  containers:
  - name: nginx-server
    image: nginx:alpine
    volumeMounts:
    - name: ngxconfig
      mountPath: /etc/nginx/config.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

2.3.3 独立挂载存储卷中的键值

对于期望将ConfigMap对象提供的配置文件补充于挂载点目录下的需求来说,此种需求可以通过容器的volumeMounts字段中使用的subPath字段来解决,它可以支持用户从存储卷挂载单个文件或单个目录而非整个存储卷。

下面的示例就于/etc/nginx/conf.d目录中单独挂载了两个文件,而保留于了目录下原有的文件:

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

2.4 使用ConfigMap资源的注意事项

  • 以存储卷方式引用的ConfigMap必须先于Pod存在,除非在Pod中将它们全部标记为”optional”,否则将会导致Pod无法正常启动的错误;同样,即使存在ConfigMap,在引用的键不存在时,也会导致一样的错误。
  • 当以环境变量方式注入的ConfigMap中的键不存在时会被忽略,Pod可以正常启动,但错误引用的信息会以“InvalidVariableNames”事件记录于日志中。
  • ConfigMap是名称空间级的资源,因此,引用它的Pod必须处于同一名称空间中。

3.Secret资源

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

3.1 Secret 概述

Secret对象存储数据的方式及使用方式类似于ConfigMap对象,以键值方式存储数据,在Pod资源中通过环境变量或存储卷进行数据访问。不同的是,Secret对象仅会被分发至调用了此对象的Pod资源所在的工作节点,且只能由节点将其存储于内存中。另外,Secret对象的数据的存储及打印格式为Base64编码的字符串,因此用户在创建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”一类的命令进行解码

3.2 创建Secret资源

3.2.1 命令式创建

Pod中的应用需要通过用户名和密码访问其他服务,例如访问数据库系 统等。创建此类的Secret对象,可以使用kubectl create secret generic <SECRET_NAME> --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

查看结果:

# kubectl get secrets mysql-auth -o yaml
apiVersion: v1
data:
  password: aWt1YmVybmV0ZXM=
  username: cm9vdA==
kind: Secret
metadata:
  ...
type: Opaque

对于本身业己存储于文件中的数据,也可以在创建generic格式Secret对象时使用“一from-file”选项从文件中直接进行加载,使用kubectl create secret generic <SECRET_NAME> --from-file[=KEY1]=/FILE_PATH命令加载文件并生成为Secret对象。

另外,若要基于私钥和数字证书文件创建用于SSL/TLS通信的Secret 对象,则需要使用kubectl create secret tls<SECRET_NAME> --cert= --key=命令来进行,注意其类型标 识符为TLS。

3.2.2 清单式创建

Secret资源是标准的KubernetesAPI对象,除了标准的apiVersion、kind和metadata字段:

  • data: key:value格式的数据,通常是敏感信息,数据格式需是 以Base64格式编码的字符串,因此需要用户事先完成编码;
  • stringData: 以明文格式(非Base64编码)定义的“key:value” 数据;无须用户事先对数据进行Base64编码,而是在创建为Secret对象时自动进行编码并保存于data字段中;stringData字段中的明文不会被API Server输出,不过若是使用kubectl apply命令进行的创建,那么注解信息中还是可能会直接输出这些信息的;
  • type:仅是为了便于编程方式处理Secret数据而提供的类型标识;

下面secret-demo.yaml示例使用stringData提供明文格式的键值数据:

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

3.3 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
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: nginxcert
      mountPath: /etc/nginx/ssl
      readOnly: true
  volumes:
  - name: nginxcert
    secret:
      secretName: nginx-ssl
# kubectl exec secret-volume-demo -- ls /etc/nginx/ssl
tls.crt
tls.key

4.小结

  • 自定义命令行选项,为容器化应用传递特定参数;
  • 通过环境变量向容器注入自定义数据;
  • 基于ConfigMap对象,以环境变量或存储卷的形式向容器提供配置信息;
  • 借助Secret对象,以存储卷的形式向容器应用提供敏感信息;