前面了解了一些常用的资源对象的使用,但是单纯依靠这些资源对象,还不足以满足我们的日常需求,一个重要的需求就是应用的配置管理、敏感信息的存储和使用(如:密码、Token等)、容器运行资源的配置,安全管控、身份认证等。

对于应用的可变配置在k8s中是通过一个ConfigMap资源对象来实现的,许多应用经常会有从配置文件、命令行参数或者环境变量中读取一些配置信息的需求,这些配置信息不会直接写死到应用程序中,ConfigMap就是提供了向容器中注入配置信息的能力,不仅可以用来保存单个属性,还可以用来保存整个配置文件,比如可以用来配置一个redis服务的访问地址,也可以用来保存整个redis的配置文件。

创建

ConfigMap资源对象使用key-value形式的键值对来配置数据,这些数据可以在Pod里面使用,如下所示的资源清单:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: cm-demo
  5. namespace: default
  6. data:
  7. data.1: hello
  8. data.2: world
  9. config: |
  10. property.1=value-1
  11. property.2=value-2
  12. property.3=value-3

其中配置数据在data属性下面进行配置,前两个被用来保存单个属性,后面一个被用来保存一个配置文件。

可以使用kubectl create -f xxx.yaml来创建ConfigMap对象,如果不知道怎么创建的话,可以使用kubectl create configmap -h来查看帮助信息:

Examples:
  # Create a new configmap named my-config based on folder bar
  kubectl create configmap my-config --from-file=path/to/bar

  # Create a new configmap named my-config with specified keys instead of file basenames on disk
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt

  # Create a new configmap named my-config with key1=config1 and key2=config2
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2

从上面看到可以从一个给定的目录来创建一个ConfigMap对象,比如有一个testcm的目录,该目录下包含一些配置文件,redis和mysql的连接信息:

$ ls testcm
redis.conf
mysql.conf

$ cat testcm/redis.conf
host=127.0.0.1
port=6379

$ cat testcm/mysql.conf
host=127.0.0.1
port=3306

然后可以使用 from-file 关键字来创建包含这个目录下面所以配置文件的 ConfigMap:

$ kubectl create configmap cm-demo1 --from-file=testcm
configmap "cm-demo1" created

其中 from-file 参数指定在该目录下面的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件的内容。创建完成后,同样我们可以使用如下命令来查看 ConfigMap 列表:

$ kubectl get configmap
NAME       DATA      AGE
cm-demo1   2         17s

可以看到已经创建了一个 cm-demo1 的 ConfigMap 对象,然后可以使用 describe 命令查看详细信息:

$ kubectl describe configmap cm-demo1
Name:         cm-demo1
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
mysql.conf:
----
host=127.0.0.1
port=3306

redis.conf:
----
host=127.0.0.1
port=6379

Events:  <none>

看到两个 key 是 testcm 目录下面的文件名称,对应的 value 值就是文件内容,这里值得注意的是如果文件里面的配置信息很大的话,describe 的时候可能不会显示对应的值,要查看完整的键值,可以使用如下命令:

$ kubectl get configmap cm-demo1 -o yaml
apiVersion: v1
data:
  mysql.conf: |
    host=127.0.0.1
    port=3306
  redis.conf: |
    host=127.0.0.1
    port=6379
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-14T16:24:36Z
  name: cm-demo1
  namespace: default
  resourceVersion: "3109975"
  selfLink: /api/v1/namespaces/default/configmaps/cm-demo1
  uid: 6e0f4d82-6fef-11e8-a101-525400db4df7

除了通过文件目录进行创建,也可以使用指定的文件进行创建 ConfigMap,同样的,以上面的配置文件为例,创建一个 redis 的配置的一个单独 ConfigMap 对象:

$ kubectl create configmap cm-demo2 --from-file=testcm/redis.conf
configmap "cm-demo2" created
$ kubectl get configmap cm-demo2 -o yaml
apiVersion: v1
data:
  redis.conf: |
    host=127.0.0.1
    port=6379
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-14T16:34:29Z
  name: cm-demo2
  namespace: default
  resourceVersion: "3110758"
  selfLink: /api/v1/namespaces/default/configmaps/cm-demo2
  uid: cf59675d-6ff0-11e8-a101-525400db4df7

