7.4.1 ConfigMap 介绍

Kubernetes允许将配置选项分离到单独的对象ConfigMap中,本质上就是一个键/值对映射,值可以是短字面量(short literal),也可以是完整的配置文件。

应用无须直接读取ConfigMap甚至根本不需要知道其是否存在。映射的内容通过环境变量或者卷中的文件(如图7.2所示)的形式传递给容器。命令行参数可以通过 $(ENV_VAR) 语法引用环境变量,因此也可以将ConfigMap条目(entry)当作命令行参数传递给进程。
image.png

当然,应用程序同样可以通过Kubernetes REST API按需直接读取ConfigMap的内容,不过除非是需求如此,应尽可能使你的应用保持对Kubernetes的无感知。

不管应用具体是如何使用ConfigMap的,将配置存放在独立的对象中有助于在不同环境(开发、测试、质量保障和生产等)下拥有多份同名的ConfigMap清单(manifest)。pod是通过名称引用ConfigMap的,因此可以在多环境下使用相同的pod描述(specification),同时保持不同的配置以适应不同环境(如图7.3所示)。
image.png

7.4.2 创建ConfigMap

先创建一个仅包含单一键的映射,并用它填充之前示例中的环境变量INTERVAL。

ConfigMap的键名规范

ConfigMap中的键名必做是一个合法的DNS子域(仅包含: . 数字 字母 - _ ),首位的圆点( . )是可选的。

使用指令kubectl创建ConfigMap

利用 kubectl 创建 ConfigMap 的映射条目时可以指定字面量或者存储在磁盘上的文件。

创建一个简单的字面量条目:
image.png
image.png

通过添加多个—from-literal参数可创建包含多条目的ConfigMap:
image.png
#观察ConfigMap的YAML描述文件:
image.png
#本人实验环境命令执行情况(K8S 1.21)
image.png

从文件内容创建ConfigMap条目

ConfigMap 同样可以存储粗粒度的配置数据,比如完整的配置文件。

将文件内容存储为单独的ConfigMap条目:
image.png
运行上述命令时,kubectl会在当前目录下查找config-file.conf文件,并将文件内容存储在ConfigMap中以config-file.conf为键名的条目下。

当然也可以手动指定键名:
image.png
这条命令会将文件内容存在键名为customkey的条目下。

多次使用—from-file参数可添加多个文件。

从文件夹创建ConfigMap

image.png
kubectl会为文件夹中的每个文件单独创建条目,仅限于那些文件名可作为合法ConfigMap键名的文件。

混合使用不同选项

创建ConfigMap时可以混合使用这里提到的所有选项:
image.png
如果如下图所示:
image.png

7.4.3 给容器传递 ConfigMap 条目作为环境变量

image.png

这里定义了一个环境变量INTERVAL,并将其值设置为fortune-config ConfigMap中健名为sleep-interval对就应的值。 运行在html-generator容器中的进程读取到环境变量INTERVAL的值为25,如下图所示:

image.png

  1. cd /root/k8s/
  2. cat >fortune-pod-env-configmap.yaml <<'EOF'
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: fortune-env-from-configmap
  7. spec:
  8. containers:
  9. - image: 10.0.0.10:5000/luksa/fortune:env
  10. env:
  11. - name: INTERVAL
  12. valueFrom:
  13. configMapKeyRef:
  14. name: fortune-config
  15. key: sleep-interval
  16. name: html-generator
  17. volumeMounts:
  18. - name: html
  19. mountPath: /var/htdocs
  20. - image: 10.0.0.10:5000/luksa/nginx
  21. name: web-server
  22. volumeMounts:
  23. - name: html
  24. mountPath: /usr/share/nginx/html
  25. readOnly: true
  26. ports:
  27. - containerPort: 80
  28. protocol: TCP
  29. volumes:
  30. - name: html
  31. emptyDir: {}
  32. EOF
  33. kubectl create -f fortune-pod-env-configmap.yaml
  34. kubectl logs fortune-env-from-configmap -c html-generator

注意:mountPath目录可以是容器中不存在的目录,会自动创建。

在pod中引用不存在的ConfigMap

