使用ConfigMap配置应用程序
in DevOps with 0 comment

使用ConfigMap配置应用程序

in DevOps with 0 comment

1.实验介绍

在使用 Pod 启动应用时,除了镜像之外通常还需要自定义一些参数或是配置,有的时候自定义的配置比较多,所以在 kubernetes 集群中提供了 ConfigMap 向容器中提供环境变量或配置文件以实现不同的配置,这样可以使镜像的配置信息与镜像解耦,通过不同的配置信息使应用程序能够得到更好的复用。

2. ConfigMap 简介

想象一个场景,在 kubernetes 集群中有的时候我们需要传递一些配置给应用,比如:数据库地址、用户名、密码等,我们可以有哪些办法呢?

所以,kubernetes 提供了统一的应用配置管理方案 ConfigMap 方便我们将配置信息与应用程序分离。

ConfigMap 概述

ConfigMap 在容器中使用的场景通常有 3 种,分别如下所示:

使用 ConfigMap 的限制条件

3. 创建 ConfigMap 资源对象

创建 ConfigMap 资源对象的方式有两种:第一种是通过 YAML 配置文件的方式进行创建,第二种是通过 kubectl 命令行方式创建,第三种是通过 ConfigMap 生成器 kustomization.yaml 方式进行创建(从 kubernetes v1.14 版本开始支持 kustomization.yaml)。

ConfigMap 以一个或多个 key:value 的形式将配置信息保存在 kubernetes etcd 数据库中,键值对的形式既可以表示单个属性/一个变量的值,比如:apploglevel=info,也可以表示整个配置文件/JSON 二进制对象的内容,比如:server.xml=<?xml...>...

ConfigMap 中保存的配置信息既可以在 Pod 中使用,也可以用于系统组件(比如:Controller)存储配置数据,ConfigMap 与我们后面将会介绍的 Secret 用法有些类似,不过 ConfigMap 主要用于存储不含敏感信息的数据。

下面我们将会详细讲解这三种不同的创建方式。

3.1 通过 YAML 配置文件方式创建

这种创建方式和使用其它的 YAML 文件是相同的,使用的命令为 kubectl create -f xxxx.yaml

在 /home/shiyanlou 目录下新建 cm-appvars.yaml 文件,并写入如下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars # ConfigMap 的名称
data: # 配置信息
  apploglevel: info
  appdatadir: /var/data

这里需要注意的是:ConfigMap 不是属性文件的替代品,它只是作为多个配置文件的引用,类似于 Linux 系统中的 /etc 目录,是专门用来存储配置文件的目录。在上面的 YAML 文件中,在 data 下配置的每一项都会成为一个新文件。

使用命令执行创建:

$ kubectl create -f cm-appvars.yaml
configmap/cm-appvars created

查看刚刚创建的 ConfigMap:

$ kubectl get configmap
NAME         DATA   AGE
cm-appvars   2      10m

# 查看刚刚创建的 cm-appvars 的详细信息
$ kubectl describe configmap cm-appvars
Name:         cm-appvars
Namespace:    default # 如果在 YAML 文件中没有指定命名空间的话,默认使用 default
Labels:       <none>
Annotations:  <none>

Data
====
appdatadir:
----
/var/data
apploglevel:
----
info
Events:  <none>

3.2 通过 kubectl 命令行方式创建

命令形式如下:

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

其中,<map-name> 是设置的 configmap 的名字,<data-source> 可以是目录、文件、或是 key-value 键值对。

<data-source> 中的可选参数为:--from-file/--from-env-file(从目录或文件创建) 和 --from-literal(直接指定键值对创建)。configmap 也可以缩写为 cm

可以在命令行中执行 kubectl create configmap -h 查看具体参数和使用方法。

从目录中创建 ConfigMap

这种方式适用于在一个目录下有多个配置文件。

/home/shiyanlou 目录下新建 configmap 文件夹,并在 configmap 文件夹下新建两个文件,分别名为:game.propertiesui.properties

game.properties 中的文件内容为:

enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

ui.properties 中的文件内容为:

color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

执行创建:

# 这里设置 configmap 的名称为 game-config
$ kubectl create configmap game-config --from-file=/home/shiyanlou/configmap/
configmap/game-config created

$ kubectl get configmap game-config -o yaml
apiVersion: v1
data:
  game.properties: |-
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-23T08:37:09Z"
  name: game-config
  namespace: default
  resourceVersion: "11509"
  selfLink: /api/v1/namespaces/default/configmaps/game-config
  uid: 212cd3eb-fd6d-43a4-8aed-d33968da8e1c

$ kubectl describe configmap game-config
Name:         game-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

Events:  <none>

从文件中创建 ConfigMap

可以从单独的文件或是多个文件中进行创建。

比如:

# 使用单个文件创建
kubectl create configmap game-config-2 --from-file=/home/shiyanlou/configmap/game.properties

# 使用多个文件创建
kubectl create configmap game-config-3 --from-file=/home/shiyanlou/configmap/game.properties --from-file=/home/shiyanlou/configmap/ui.properties

使用参数 --from-env-file 从 env-file 中创建 ConfigMap。env-file 是包含了环境变量的列表。

/home/shiyanlou/configmap 目录下新建 game-env-file.properties 文件,并写入如下内容:

enemies=aliens
lives=3
allowed="true"

执行创建:

$ kubectl create configmap game-config-env-file --from-env-file=/home/shiyanlou/configmap/game-env-file.properties
configmap/game-config-env-file created

$ kubectl get configmap game-config-env-file -o yaml
apiVersion: v1
data:
  allowed: '"true"'
  enemies: aliens
  lives: "3"
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-23T09:00:46Z"
  name: game-config-env-file
  namespace: default
  resourceVersion: "13919"
  selfLink: /api/v1/namespaces/default/configmaps/game-config-env-file
  uid: 790cbece-1144-4ebe-9ea1-1e151f210ef7

另外使用文件创建的话,可以使用自定义的名称替代默认使用的文件名,比如:

# 使用 game.properties 文件创建,但是将其重命名为 game-special-key
$ kubectl create configmap game-config-4 --from-file=game-special-key=/home/shiyanlou/configmap/game.properties
configmap/game-config-4 created

# 可以看到这里显示的是 game-special-key,而不再是之前的 game.properties
$ kubectl describe configmap game-config-4
Name:         game-config-4
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game-special-key:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

使用 key-value 键值对创建 ConfigMap

使用参数 --from-literal 直接在命令中设置 key-value 键值对。

比如:

# 传递多组键值对
$ kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
configmap/special-config created

$ kubectl get configmaps special-config -o yaml
apiVersion: v1
data:
  special.how: very
  special.type: charm
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-23T09:16:09Z"
  name: special-config
  namespace: default
  resourceVersion: "15490"
  selfLink: /api/v1/namespaces/default/configmaps/special-config
  uid: 231c59c3-dc4d-4650-a04f-af51debc94fa

3.3 通过生成器创建 ConfigMap

kubectl 从 1.14 版本开始支持 kustomization.yaml。我们可以使用生成器创建 ConfigMap,然后发送给 API Server 创建资源对象。生成器是在 kustomization.yaml 文件内部指定。

从文件创建生成器

比如使用 configmap/game.properties 文件生成一个 ConfigMap:

cat <<EOF >/home/shiyanlou/kustomization.yaml
configMapGenerator:
- name: game-config-5
  files:
  - configmap/game.properties
EOF

执行创建:

$ kubectl apply -k .
configmap/game-config-5-c6kgkt26m6 created

# 可以看到使用生成器创建的 ConfigMap 在名称末尾有一个 hash 后缀,这样可以确保每次配置文件内容修改以后生成器会生成一个新的 ConfigMap
$ kubectl get configmap|grep game-config-5
game-config-5-c6kgkt26m6   1      49s

$ kubectl describe configmaps/game-config-5-c6kgkt26m6
Name:         game-config-5-c6kgkt26m6
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"game.properties":"enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.p...

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

