1 为什么需要ConfigMap和Secret

应用程序启动过程中通常需要传递参数,当参数较多时会将参数保存到配置文件中,并在配置文件中设置默认参数,当程序启动时再从配置文件中读取配置。

k8s中的应用当然也有这种需求,一种方式是将基础的配置文件直接打包到镜像中,然后在启动命令中设置某些需要更改的配置项,但是,既然将配置项放到配置文件中,当然是希望能够方便地修改配置,如果每次修改配置都需要修改命令行参数,那配置文件有何意义?如果修改配置文件的默认配置还需要重新打包镜像,那配置就没作用了,就跟直接将配置项写死在程序中一样。因此,镜像中不应该包含配置文件,而是只有应用程序。

为了能够让应用程序从统一的地方加载配置文件,同时,能够一次性修改Deployment或者DaemonSet的所有Pod的配置项,k8s提供了两种保存配置的方式,ConfigMap和Secret(ConfigMap是直接以键值对的方式保存配置项,Secret则是会对配置项的值进行加密存储),Pod可以将它们作为卷挂载到容器中。

2 k8s中给容器传递配置的方式

  • 直接覆盖容器的ENTRYPOINT和CMD,修改容器启动的命令和参数
  • 为容器设置环境变量
  • 使用ConfigMap分离不同环境的配置

3 ConfigMap的基本使用

ConfigMap的使用包含ConfigMap的创建和挂载。

ConfigMap的创建可以通过命令行和yaml文件。

通过命令行创建ConfigMap:

kubectl create configmap app-config --from-literal=log_level=debug --from-literal=user=admin

上面的命令会创建名为app-config的ConfigMap,里面有两个配置项:user和log_level,值分别是admin和debug。最终在ConfigMap中的配置就是:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  log_level: debug
  user: admin

两个配置项以kv的方式保存在configmap.data。

一般来说,很多程序的配置项比较多,特别是生产环境的配置,因此,实际工作中一般不太可能直接用上面的命令创建ConfigMap,通常都是使用文件或者目录的方式创建:

kubectl create configmap app-config --from-file=a.conf --from-file=config_item=b.conf --from-file=config_dir

上面演示了三种从文件或者文件夹读取配置的方式:

  • –from-file=a.conf 会创建键为a.conf,值为文件内容的配置项
  • –from-file=config_item=b.conf 会创建键为config_item,值为文件内容的配置项
  • –from-file=config_dir 会创建以目录下文件名为键,值为对应内容的配置项

可以发现,用这种从文件中读取配置创建ConfigMap的方式默认情况下会使用文件名作为键,文件内容作为值,当然,也可以自定义键名,这种方式创建的ConfigMap的配置如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  a.conf: |
    a=b
    c=d
    e=f
  config_item: |
    m=n
    p=q
  m.conf: |
    l=m
  n.conf: |
    d=e

在k8s中,创建ConfigMap最常用的肯定还是yaml的方式,根据上面的ConfigMap的yaml结果也可以知道ConfigMap中的配置方式。

唯一需要说明的就是多行文本的表示:上例中,由于配置文件有多行,如果不加后面的|,最终的ConfigMap中的配置项的值会将多行变成一行,这当然不是我们预期的,因此,为了让配置文件能够以原本的样子保存到ConfigMap中,一般都可以带上|,如果真的要让配置文件内容与ConfigMap一模一样,则可以使用|+,前面的|在多个空行的情况下会合并成一个空行。

创建完ConfigMap,下一步就是将ConfigMap挂载给Pod使用。

最简单的方式就是直接将ConfigMap挂载到容器中,其中的键变成文件名,值就成为文件的内容:

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: my-config
      mountPath: /opt
  volumes:
  - name: my-config 
    configMap:
      name: app-config

Pod将app-config这个ConfigMap命名为my-config,然后将my-config这个卷挂载到nginx容器的/opt目录,创建该Pod后,Pod的nginx容器的/opt目录下就有4个配置文件:a.conf、config-item、m.conf、n.conf,容器的进程在启动后就可以读取这些配置文件。

