K8S配置管理:ConfigMap与Secret
一、概述
在前面的两篇文章(K8S里的“豌豆荚”:Pod,K8S中的神秘任务Job与CronJob)中我们学习了Kubernetes 里的三种 API 对象:Pod、Job 和 CronJob,虽然没有比其他对象更高级,但使用它们也可以在集群里编排运行一些实际的业务了。不过想让业务更顺利地运行,有一个问题不容忽视,那就是应用的配置管理。配置管理是一个至关重要的环节。想象一下,你开发了一个超棒的微服务应用,它依赖于数据库连接地址、各种密钥以及一些个性化的配置参数。当你把这个应用容器化并部署到 K8S 集群中时,如果这些配置信息都硬编码在容器镜像里,那后续的维护和升级工作将会是一场噩梦。比如,当数据库地址发生变更,难道你要重新构建整个镜像,再重新部署吗?这显然既耗时又费力。这时候,ConfigMap 和 Secret 就闪亮登场啦!接下来就深度揭秘ConfigMap与Secret。
二、ConfigMap是什么?
ConfigMap 是 Kubernetes 里专门用于存储非敏感配置数据的资源对象 ,它就像是一个公开的配置仓库,以键值对(Key-Value)的形式保存配置信息,支持纯文本、JSON、XML 等多种格式,可以将应用程序的配置与容器镜像分离,让配置的管理更加灵活和独立。比如,你有一个 Java Web 应用,它需要连接数据库,数据库的地址、端口这些配置信息就可以存储在 ConfigMap 中。这样,当数据库地址发生变化时,你只需要修改 ConfigMap 中的配置,而不需要重新构建容器镜像,极大地提高了应用的可维护性。同时,ConfigMap 还可以在多个 Pod 之间共享,实现配置的统一管理,减少了重复配置的工作。 从本质上来说,ConfigMap 解决了配置与应用耦合的问题,让应用可以更加专注于业务逻辑的实现,而不用担心配置的变化对应用本身造成影响,就像为应用穿上了一层 “配置隔离衣”,使其在不同的环境中都能轻松应对配置的差异。
2.1 创建方式大揭秘
2.1.1 命令行创建
通过kubectl create configmap命令 ,我们可以快速创建 ConfigMap。结合--from-literal 、--from-file 、--from-env-file等参数,能实现从不同来源创建 ConfigMap。
- 从字面量创建:使用--from-literal参数可以直接在命令行中指定键值对。比如,我们要创建一个名为my-config的 ConfigMap,包含key1=value1和key2=value2两个键值对,可以使用以下命令:
kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2
执行这个命令后,Kubernetes 会在集群中创建一个名为my-config的 ConfigMap,其中包含我们指定的两个键值对。这种方式非常适合创建一些简单的、临时的配置,比如设置一些环境变量。
2.1.2 YAML 文件创建
YAML 文件创建 ConfigMap 可以让配置更加结构化和可维护 ,适合在生产环境中使用。下面是一个 YAML 文件创建 ConfigMap 的结构示例:
apiVersion: v1
kind: ConfigMap
metadata:name: my-configmapnamespace: default
data:user: 'zhangsan'path: '/etc/app'
- apiVersion:指定 Kubernetes API 的版本,这里使用v1,表示这是一个稳定的 API 版本。不同的 Kubernetes 版本可能支持不同的 API 版本,在创建资源时,需要根据实际情况选择合适的版本。
- kind:指定资源类型,这里是ConfigMap,表示我们要创建的是一个 ConfigMap 对象。
- metadata:包含 ConfigMap 的元数据,如name指定 ConfigMap 的名称,在同一个命名空间中,名称必须唯一;namespace指定 ConfigMap 所属的命名空间,默认是default命名空间。命名空间可以帮助我们将不同的应用或项目隔离开来,便于管理和维护。
- data:用于存储实际的配置数据,以键值对的形式呈现。这里的key1和key2是简单的键值对,而config.properties是一个多行的配置内容,使用|符号表示后面的内容是一个多行字符串,适合存储完整的配置文件内容。
使用 YAML 文件创建 ConfigMap 时,首先需要将上述内容保存为一个.yaml文件,比如configmap.yaml,然后执行以下命令:
kubectl apply -f configmap.yaml
Kubernetes 会读取这个 YAML 文件,并根据其中的定义创建 ConfigMap。如果 ConfigMap 已经存在,kubectl apply命令会更新它的配置。
创建成功后,我们还是可以用 kubectl get、kubectl describe 来查看 ConfigMap 的状态。可以发现 ConfigMap 的 Key-Value 信息就已经存入了 etcd 数据库,后续就可以被其他 API 对象使用。
三、Secret是什么?
了解了 ConfigMap 对象,我们再来看 Secret 对象就会容易很多,它和 ConfigMap 的结构和用法很类似,但也有着本质的区别,ConfigMap 主要处理非敏感的配置数据,就像一个公开的信息展示板;而 Secret 则如同一个坚固的保险箱,专门用于存储敏感信息,如数据库密码、API 访问令牌、私钥等。这些敏感信息一旦泄露,可能会导致严重的安全问题,比如数据库被恶意访问、应用程序被非法调用等。Secret 的存在,使得这些敏感数据可以与容器镜像和 Pod 的定义分离,大大降低了敏感信息暴露的风险。在一个应用中,数据库的用户名和密码就可以存储在 Secret 中,而不是直接写在应用代码或者镜像里,这样即使镜像被获取,敏感的数据库凭证也能得到保护。
3.1 创建方式大揭秘
3.1.1 命令行创建
通过 kubectl 命令创建:使用kubectl create secret命令可以创建不同类型的 Secret 。以创建 Opaque 类型的 Secret 为例,如果要存储数据库的用户名和密码,可以使用--from-literal参数从字面量创建。假设用户名是admin,密码是123456,命令如下:
kubectl create secret generic db-secret --from-literal=username=dbuser--from-literal=password=dbpassword
这里的generic表示创建的是 Opaque 类型的 Secret,db-secret是 Secret 的名称,--from-literal后面跟着的是键值对,分别表示用户名和密码。
3.1.2 通过 YAML 文件创建
使用 YAML 文件创建 Secret 可以让配置更加清晰和可维护 。下面是一个创建 Secret 的 YAML 文件示例:
apiVersion: v1
kind: Secret
metadata:name: my-secretnamespace: default
data:username: ZGJ1c2Vy # base64编码后的dbuserpassword: ZGJwYXNzd29yZA== # base64编码后的dbpassword
我们从上面的yaml文件会发现,这里的“username”值是一串“乱码”,而不是刚才在命令行里写的明文 “dbuser”。这串“乱码”就是 Secret 与 ConfigMap 的不同之处,不让用户直接看到原始数据,起到一定的 保密作用。不过它的手法非常简单,只是做了 Base64 编码,根本算不上真正的加密,所以我们完全可以绕开 kubectl,自己用 Linux 小工具“base64”来对数据编码,然后写入 YAML 文 件,比如:
echo -n "dbpassword" | base64
ZGJwYXNzd29yZA==
要注意这条命令里的 echo ,必须要加参数 -n 去掉字符串里隐含的换行符,否则 Base64 编 码出来的字符串就是错误的。
这样一个存储敏感信息的 Secret 对象也就创建好了,而且因为它是保密的,使用 kubectl describe 不能直接看到内容,只能看到数据的大小,你可以和 ConfigMap 对比一下。
四、ConfigMap/Secret在pod中的使用
现在通过编写 YAML 文件,我们创建了 ConfigMap 和 Secret 对象,该怎么在 Kubernetes 里 应用它们呢?
因为 ConfigMap 和 Secret 只是一些存储在 etcd 里的字符串,所以如果想要在运行时产生效果,就必须要以某种方式“注入”到 Pod 里,让应用去读取。在这方面的处理上 Kubernetes 和 Docker 是一样的,也是两种途径:环境变量和加载文件。
4.1 以环境变量的方式使用 ConfigMap/Secret
了解Pod的都知道,描述容器的字段“containers”里有一个“env”,它定义了 Pod 里容 器能够看到的环境变量。下面我就把引用了 ConfigMap 和 Secret 对象的 Pod 列出来,给大家做个示范:
apiVersion: v1
kind: Pod
metadata:name: my-pod
spec:containers:- name: my-containerimage: nginx:latestenv:- name: USERvalueFrom:configMapKeyRef:name: my-configmapkey: user- name: USERNAMEvalueFrom:secretKeyRef:name: my-secretkey: username
“valueFrom”字段指定了环境变量值的来源,可以是“configMapKeyRef”或者 “secretKeyRef”,然后你要再进一步指定应用的 ConfigMap/Secret 的“name”和它里面的 “key”,要当心的是这个“name”字段是 API 对象的名字,而不是 Key-Value 的名字。
通过上面yaml文件来观看可能不是那么直观,因为 ConfigMap 和 Secret 在 Pod 里的组合关系不像 Job/CronJob 那么简单直接,所以我还是用画图来表示它们的引用关系:
从这张图你就应该能够比较清楚地看出 Pod 与 ConfigMap、Secret 的“松耦合”关系,它们不是直接嵌套包含,而是使用“KeyRef”字段间接引用对象,这样,同一段配置信息就可以在不同 的对象之间共享。
4.2 以 Volume 的方式使用 ConfigMap/Secret
Kubernetes 为 Pod 定义了一个“Volume”的概念,可以翻译成是“存储卷”。如果把 Pod 理解成是一个虚拟机,那么 Volume 就相当于是虚拟机里的磁盘。
我们可以为 Pod“挂载(mount)”多个 Volume,里面存放供 Pod 访问的数据,这种方式有点 类似 docker run -v,虽然用法复杂了一些,但功能也相应强大一些。 在 Pod 里挂载 Volume 很容易,只需要在“spec”里增加一个“volumes”字段,然后再定义卷的 名字和引用的 ConfigMap/Secret 就可以了。要注意的是 Volume 属于 Pod,不属于容器,所 以它和字段“containers”是同级的,都属于“spec”。
下面让我们来定义两个 Volume,分别引用 ConfigMap 和 Secret,名字是 cm-vol 和 sec-vol:
spec:volumes:- name: cm-volconfigMap:name: my-configmap- name: sec-volsecret:secretName: my-secret
有了 Volume 的定义之后,就可以在容器里挂载了,这要用到“volumeMounts”字段,正如它的字面含义,可以把定义好的 Volume 挂载到容器里的某个路径下,所以需要在里面用 “mountPath”“name”明确地指定挂载路径和 Volume 的名字。
containers:
- volumeMounts:- mountPath: /tmp/cm-itemsname: cm-vol- mountPath: /tmp/sec-itemsname: sec-vol
把“volumes”和“volumeMounts”字段都写好之后,配置信息就可以加载成文件了。这里我还 是画了图来表示它们的引用关系:
通过上面的图可以看到看到,挂载 Volume 的方式和环境变量又不太相同。环境变量是直接引用了 ConfigMap/Secret,而 Volume 又多加了一个环节,需要先用 Volume 引用 ConfigMap/Secret,然后在容器里挂载 Volume,有点“兜圈子”“弯弯绕”。
这种方式的好处在于:以 Volume 的概念统一抽象了所有的存储,不仅现在支持 ConfigMap/Secret,以后还能够支持临时卷、持久卷、动态卷、快照卷等许多形式的存储,扩展性非常好。
五、小结
通过上面的分享相信大家也了解到了在 Kubernetes 里管理配置信息的 API 对象 ConfigMap 和 Secret,它们分别代表了明文信息和机密敏感信息,存储在 etcd 里,在需要的时候可以注入 Pod 供 Pod 使用。
Tips: 为了大家快速高效的学习,已经将文章提交到了git仓库,涵盖后端大部分技术,以及后端学习路线,仓库内容会持续更新,建议 Star 收藏 以便随时查看https://gitee.com/bxlj/java-article。