可以看到一个关联redis.conf文件配置信息的ConfigMap对象创建成功。—from-file这个参数可以使用多次。

另外,通过帮助文档可以看到,还可以直接使用字符串进行创建,通过—from-literal参数传递配置信息,这个参数也能使用多次,格式如下:

$ kubectl create configmap cm-demo3 --from-literal=db.host=localhost --from-literal=db.port=3306
configmap "cm-demo3" created
$ kubectl get configmap cm-demo3 -o yaml
apiVersion: v1
data:
  db.host: localhost
  db.port: "3306"
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-14T16:43:12Z
  name: cm-demo3
  namespace: default
  resourceVersion: "3111447"
  selfLink: /api/v1/namespaces/default/configmaps/cm-demo3
  uid: 06eeec7e-6ff2-11e8-a101-525400db4df7

使用

ConfigMap这些配置数据可以通过很多种方式在Pod里使用,主要有一下几种方式:

  • 设置环境变量的值
  • 在容器里设置命令行参数
  • 在数据卷里面挂载配置文件

首先,使用Configmap来填充环境变量,如下所示的Pod资源对象:

apiVersion: v1
kind: Pod
metadata:
  name: testcm1-pod
spec:
  containers:
  - name: testcm1
    image: busybox
    command: ["/bin/sh", "-c", "env"]
    env:
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: cm-demo3
          key: db.host
    - name: DB_PORT
      valueFrom:
        configMapKeyRef:
          name: cm-demo3
          key: db.prot
    envFrom:
      - configMapRef:
         name: cm-demo1

Pod 运行后会输出如下所示的信息:

$ kubectl logs testcm1-pod
......
DB_HOST=localhost
DB_PORT=3306
mysql.conf=host=127.0.0.1
port=3306
redis.conf=host=127.0.0.1
port=6379
......

可以看到 DB_HOST 和 DB_PORT 都已经正常输出了,另外的环境变量是因为直接把 cm-demo1 给注入进来了,所以把它们的整个键值给输出出来了,这也是符合预期的。

也可以使用configMap的命令行参数,configMap也可以被用来设置容器中的命令或者参数值,如下:

apiVersion: v1
kind: Pod
metadata:
  name: testcm2-pod
spec:
  containers:
  - name: testcm2
    image: busybox
    command: ["/bin/sh", "-c", "echo ${DB_HOST} ${DB_PORT}"]
    env:
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: cm-demo3
          key: db.host
    - name: DB_PORT
      valueFrom:
        configMapKeyRef:
          name: cm-demo3
          key: db.prot

运行后Pod输出:

$ kubectl logs testcm2-pod
localhost 3306

另外一种是非常常见的使用ConfigMap的方式:通过数据卷使用,在数据卷里面使用ConfigMap,就是将文件填入数据卷,在这个文件中,键就是文件内容,如下资源对象所示:

apiVersion: v1
kind: Pod
metadata:
  name: testcm3-pod
spec:
  volumes:
  - name: config-volume
    configMap:
      name: cm-demo2
  containers:
  - name: testcm2
    image: busybox
    command: ["/bin/sh", "-c", "echo ${DB_HOST} ${DB_PORT}"]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config

运行这个 Pod 的,查看日志:

$ kubectl logs testcm3-pod
host=127.0.0.1
port=6379

也可以在ConfigMap值被映射的数据卷里去控制路径,如下定义:

apiVersion: v1
kind: Pod
metadata:
  name: testcm4-pod
spec:
  volumes:
  - name: config-volume
    configMap:
      name: cm-demo1
      items:
      - key: mysql.conf
        path: path/to/mysql.conf
  containers:
    - name: testcm4
      image: busybox
      command: ["/bin/sh", "-c", "cat /etc/config/path/to/mysql.conf"]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config

运行这个Pod,查看日志:

$ kubectl logs testcm4-pod
host=127.0.0.1
port=3306

需要注意的是,当configmap以数据卷的形势挂载进Pod时,这时更新ConfigMap(删除或重建),Pod内挂载的配置信息会热更新。这时可以增加一些检测配置文件更变的脚本,然后重新加载对应服务就可以实现应用的热更新。

只有通过 Kubernetes API 创建的 Pod 才能使用 ConfigMap,其他方式创建的(比如静态 Pod)不能使用;ConfigMap 文件大小限制为 1MB(ETCD 的要求)。