类似的我们可以将配置文件的名称进行重命名,只需要将 kustomization.yaml 中的 files 如下:

# 使用名称 game-special-key 替代 game.properties
- name: game-config-5
  files:
    - game-special-key=configmap/game.properties

然后执行 kubectl apply -k . 会生成新的 ConfigMap。

从键值对生成 ConfigMap

在终端执行如下命令写入值:

cat <<EOF >/home/shiyanlou/kustomization.yaml
configMapGenerator:
- name: special-config-2
  literals:
  - special.how=very
  - special.type=charm
EOF

执行创建:

$ kubectl apply -k .
configmap/special-config-2-c92b5mmcf2 created

4. 在 Pod 中使用 ConfigMap

每次使用的时候,我们都需要先定义 ConfigMap,然后在 Pod 中引用定义好的配置信息。

4.1 通过环境变量方式使用 ConfigMap

使用一个 ConfigMap 中的配置信息作为环境变量

使用 key-value 键值对定义一个用作环境变量的 ConfigMap:

$ kubectl create configmap config-1 --from-literal=special.how=very
configmap/config-1 created

定义 Pod YAML 文件时,SPECIAL_LEVEL_KEY 环境变量使用在 ConfigMap 中定义的 special.how 配置信息的值,在 /home/shiyanlou 目录下新建 pods 文件夹,并在该文件夹下新建 pod-single-configmap-env-variable.yaml 文件,向该文件中写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: ['/bin/sh', '-c', 'env'] # 容器的启动命令是输出环境变量
      env:
        - name: SPECIAL_LEVEL_KEY # 定义环境变量的名称
          valueFrom: # 环境变量 SPECIAL_LEVEL_KEY 的取值
            configMapKeyRef:
              name: config-1 # 环境变量的值取自于 config-1 ConfigMap
              key: special.how # key 为 special.how
  restartPolicy: Never

执行创建 Pod:

$ kubectl create -f pods/pod-single-configmap-env-variable.yaml
pod/dapi-test-pod created

# 查看状态为 Completed,因为启动容器后只查看了环境变量,并且设置为再也不重启,所以容器打印环境变量之后也变为完成状态,并且不会在重启
$ kubectl get pods
NAME            READY   STATUS      RESTARTS   AGE
dapi-test-pod   0/1     Completed   0          18s

然后查看日志,检查是否有成功设置环境变量:

$ kubectl logs dapi-test-pod|grep SPECIAL_LEVEL_KEY
SPECIAL_LEVEL_KEY=very

使用多个 ConfigMap 中的配置信息作为环境变量

/home/shiyanlou/configmap 目录下新建 configmaps.yaml 文件,并写入如下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-2
  namespace: default
data:
  special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: config-3
  namespace: default
data:
  log_level: INFO

执行创建:

$ kubectl create -f configmap/configmaps.yaml
configmap/config-2 created
configmap/config-3 created

在 Pod YAML 文件中使用前面定义的 ConfigMap 中的配置信息,将下面的内容写入 /home/shiyanlou/pods 目录下的 pod-multiple-configmap-env-variable.yaml 文件中:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-2
spec:
  containers:
    - name: test-container-2
      image: busybox
      command: ['/bin/sh', '-c', 'env']
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: config-2
              key: special.how
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: config-3
              key: log_level
  restartPolicy: Never

执行创建:

$ kubectl create -f pods/pod-multiple-configmap-env-variable.yaml
pod/dapi-test-pod-2 created

执行命令 kubectl logs dapi-test-pod-2 可以看到打印出的环境变量中有 LOG_LEVEL=INFO 以及 SPECIAL_LEVEL_KEY=very

使用 ConfigMap 中的所有配置信息作为环境变量

/home/shiyanlou/configmap 文件夹下新建 configmap-multikeys.yaml 文件,并写入如下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-4
  namespace: default
data:
  SPECIAL_LEVEL: very
  SPECIAL_TYPE: charm

执行创建:

