2.6 配置管理

2.6 配置管理 #

服务中的配置信息,从数据安全的角度来看可以分成两类:一类是明文配置,可以任意查询修改,比如服务端口、运行参数、文件路径等等。另一类则是机密配置,由于涉及敏感信息需要保密,不能随便查看,比如密码、密钥、证书等等。这两类配置信息本质上都是字符串,只是由于安全性的原因,在存放和使用方面有些差异。

Kubernetes 中的 ConfigMap API 用来保存明文配置,Secret API 用来保存秘密配置。

2.6.1 ConfigMap #

export out="--dry-run=client -o yaml" # 定义Shell变量
kubectl create cm info --from-literal=k=v $out

ConfigMap 里的数据都是 Key-Value 结构,所以 --from-literal 参数使用 k=v 的形式生成数据。ConfigMap 的 YAML 描述大概如下:


apiVersion: v1
kind: ConfigMap
metadata:
  name: info

data:
  count: '10'
  debug: 'on'
  path: '/etc/systemd'
  greeting: |
    say hello to kubernetes.    

由上图可知,现在 ConfigMap 的 Key-Value 信息就已经存入了 etcd 数据库,后续就可以被其他 API 对象使用。

2.6.2 Secret #

Secret 和 ConfigMap 的结构和用法很类似,不过 Secret 对象又细分出很多类,比如:

  • 访问私有镜像仓库的认证信息
  • 身份识别的凭证信息
  • HTTPS 通信的证书和私钥
  • 一般的机密信息(格式由用户自行解释)

最后一种比较常见。创建 YAML 样板的命令是 kubectl create secret generic ,可以使用参数 --from-literal 给出 Key-Value 值:

kubectl create secret generic user --from-literal=name=root $out

echo 的 -n 参数是为了去掉字符串里隐含的换行符

比如有如下的一个 Secret YAML 描述:

apiVersion: v1
kind: Secret
metadata:
  name: user

data:
  name: cm9vdA==  # root
  pwd: MTIzNDU2   # 123456
  db: bXlzcWw=    # mysql

Secret 对配置参数做了 base64 加密。kubectl describe 不能直接看到内容,只能看到数据的大小。

2.6.3 使用配置 #

ConfigMap 和 Secret 只是一些存储在 etcd 里的字符串,如果想要在运行时产生效果,就必须要以某种方式 “注入” 到 Pod 里,让应用去读取。有两种实现途径:环境变量和加载文件。

环境变量 #

在 Pod 里描述容器的字段 “containers” 里有一个 “env”,它定义了 Pod 里容器能够看到的环境变量。可以使用 “value” 的形式,把环境变量的值写死在了 YAML 里,比如:

spec:
  containers:
    - image: busybox:latest
      name: busy
      env:
        - name: os
          value: "ubuntu"

这里的 os 环境变量就固定写死是 ubuntu 了。

实际上 env 还可以使用 “valueFrom” 字段,从 ConfigMap 或者 Secret 对象里获取值,这样就实现了把配置信息以环境变量的形式注入进 Pod,也就是配置与应用的解耦。

可以使用 kubectl explain 查看对 valueFrom 的说明:

valueFrom 字段指定了环境变量值的来源,可以是 configMapKeyRef 或者 secretKeyRef,然后需要再进一步指定应用的 ConfigMap/Secret 的 name 和它里面的 key,需要注意的是这个 name 字段是 API 对象的名字,也就是 ConfigMap 或是 Secret 对象里的metadata.name的值,而不是 Key-Value 的名字。

如下的例子,ConfigMap YAML:

apiVersion: v1
kind: ConfigMap
metadata:
  name: info

data:
  count: '10'
  debug: 'on'
  path: '/etc/systemd'
  greeting: |
    say hello to kubernetes.    

Secret YAML:

apiVersion: v1
kind: Secret
metadata:
  name: user

data:
  name: cm9vdA==  # root
  pwd: MTIzNDU2   # 123456
  db: bXlzcWw=    # mysql

env-pod YAML 引用了 ConfigMap 和 Secret 对象里的配置:

apiVersion: v1
kind: Pod
metadata:
  name: env-pod

spec:
  containers:
    - env:
        - name: COUNT
          valueFrom:
            configMapKeyRef:
              name: info
              key: count
        - name: GREETING
          valueFrom:
            configMapKeyRef:
              name: info
              key: greeting
        - name: USERNAME
          valueFrom:
            secretKeyRef:
              name: user
              key: name
        - name: PASSWORD
          valueFrom:
            secretKeyRef:
              name: user
              key: pwd

      image: busybox
      name: busy
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sleep", "300" ]

对于 env-pod,apply 运行起来之后,可以通过 kubectl exec 进到 pod 中:

envFrom #

如果 ConfigMap 里的信息比较多,用 env.valueFrom 一个个地写会非常麻烦,容易出错,而 envFrom 可以一次性地把 ConfigMap 里的字段全导入进 Pod,并且还能够指定变量名的前缀,非常方便。比如下面的这个示例:

cm.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'wp'
  PASSWORD: '123'
  ROOT_PASSWORD: '123'

pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: maria-pod
  labels:
    app: wordpress
    role: database

spec:
  containers:
    - image: mariadb:10
      name: maria
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 3306

      envFrom:
        - prefix: 'MARIADB_'
          configMapRef:
            name: maria-cm

这里使用 envFrom 将 maria-cm 的数据全部加载进来到 pod 里,并设置了一个 prefix 前缀。

加载文件 #

Pod 有 Volume 存储卷的概念,如果把 Pod 理解成是一个虚拟机,那么 Volume 就相当于是虚拟机里的磁盘。可以为 Pod “挂载(mount)” 多个 Volume,里面存放供 Pod 访问的数据,这种方式有点类似 docker run -v。

在 Pod 里挂载 Volume 只需要在 “spec” 里增加一个 “volumes” 字段,然后再定义卷的名字和引用的 ConfigMap/Secret 就可以。Volume 属于 Pod,不属于容器,所以这个字段和 “containers” 字段是同级的,都属于 “spec”。

比如,分别引用 ConfigMap 和 Secret,名字是 cm-vol 和 sec-vol:

spec:
  volumes:
    - name: cm-vol
      configMap:
        name: info
    - name: sec-vol
      secret:
        secretName: user

在容器里挂载需用到 volumeMounts 字段,该字段可以把定义好的 Volume 挂载到容器里的某个路径下,还需要在里面用 mountPath,name 明确地指定挂载路径和 Volume 的名字,配置信息就可以加载成文件。

containers:
  - volumeMounts:
      - mountPath: /tmp/cm-items
        name: cm-vol
      - mountPath: /tmp/sec-items
        name: sec-vol

比如如下的 vol-pod YAML:

apiVersion: v1
kind: Pod
metadata:
  name: vol-pod

spec:
  volumes:
    - name: cm-vol
      configMap:
        name: info
    - name: sec-vol
      secret:
        secretName: user

  containers:
    - volumeMounts:
        - mountPath: /tmp/cm-items
          name: cm-vol
        - mountPath: /tmp/sec-items
          name: sec-vol

      image: busybox
      name: busy
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sleep", "300" ]

apply 创建后可以通过 exec 进入 pod 查看:

ConfigMap 和 Secret 都变成了目录的形式,而里面的 Key-Value 变成了一个个的文件,而文件名就是 Key。

ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。如果需要保存超出此尺寸限制的数据,可以考虑挂载存储卷。

环境变量用法简单,更适合存放简短的字符串,而 Volume 更适合存放大数据量的配置文件,在 Pod 里加载成文件后让应用直接读取使用。

参考 #