如果创建pod时引用的ConfigMap不存存,Kubernetes会正常调度pod并尝试运行所有容器。然而引用不存在的ConfigMap的容器会启动失败,其余容器能正常启动。如果之后创建了这个缺失的ConfigMap,失败的容器会自己启动,无须重新创建pod。

注意:可以标记对ConfigMap的引用是可选的(设置configMapKeyRef.optional: true)。这样,即使ConfigMap不存在,容器也能正常启动。

7.4.4 一次性传递ConfigMap的所有条目作为环境变量

假设一个ConfigMap包含FOO、BAR和FOO-BAR三个键。可以通过envFrom属性字段将所有条目暴露作为环境变量。
image.png
如你所见,可以为所有环境变量设置前缀prefix,如本例的CONFIG,容器中两个环境变量的名称为:CONFIG_FOO与CONFIG_BAR。由于CONFIG_FOO-BAR包含 - ,并不是一个合法的环境变量名称。Kubernetes不会主动转换键名(例如不会将 - 转换为 )。如果ConfigMap的某键名格式不正确,创建环境变量时会忽略对应的条目(忽略时不会发出事件通知)。

注意:前缀设置是可选的,若不设置前缀值,环境变量的名称与ConfigMap中的键名相同。

7.4.5 传递ConfigMap条目作为命令行参数

在字段pod.spec.containers.args中无法直接引用ConfigMap的条目,但是可以利用ConfigMap条目初始化某个环境变量,然后在args字段中引用该环境变量。如图7.7所示。
image.png
image.png

cd /root/k8s/

cat >fortune-pod-args-configmap.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: fortune-args-from-configmap
spec:
  containers:
  - image: 10.0.0.10:5000/luksa/fortune:args
    env:
    - name: INTERVAL
      valueFrom: 
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval
    args: ["$(INTERVAL)"]
    name: html-generator
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: 10.0.0.10:5000/luksa/nginx
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    emptyDir: {}
EOF

kubectl create -f fortune-pod-args-configmap.yaml
kubectl logs fortune-args-from-configmap -c html-generator

7.4.6 使用configMap卷将条目暴露为文件

环境变量或命令行参数值作为配置选项通常适用于变量值较短的场景。由于ConfigMap中可以包含完整的配置文件内容,当你想要将其暴露给容器时,可以使用configMap卷。

configMap卷会将ConfigMap中的每个条目均暴露成一个文件。运行在容器中的进程可以通过读取文件内容获得对应的条目值。

尽管这种方法主要适用于传递较大的配置文件给容器,同样可以用于传递较短的变量值。

创建ConfigMap

使用配置文件配置在fortune pod的Web服务器容器中的Nginx。如果想要让Nginx服务器压缩传递给客户端的响应报文,Nginx的配置文件需开启gzip配置,如下:
image.png

#删除现有的ConfigMap fortune-config
kubectl delete configmap fortune-config

mkdir /root/k8s/configmap-files
cd /root/k8s/configmap-files

cat >my-nginx-config.conf <<'EOF'
server {
    listen              80;
    server_name         www.kubia-example.com;

    gzip on;
    gzip_types text/plain application/xml;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

}
EOF

echo "25" >sleep-interval

#从文件夹创建ConfigMap:
kubectl create configmap fortune-config --from-file=./

image.png
image.png
注意:所有条目第一行最后的管道符号( | )表示后续的条目值是多行字面量。
image.png

在卷内使用ConfigMap的条目

要创建包含ConfigMap条目内容的卷,只需要创建一个引用ConfigMap名称的卷(configMap字段),并挂载到容器中。

Nginx需读取配置文件/etc/nginx/nginx.conf,而Nginx镜像内的这个文件包含默认配置,并不想完全覆盖这个配置文件。幸运的是,在默认配置文件中会自动嵌入子文件夹/etc/nginx/config.d/下的所有.conf文件,因此只需要将你的配置文件置于该子文件夹中即可。图7.9展示了如何做到这一点。
pod的定义描述如代码清单7.14所示。
image.png
image.png

cd /root/k8s/

cat >fortune-pod-configmap-volume.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume
spec:
  containers:
  - image: 10.0.0.10:5000/luksa/fortune:env
    env:
    - name: INTERVAL
      valueFrom:
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval      #引用ConfigMap条目
    name: html-generator
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: 10.0.0.10:5000/luksa/nginx
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html    #网页
      readOnly: true
    - name: config
      mountPath: /etc/nginx/conf.d        #配置文件
      readOnly: true