4 ConfigMap的实践

当然,有时候事情并不是如此美好,k8s中也提供了一些选项用以应对一些特殊场景。

场景一:不允许修改ConfigMap

有时候ConfigMap的值是固定的,需要防止ConfigMap中的值被意外修改,这时候可以设置configmap.immutable为true,该选项只在1.21及以上的版本可用。

将ConfigMap设置为不能修改的好处除了防止被意外修改,还能够降低apiserver的压力,因为系统会关闭对无法修改的ConfigMap的监视操作。

场景二:可选的ConfigMap

默认情况下,当挂载某个不存在的ConfigMap时,Pod会长时间处于ContainerCreating状态,然后启动失败。有时候Pod会弱依赖其他的ConfigMap,也就是说,如果ConfigMap存在则进行挂载,如果不存在,则忽略,此时可以设置pod.spec.volumes.configMap.optional选项为true,那么在ConfigMap不存在的情况下,Pod会启动成功。

场景三:只挂载ConfigMap中的单个配置项

常规场景下是将ConfigMap中的键值对以目录中的文件方式挂载到目标目录,但是有时候只需要挂载某个配置项,这时候可以使用pod.spec.containers.volumeMounts.subPath选项,将该配置设置为ConfigMap中的某个键:

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: my-config
      mountPath: /opt/abc.conf
      subPath: a.conf
  volumes:
  - name: my-config 
    configMap:
      name: app-config

这里就将app-config这个ConfigMap中的a.conf挂载到容器中的/opt/abc.conf文件。

但是使用subPath有个问题:如果后续修改了Configmap的内容,容器重启时会失败。因此,一般不建议使用subPath。

5 Secret的基本使用

ConfigMap中的配置是以明文形式存储,为了增强数据的安全性,k8s提供了Secret。

Secret有三种类型:

  • Opaque:值以base64编码,如果想增强安全性,需要使用额外的安全插件
  • kubernetes.io/service-account-token:SA使用的Secret
  • kubernetes.io/dockerconfigjson:访问镜像仓库的Secret

跟ConfigMap类似,Secret也有命令行和yaml文件的方式创建。

请添加图片描述

可以发现,Opaque类型的Secret的创建命令跟ConfigMap很像,就是多了个类型字段,类型设置为generic,同时,通过base64 -d就可以直接解码成原来的值,因此,实项目中,如果需要高度安全,Opaque类型的Secret肯定还是不合适的。

对于文件类型的创建命令以及挂载到容器中的方式跟ConfigMap没有区别,这里不再过多介绍。

kubernetes.io/service-account-token类型的Secret:

创建ServiceAccount时,会创建对应的Secret,其中就包含证书和token,当pod.spec.serviceAccount设置为某个ServiceAccount时,Pod就会将该ServiceAccount对应的Secret挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录。

kubernetes.io/dockerconfigjson类型的Secret:

当容器运行时需要拉去镜像启动容器时,如果需要认证信息,就需要创建这种类型的Secret。

假设用户可以用admin/pwd登陆到内部的registry.oa.com镜像仓库,如果没有指定pod.spec.imagePullSecrets,而直接拉取不到镜像时,pod就会启动失败。

kubectl create secret docker-registry my-registry --docker-server=registry.oa.com --docker-username=admin --docker-password=pwd

此时查看Secret可以发现,k8s将用户名和密码变成了镜像仓库的登陆token,然后就可以将pod.spec.imagePullSecrets设置为my-registry。

6 ConfigMap和Secret的对比

ConfigMap和Secret都是用于存储应用的配置,但是Secret用于存储特定场景下的安全性较高的配置。

  • ConfigMap以明文形式保存应用程序的配置,可以通过卷的形式挂载到容器中进行使用
  • Opaque类型的Secret就是将数据以base64进行编码;SA类型的Secret是供Pod挂载,用以跟apiserver进行通信;dockerconfigjson类型的Secret用以保存访问镜像仓库的认证信息
  • Secret除了这里讲解的三种类型,还有其他的类型,例如tls和helm
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