什么是ConfigMap

ConfigMap ,即配置字典, 是Kubernetes提供的用于管理容器中程序运行所需配置的解决方案。 它是一种持久化的机制, 不会随着容器的创建销毁而变化。 容器启动时从ConfigMap中读取配置, 并且多个容器可以共享同一个ConfigMap。

软件配置常见的几种形式:

  • 环境变量
    比如许多运行在Java虚拟机上的程序在启动时会读取JAVA_HOME环境变量, 配置不同的环境变量值, 也就使用了不同的Java虚拟机。
    再比如许多项目会使用环境变量来区分当前环境是product,develop,test

  • 配置文件
    配置文件是最普及,最常用的一类配置形式。 无论是服务端程序还是客户端程序, 都会使用这种形式。
    常见的配置文件格式有 *.ini, *.json, *.xml, *.conf, *.yaml, *.toml等。

  • 命令行参数
    这种形式用于命令行启动的程序, 并且常常会和配置文件,环境变量等形式搭配使用。 其优点在于灵活, 可以方便地对每次程序运行时指定不同的配置项, 因此很适合执行时间较短的程序, 例如压缩解压, 缺点在于总是会让整条命令变得又长又臭。

  • 远程配置中心
    随着微服务架构的盛行, 配置不再需要存储在本地了, 程序启动之后会到远程的服务去读取配置, 用户启动时只需要“告诉”程序去哪里读取配置。 这种配置方式极大地简化了本地部署的步骤, 适用于在多台服务器上大量部署的程序, 是未来的发展趋势。

  • 插件
    严格来说, 插件并不能算是一种配置形式。 因为它不像其他几种形式, 必须在程序刚刚启动时读取, 也不像其他几种形式, 通过用户编辑其内容来改变程序的行为。 插件更多的是在程序运行时根据需要或配置进行加载, 用以扩充程序的功能。

创建配置字典的几种形式

配置字典是一个键值对形式Map。 键是由用户随意指定的, 而值才是Pod中的进程期望读取的配置。 很多应用的配置格式本身就是键值对形式的, 比如Java提供的*.properties文件就是键值对的列表。 在使用配置文件时应该将这个文件的内容整个作为配置字典中的, 而不是每个配置文件中的键值对对应一个配置字典中的键值对。

在命令行直接通过键值对创建

可以直接在命令行中指定键值对, 每个键值对都是一项配置。 就像这样:

  kubectl create configmap My-Config 
      --from-literal=key1=value1 
      --from-literal=loglevel='INFO, stdout' 
      --from-literal=java_home_env=/usr/lib/jre_1.8.0

通过单个配置文件创建

也可以将一个本地的配置文件上传形成一个配置字典。 指定文件可以使用绝对路径, 也可以使用相对路径。
--from-file参数的key是可选的, 如果不指定的话, 默认使用文件名作为key。

  kubectl create configmap My-File-Config 
    --from-file=/home/app/config.properties 
    --from-file=config=/home/app/config.properties

通过目录创建

很多应用都会同时使用多个配置文件, 而这些配置文件常常在同一个目录下。 因此当然也可以把一整个目录都创建成配置字典。
> 从目录创建配置字典实际上是针对目录内的每个文件, 创建以文件名为key的配置项。 因此这种用法有两个注意点:
> 1. 可以在一个配置字典中指定多个目录, 但是要保证所有这些目录中不能存在重名的文件。
> 2. 不能指定key, 此时key只能是文件名。

  kubectl create configmap My-Dir-Config 
    --from-file=/home/app 
    --from-file=config=/home/anothor

通过二进制文件创建

二进制文件同样也可以用来创建配置字典。 不过并不是直接存储在配置字典中, 而是先对文件进行base64编码, 再把编码后的字符串存储在配置字典中。
这是一种非常规用法, 一般不推荐。 如果文件太大的话, 会对k8s集群内的etcd造成压力。 另外上传时还可能会因为超时而中断请求。 仅适用于个别奇葩应用使用非文本形式的配置文件,或者一些程序会使用.so.dill文件作为插件时使用。

  kubectl create configmap My-Binary-Config 
    --from-file=/home/app/config.tar 
    --from-file=/home/app/plugin.so