#    - name: config
#      mountPath: /tmp/whole-fortune-config-volume
#      readOnly: true
    ports:
      - containerPort: 80
        name: http
        protocol: TCP
  volumes:
  - name: html
    emptyDir: {}
  - name: config
    configMap:
      name: fortune-config
EOF

kubectl create -f fortune-pod-configmap-volume.yaml

检查Nginx是否使用被挂载的配置文件

image.png

检查被挂载的configMap卷的内容

image.png
条目sleep-interval对应的文件也被包含在内,然而它只会被fortuneloop容器所使用。可以创建两个不同的ConfigMap,一个用以配置容器fortuneloop,另一个用来配置web-server,然而采用多个ConfigMap去分别配置同一pod中的不同容器的做法是不好的。毕竟同一pod中的容器是紧密联系的,需要被当作整体单元来配置。

在卷内暴露指定的ConfigMap条目

幸运的是,可以创建仅包含ConfigMap中部分条目的configMap卷——本示例中的条目my-nginx-config.conf。这样容器fortuneloop不会受到影响,条目sleep-interval会作为环境变量传递给容器而不是以卷的方式。

通过卷的items属性能够指定哪些条目会被暴露作为configMap卷中的文件,如下代码清单
image.png
指定单个条目时需同时设置条目的键名以及存放条目值的文件相对路径。如果采用上面的配置文件创建pod,/etc/nginx/conf.d/文件夹是比较干净的,仅包含所需的gzip.conf文件。

cd /root/k8s/

cat >fortune-pod-configmap-volume-with-items.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume-with-items
spec:
  containers:
  - image: 10.0.0.10:5000/luksa/fortune:env
    name: html-generator
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: 10.0.0.10:5000/luksa/nginx
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    - name: config
      mountPath: /etc/nginx/conf.d/
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    emptyDir: {}
  - name: config
    configMap:
      name: fortune-config
      items:                          #只暴露指定的条目
      - key: my-nginx-config.conf     #所使用的条目键名
        path: gzip.conf               #存储条目值的文件
EOF

kubectl create -f fortune-pod-configmap-volume-with-items.yaml

image.png

挂载到某一文件夹会隐藏该文件夹中已存在的文件

将卷挂载到/etc/nginx/conf.d/文件夹,该文件夹原本存在的任何文件都会被隐藏。如果你希望添加文件至某个文件夹如/etc,绝不能采用这种方法,因为这个文件夹包含不少重要文件。

单独的ConfigMap条目作为文件被挂载,且不隐藏文件夹中的其他文件

volumeMount额外的subPath字段可以被用作挂载卷中的某个独立文件或者文件夹,无须挂载完整卷。见图7.10

假设拥有一个包含文件myconfig.conf的configMap卷,希望能将其添加为/etc 文件夹下的文件someconfig.conf。通过属性subPath可以将该文件挂载的同时又不影响文件夹中的其他文件。
image.png
image.png

挂载任意一种卷时均可以使用subPath属性。可以挂载部分卷而不是挂载整个卷。不过这种独立文件的挂载方式会带来文件更新上的缺陷,接下来的小节会讲。

cd /root/k8s/

cat >test-subpath.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: test-subpath
spec:
  containers:
  - image: 10.0.0.10:5000/luksa/fortune:env
    name: html-generator
    volumeMounts:
    - name: config
      mountPath: /etc/issue            #挂载到该文件
      subPath: my-nginx-config.conf    #ConfigMap中的条目
  volumes:
  - name: config
    configMap:
      name: fortune-config        
EOF

kubectl create -f test-subpath.yaml

image.png

为configMap卷中的文件设置权限

configMap卷中所有文件的默认权限为0644(-rw-r—r—),可以通过defaultMode属性改变默认权限。
image.png

ConfigMap通常被用作存储非敏感数据,不过依旧可能希望仅限于文件拥用者的用户和组可读写(0660)。

cd /root/k8s

cat >fortune-pod-configmap-volume-defaultMode.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume
spec:
  containers:
  - image: 10.0.0.10:5000/luksa/fortune:env
    env:
    - name: INTERVAL
      valueFrom:
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval
    name: html-generator
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: 10.0.0.10:5000/luksa/nginx
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    - name: config
      mountPath: /etc/nginx/conf.d
      readOnly: true