$ kubectl create -f configmap/configmap-multikeys.yaml
configmap/config-4 created

使用 envFrom 将 ConfigMap 中的所有配置信息定义为容器的环境变量,ConfigMap 中的 key 会成为 Pod 中的环境变量名。在 /home/shiyanlou/pods 目录下新建 pod-configmap-envFrom.yaml 文件并写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-3
spec:
  containers:
    - name: test-container-3
      image: busybox
      command: ['/bin/sh', '-c', 'env']
      envFrom:
        - configMapRef:
            name: config-4
  restartPolicy: Never

执行创建:

$ kubectl create -f pods/pod-configmap-envFrom.yaml
pod/dapi-test-pod-3 created

执行命令 kubectl logs dapi-test-pod-2 可以看到打印出的环境变量中有 SPECIAL_LEVEL=very 以及 SPECIAL_TYPE=charm

在 Pod 命令中引用环境变量

在 Pod YAML 文件的 command 部分可以使用 $(VAR_NAME) 方式引用环境变量。

/home/shiyanlou/pods 目录下新建 pod-configmap-env-var-valueFrom.yaml 文件并写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-4
spec:
  containers:
    - name: test-container-4
      image: busybox
      command:
        ['/bin/sh', '-c', 'echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)']
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: config-4
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: config-4
              key: SPECIAL_TYPE
  restartPolicy: Never

执行创建:

$ kubectl create -f pods/pod-configmap-env-var-valueFrom.yaml
pod/dapi-test-pod-4 created

执行命令 kubectl logs dapi-test-pod-4 可以看到输出的环境变量 verycharm

4.2 通过卷挂载(volumeMount)方式使用 ConfigMap

前面我们提到过可以使用参数 --from-file 从文件中创建 ConfigMap,这个时候文件名就成为了 ConfigMap 中的 key,文件中的内容就是 key 对应的 value。

使用 ConfigMap 中的配置数据挂载到卷

Pod YAML 文件定义中,在 volumes 指定 ConfigMap 的名字,在 volumeMounts.mountPath 指定具体挂载到容器中的目录。

/home/shiyanlou/pods 目录下新建 pod-configmap-volume.yaml 文件,并写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-5
spec:
  containers:
    - name: test-container-5
      image: busybox
      command: ['/bin/sh', '-c', 'ls /etc/config'] #查看是否成功挂载
      volumeMounts:
        - name: config-volume # 引用 volume 的名称
          mountPath: /etc/config # 挂载到容器内的目录路径
  volumes:
    - name: config-volume # 定义 volume 的名称
      configMap:
        name: config-4 # 使用 ConfigMap "config-4"
  restartPolicy: Never

执行创建:

$ kubectl create -f pods/pod-configmap-volume.yaml
pod/dapi-test-pod-5 created

查看日志,获取容器中 /etc/config 目录下的文件列表:

$ kubectl logs dapi-test-pod-5
SPECIAL_LEVEL
SPECIAL_TYPE

这样我们可以很明确的发现,ConfigMap 配置信息的 key 成为了挂载到容器后的文件名。如果容器在 /etc/config 目录下存在文件,挂载后原有文件将会被覆盖。

卷挂载时明确存储路径

使用 path 字段可以明确挂载到容器后的文件名。

/home/shiyanlou/pods 目录下新建 pod-configmap-volume-specific-key.yaml 文件,并写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-6
spec:
  containers:
    - name: test-container-6
      image: busybox
      command: ['/bin/sh', '-c', 'cat /etc/config/keys']
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: config-4
        items:
          - key: SPECIAL_LEVEL
            path: keys # SPECIAL_LEVEL 的 value 以 keys 文件名进行挂载
  restartPolicy: Never

执行创建:

$ kubectl create -f pods/pod-configmap-volume-specific-key.yaml
pod/dapi-test-pod-6 created

查看日志,检查 /etc/config/keys 文件中存储的值是否为 SPECIAL_LEVEL 对应的值:

$ kubectl logs dapi-test-pod-6
very