使用命令行创建配置字典的缺陷

使用命令行直接创建配置字典的优点是方便直接。 但是缺点也有: - 除非使用 –namespace 指定, 或者.kube/config文件中指定了命名空间, 否则该配置字典会在default命名空间内。
- 能够指定配置字典的名称, 但是不能指定配置字典的Label(当然可以在创建之后再用 kubectl lable命令添加)。
- 对于大量的配置, 使用命令行显然不方便。 好在–from-file选项弥补了这一点。
- 这是前面说到的”又长又臭的命令”的完美诠释。

如果想要添加配置字典的Label, 需要两条命令。 首先创建一个配置字典, 然后使用

  kubectl lable configmap My-ConfigMap key=value key2=value2

使用yaml文件创建配置字典

其实就是把想要配置的内容一股脑写到data字段下面就好啦。 示例:

apiVersion:  v1
kind: ConfigMap
metadata:
  labels:
    app: flume
  namespace: flume-namespace
  name: flume-config
data:
  flume.properties: |
    agent.sources = a1
    agent.channels = a1
    agent.sinks = a1
  log4j.properties: |
    flume.root.logger=INFO,console
    log4j.rootLogger=${flume.root.logger}
  java_home_env:
    /usr/lib/jre_1.8.0

事实上, 当想要在命令行查看已创建的配置字典内容, 使用kubectl get config flume-config -o yaml时, 看到的也是上面的形式。

在Pod内使用ConfigMap的几种方式

用于容器内的环境变量

apiVersion: v1
kind: Pod
metadata:
  name: java
spec:
  containers:
  - name: test-container
    image: alpine
    command: [ "/bin/sh", "-c", "echo $JAVA_HOME" ]
    env:
    - name: JAVA_HOME
      valueFrom:
        configMapKeyRef:
          name: My-Config
          key: java_home_env

用于容器内的目录

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "ls /etc/config/" ]
      volumeMounts:
      - name: configDir
        mountPath: /etc/config
  volumes:
    - name: configDir
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: My-Dir-ConfigMap

用于容器内的文件

要注意, 当使用配置字典作为容器内的配置文件时, 会将目标目录整个覆盖, 只保留配置字典中存在的”key”, 也就是对应的配置文件。
这样造成的后果就是不能够仅更新某个目录下面的单个文件。 例如如果想要用配置字典更新 /etc/hosts文件, 那么会导致 /etc 目录下的其他文件都被抹除。 因此使用时一定要小心。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
  - name: test-container
    image: k8s.gcr.io/busybox
    command: [ "/bin/sh","-c","cat /etc/config/keys" ]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: special-config
      items:
      - key: SPECIAL_LEVEL
        path: keys

用于容器内的命令行

配置字典对于命令行形式的配置没有直接的支持, 不过可以通过设置环境变量, 然后在命令行中使用环境变量来间接地实现。

用于容器内的插件

配置字典用作容器内的文件时, 会将base64编码还原, 因此容器内进程看到的是一个完整的二进制文件, 而不是奇怪的编码。

配置字典的更新

当Pod已经运行, 此时再更新其挂载的配置字典, 那么容器内是否能够感知到配置的变更呢? 这要取决于Pod使用配置字典的方式: 1. 如果是使用配置文件或者配置目录的形式, 那么当k8s集群的配置字典更新, 容器内挂载的文件也会同步更新。 因此对于一些能够监听配置文件变更并自动加载新配置的程序(如Apache Flume)来说, 不需要重启Pod就可以立刻应用新的配置。
2. 如果是使用命令行参数或环境变量的方式进行配置的, 由于环境变量是在创建容器的时候指定的, 因此无法随着配置字典的变更而变更。