#    - name: config
#      mountPath: /tmp/whole-fortune-config-volume
#      readOnly: true
  volumes:
  - name: html
    emptyDir: {}
  - name: config
    configMap:
      name: fortune-config
      defaultMode: 0660
EOF

kubectl create -f fortune-pod-configmap-volume-defaultMode.yaml

image.png
如图,defaultMode修改的是..2021_12_28_something这个文件夹中文件的权限。

7.4.7 更新应用配置且不重启应用程序

使用环境变量或命令行参数作为配置源的弊端在于无法在进程运行时更新配置。将ConfigMap暴露为卷可以达到配置热更新的效果,无须重新创建pod或重启容器。

ConfigMap被更新之后,所有引用它的卷里面的文件也会相应更新,pod中的进程发现文件被改变之后就重载这些文件。Kubernetes同样支持更新之后手动通知容器。

警告:更新ConfigMap之后对应文件的更新耗时可能较长(往往需要数分钟)。

修改ConfigMap

修改ConfigMap,运行在pod中的进程会重载configMap卷中对应的文件。尝试用kubectl edit命令修改ConfigMap fortune-config来关闭gzip压缩:

#实验环境准备
kubectl create -f fortune-pod-configmap-volume.yaml
kubectl port-forward fortune-configmap-volume 8080:80
curl -H "Accept-Encoding: gzip" -I localhost:8080

#修改ConfigMap fortune-config
kubectl edit configmaps fortune-config
#修改:gzip off;

#使用kubectl exec命令打印出该文件内容进行确认:
kubectl exec fortune-configmap-volume -c web-server -- cat /etc/nginx/conf.d/my-nginx-config.conf

#检查结果
curl -H "Accept-Encoding: gzip" -I localhost:8080

等待一段时间,最终会看到配置文件的变化。然后发现这对Nginx并没有什么影响,这是因为Nginx不会去监听文件的变化并重载。

通知Nginx重载配置

#向nginx主进程(master process)发送reload信号
kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload

#检查结果
curl -H "Accept-Encoding: gzip" -I localhost:8080

了解文件被自动更新的过程

Kubernetes在更新完configMap卷中的所有文件之前,应用是不会监听到文件变化并主动进行重载的。所有的文件会被自动一次性全部更新,Kubernetes通过符号链接做到这一点。
image.png
被挂载的configMap卷中的文件(my-nginx-config.conf, sleep-interval)是指向 ..data 文件的符号链接。而 ..data文件是指向 ..4984_09_04_something目录的符号链接。每当ConfigMap被更新后,Kubernetes会创建一个这样的文件夹,写入所有文件并重新将 ..data 链接到这个文件夹,通过这种方式可以一次性修改所有文件。

挂载到现有目录中的文件不会得到更新

如果挂载的是单个文件而不是整个卷,ConfigMap更新之后对应的文件不会被更新!

#可以创建该pod验证
kubectl create -f test-subpath.yaml
kubectl exec test-subpath -- cat /etc/issue

#修改my-nginx-config.conf条目的值
kubectl edit cm fortune-config

#经检查pod中的配置没有更新!
kubectl exec test-subpath -- cat /etc/issue

如果你需要挂载单个文件并且希望在修改ConfigMap的同时会自动更新这个文件,一种方案是挂载整个卷至另外一个文件夹,并创建指向这个文件的符号链接。符号链接可以原生创建在容器镜像中,也可以在容器启动时创建。

了解更新 ConfigMap 的影响

关键在于应用是否支持重载配置。
ConfigMap更新之后创建的新pod或重启的pod会使用新配置,而之前的pod依旧使用旧配置, 这会导致运行中的不同实例的配置不同。因此,如果应用不会自动重载配置(比如Nginx),那么修改某些运行中的pod所使用的ConfigMap并不是一个好主意。

如果应用支持主动重载配置,那么修改ConfigMap的行为就算不了什么。不过需要注意:由于configMap卷中的文件的更新行为对于所有运行中实例而言不是同步的,因此不同pod中的文件可能会在长达一分钟的时间内出现不一致的情况。