Kubernetes 配置存储
概述
K8s提供了多种外部数据注入容器的方式,今天我们主要学习环境变量、ConfigMap以及Secret的使用和配置
配置环境变量
我们可以通过配置清单中的
env
及envFrom(来自外部配置)
字段来设置环境变量。
对一个容器添加环境变量可以在容器创建时通过-e ENV=name
方式加载,而k8s在创建 Pod 时,也提供了其下容器环境变量配置的能力
配置示例
我们可以通过配置清单中的
env
及envFrom(来自外部配置)
字段来设置环境变量
创建配置清单
1 | busybox-deployment.yml |
1 | apiVersion: apps/v1 |
参数解释
在清单中我们配置了三个环境变量
DEMO_VERSION
:直接添加变量值demov1
DEMO_POD_NAME
:结合valueFrom中fieldRef获取pod名称字段metadata.name
DEMO_CONT_MEM
:结合valueFrom中resourceFieldRef获取容器资源字段limits.memory
什么是ConfigMap
ConfigMap是一种API对象,用来将非加密数据保存到键值对中,可以用作环境变量、命令行参数或者存储卷中的配置文件
一个重要的需求就是应用的配置管理、敏感信息的存储和使用(如:密码、Token 等)、容器运行资源的配置、安全管控、身份认证等等
对于应用的可变配置在 Kubernetes 中是通过一个 ConfigMap 资源对象来实现的,我们知道许多应用经常会有从配置文件、命令行参数或者环境变量中读取一些配置信息的需求,这些配置信息我们肯定不会直接写死到应用程序中去的,比如你一个应用连接一个 redis 服务,下一次想更换一个了的,还得重新去修改代码,重新制作一个镜像,这肯定是不可取的,而 ConfigMap 就给我们提供了向容器中注入配置信息的能力,不仅可以用来保存单个属性,还可以用来保存整个配置文件,比如我们可以用来配置一个 redis 服务的访问地址,也可以用来保存整个 redis 的配置文件。
ConfigMap的作用
ConfigMap可以将环境变量配置信息和容器镜像解耦,便于应用配置的修改。如果需要存储加密信息时可以使用Secret对象。
ConfigMap组件可以实现应用和配置分离,避免因为修改配置项而重新构建镜像,ConfigMap 用于保存配置数据的键值对,可以用来保存单个属性,也可以用来保存配置文件
和配置中心的区别
ConfigMap是没有apollo,nacos等配置中心强大的,但是这些注册中心是需要应用程序去做一个对接的而ConfigMap就是,你把配置写给我,我给你注入进去就可以了,如果你是volume的话,我给你挂载进去就可以了
当然,我们是可以通过一些额外的手段可以做到,即使你的程序不支持热更新,我们也可以让它做一个reload操作的,是可以做到的;
像redeis,mysql这些配置,只要你应用层面和apollo/nacos对接做了配置的话,那么 在apollo/nacos后台直接改个配置,这边就直接下发下去,就直接生效了。
这个configmap可能更多的还是在一个简单的配置方面,它和你的应用代码的耦合度就没那么高。但是你想利用configmap去做apollo/nacos这样的一个事情,应该是可以做到的。 就是你想通过configmmap去实现一个,去开发一个类似于apollo这样的一个配置中心,应该是可以做到的。
如何使用
只有通过 Kubernetes API 创建的 Pod 才能使用 ConfigMap,其他方式创建的(比如静态 Pod)不能使用;ConfigMap 文件大小限制为 1MB(ETCD 的要求
什么是Secret
一般情况下 ConfigMap 是用来存储一些非安全的配置信息,如果涉及到一些安全相关的数据的话用 ConfigMap 就非常不妥了,因为 ConfigMap 是明文存储的,这个时候我们就需要用到另外一个资源对象了
Secret
用来保存敏感信息,例如密码、OAuth 令牌和 ssh key 等等,将这些信息放在 Secret
中比放在 Pod 的定义中或者 Docker 镜像中要更加安全和灵活
Secret 类型
Secret
主要使用的有以下几种类型
- Opaque:base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过 base64 –decode 解码得到原始数据,所有加密性很弱。
- kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息,~/.docker/config.json 文件的序列化形式
- kubernetes.io/service-account-token:用于 ServiceAccount, ServiceAccount 创建时 Kubernetes 会默认创建一个对应的 Secret 对象,Pod 如果使用了 ServiceAccount,对应的 Secret 会自动挂载到 Pod 目录 /run/secrets/kubernetes.io/serviceaccount 中。
- kubernetes.io/ssh-auth:用于 SSH 身份认证的凭据
- kubernetes.io/basic-auth:用于基本身份认证的凭据
- bootstrap.kubernetes.io/token:用于节点接入集群的校验的 Secret
ConfigMap使用
创建ConfigMap
通过资源清单创建
ConfigMap
资源对象使用key-value
形式的键值对来配置数据,这些数据可以在 Pod 里面使用,
创建资源清单
1 | vi cm-demo.yaml |
其中配置数据在
data
属性下面进行配置,前两个被用来保存单个属性,后面一个被用来保存一个配置文件
1 | apiVersion: v1 |
应用配置
我们可以执行apply应用配置
1 | kubectl apply -f cm-demo.yaml |
查看配置
可以通过
describe
命令进行查看配置
1 | kubectl describe cm cm-demo |
指定目录创建
们就可以使用
from-file
关键字来创建包含这个目录下面所有配置文件的ConfigMap
创建测试文件
比如我们创建
/tmp/config/cm
的目录,该目录下面包含一些配置文件,redis 和 mysql 的连接信息
1 | mkdir -p /tmp/config/cm |
创建ConfigMap
其中
from-file
参数指定在该目录下面的所有文件都会被用在ConfigMap
里面创建一个键值对,键的名字就是文件名,值就是文件的内容。
1 | kubectl create cm cm-demo1 --from-file=/tmp/config/cm/ |
查看配置
1 | kubectl describe cm cm-demo1 |
我们可以看到两个
key
是 testcm 目录下面的文件名称,对应的value
值就是文件内容。
查看完整配置
如果文件里面的配置信息很大的话,
describe
的时候可能不会显示对应的值,要查看完整的键值,可以使用如下命令:
1 | kubectl get cm cm-demo1 -oyaml |
单个文件创建
除了通过文件目录进行创建,我们也可以使用指定的文件进行创建
ConfigMap
创建ConfigMap
我们以上面的配置文件为例,我们创建一个 redis 的配置的一个单独
ConfigMap
对象
1 | kubectl create cm cm-demo2 --from-file=/tmp/config/cm/redis.conf |
查看配置
1 | kubectl describe cm cm-demo2 |
们可以看到一个关联 redis.conf 文件配置信息的
ConfigMap
对象创建成功了
多个文件创建
创建ConfigMap
--from-file
这个参数可以使用多次,可以同时指定多个配置文件创建
1 | kubectl create cm cm-demo3 --from-file=/tmp/config/cm/redis.conf --from-file=/tmp/config/cm/mysql.conf |
查看配置
1 | kubectl describe cm cm-demo3 |
比如我们这里使用两次分别指定 redis.conf 和 mysql.conf 文件,就和直接指定整个目录是一样的效果了
通过字符串创建
我们可以看到我们还可以直接使用字符串进行创建,过
--from-literal
参数传递配置信息,同样的,这个参数可以使用多次
创建ConfigMap
一般数据较小的话,可以直接在资源配置清单里写一下,数据量比较大的话,就可以写到文件里进行创建使用
1 | kubectl applay configmap cm-demo4 --from-literal=db.host=localhost --from-literal=db.port=3306 |
使用ConfigMap
我们创建了ConfigMap就是为了使用,下面我们来看下如何使用
使用方式
ConfigMap
这些配置数据可以通过很多种方式在 Pod 里使用,主要有以下几种方式:
- 设置环境变量的值
- 在容器里设置命令行参数
- 在数据卷里面挂载配置文件
用于环境变量
Configmap 用于配置环境变量的好处是可以将环境配置信息和容器镜像解耦,便于应用配置的修改。
创建资源清单
们使用
ConfigMap
来填充我们的环境变量,如下所示的 Pod 资源对象
1 | vi configmap1-pod.yaml |
1 | apiVersion: v1 |
应用配置
我们现在可以直接应用配置
1 | kubectl apply -f configmap1-pod.yaml |
这里可以先看下当前pod的状态,虽然后面会处于报错状态,但是是正常现象,因为pod执行完就退出了
查看日志
因为我们的容器启动就是为了打印环境信息,现在我们来看一下打印的日志
1 | kubectl logs configmap1-pod |
我们可以看到 DB_HOST 和 DB_PORT 都已经正常输出了,另外的环境变量是因为我们这里直接把 cm-demo1 给注入进来了,所以把他们的整个键值给输出出来了,这也是符合预期的
用户命令行参数
另外我们也可以使用
ConfigMap
来设置命令行参数,ConfigMap
也可以被用来设置容器中的命令或者参数值
创建资源清单
1 | vi configmap2-pod.yaml |
1 | apiVersion: v1 |
应用配置
1 | kubectl apply -f configmap2-pod.yaml |
查看日志
1 | kubectl logs configmap2 |
我们看到已经把我们configmap中配置的环境信息打印出来了
用于数据卷挂载
通过数据卷使用,在数据卷里面使用 ConfigMap,就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容
创建资源清单
1 | vi configmap3-pod.yaml |
在这里我们打印了挂载目录的配置文件
1 | apiVersion: v1 |
这里使用了cm-demo2的configmap,我们先来查看以下内容
1 | kubectl get cm cm-demo2 -oyaml |
应用配置
1 | kubectl apply -f configmap3-pod.yaml |
查看日志
我们配置文件打印了配置文件,我们来看下
1 | kubectl logs configmap3 |
我们看到已经把配置文件打印出来了
配置更新
configmap有一个重要的功能就是进行配置更新,下面我们演示一下基于环境变量和挂载的配置更新
创建配置清单
环境变量
这里我们对configmap1-pod的配置清单进行修改
1 | vi configmap1-pod.yaml |
1 | apiVersion: v1 |
执行命令并且应用配置
1 | kubectl apply -f configmap1-pod.yaml |
我们可以登录容器查看环境变量信息,执行以下命令可以在容器内部执行命令
1 | kubectl exec configmap1-pod -- env |
我们发现当前的环境变量的username是admin
挂载方式
我们这里对configmap3作为的配置进行修改
1 | vi configmap3-pod.yaml |
这里我们将configmap3挂载到了
/etc/config
1 | apiVersion: v1 |
执行命令并且应用配置
1 | kubectl apply -f configmap3-pod.yaml |
我们可以登录容器查看环境变量信息,执行以下命令可以在容器内部执行命令
1 | kubectl exec configmap3 -- cat /etc/config/data.name |
我们发现username的值也是admin
修改configmap
下面我们对configmap进行修改,将username改为root
修改configMap
我们需要对configMap的参数进行修改
1 | vi cm-demo.yaml |
1 | apiVersion: v1 |
应用配置
现在我们需要重新应用configMap
1 | kubectl apply -f cm-demo.yaml |
这样我们就更新了configMap
查看配置
最后我们看下配置是否生效
环境变量查看
我们可以通过如下命令查看下配置是否生效
1 | kubectl exec configmap1-pod -- env |
我们发现没有任何变化,说明基于环境变量的configMap对于pod是不能够自动更新的需要手动更新
挂载方式查看
我们查看下挂载方式的配置更新是否生效
1 | kubectl exec configmap3 -- cat /etc/config/data.name |
我们发现配置已经更新了
Secret使用
创建Secret
Secret 资源包含2个键值对: data 和 stringData,data 字段用来存储 base64 编码的任意数据。
提供 stringData 字段是为了方便,它允许 Secret 使用未编码的字符串,data 和 stringData 的键必须由字母、数字、-,_ 或 . 组成。
通过data创建
这里我们对用户名和密码进行base64编码,比如我们来创建一个用户名为
admin
,密码为123456
的Secret
对象
密码加密
首先我们需要先把用户名和密码做
base64
编码
1 | echo -n "admin"|base64 |
根据用户名密码获取加密的信息
创建资源清单
然后我们就可以利用上面编码过后的数据来编写一个 YAML 文件
1 | vi secret-demo.yaml |
1 | apiVersion: v1 |
应用配置
然后我们就可以使用 kubectl 命令来创建了
1 | kubectl apply -f secret-demo.yaml |
这样我们就创建了一个secret对象出来
查看详情
上面我们看不到配置信息,我们可以使用
describe
关键字查看详情
1 | kubectl describe secrets mysecret |
这样我们就可以看到详情信息了
但是我们发现还是看不到具体加密数据,我们可以通过如下的方式进行查看
1 | kubectl get secrets mysecret -oyaml |
通过stringData创建
对于某些场景,你可能希望使用
stringData
字段,这字段可以将一个非 base64 编码的字符串直接放入 Secret 中, 当创建或更新该 Secret 时,此字段将被编码。
创建加密字符串
比如当我们部署应用时,使用 Secret 存储配置文件, 你希望在部署过程中,填入部分内容到该配置文件
1 | apiUrl: "https://my.api.com/api/v1" |
创建资源清单
那么我们就可以使用以下定义将其存储在 Secret 中
1 | vi secret-demo2.yaml |
1 | apiVersion: v1 |
应用配置
比如我们直接创建上面的对象后重新获取对象的话
config.yaml
的值会被编码
1 | kubectl apply -f secret-demo2.yaml |
查看详情
控制器会自动帮我们把非编码的values编码成base64,然后放到data属性中去!!!
1 | kubectl get secrets mysecret2 -oyaml |
查看信息
因为使用的是base64进行加密的,我们依然可以通过base64 -d来解密
1 | echo "YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IGFkbWluCnBhc3N3b3JkOiAxMjM0NTYK" |base64 -d |
通过命令创建
我们还可以通过
kubectl create命令
来创建Opaque类型的Secret资源
执行创建命令
1 | kubectl create secret generic mysecret3 --from-literal=username=admin --from-literal=password=123456 |
这样我们就创建了一个secret
查看详情
我们可以通过命令来查看secret的详细信息
1 | kubectl get secret mysecret3 -oyaml |
我们发现用户名密码已经被使用base64加密了
使用Secret
使用方式
创建好
Secret
对象后,有两种方式来使用它
- 以环境变量的形式
- 以Volume的形式挂载
用于环境变量
创建资源清单
首先我们来测试下环境变量的方式,同样的,我们来使用一个简单的busybox镜像来测试下
1 | vi secret1-pod.yaml |
主要需要注意的是上面环境变量中定义的
secretKeyRef
字段,和我们前文的configMapKeyRef
类似,一个是从Secret
对象中获取,一个是从ConfigMap
对象中获取
1 | apiVersion: v1 |
应用配置
1 | kubectl apply -f secret1-pod.yaml |
因为我们设置后台运行,一旦启动就会停止
查看日志
我们来查看以下日志,因为我们启动分时候会打印secret中的参数
1 | kubectl logs secret1-pod |
我们发现日志已经被打印出来了,可以看到有 USERNAME 和 PASSWORD 两个环境变量输出出来
环境变量挂载使用
环境变量还有一种就是直接挂载的方式使用,和上面我们configMap使用的挂载ENV方式一样
创建资源清单
我们创建一个挂载的资源清单
1 | vi secret2-pod.yaml |
1 | apiVersion: v1 |
应用配置
1 | kubectl apply -f secret2-pod.yaml |
查看详情
通过日志查看详情
1 | kubectl logs secret2-pod |
我们发现我们在secret中配置的参数都打印出来了
数据卷挂载使用
下面我们验证以下以数据卷挂载的方式,和configmap很类似
创建资源清单
同样的我们用一个 Pod 来验证下
Volume
挂载,创建一个 Pod 文件
1 | vi secret3-pod.yaml |
这里我们将mysecret挂载到了
/etc/secrets
1 | apiVersion: v1 |
应用配置
1 | kubectl apply -f secret3-pod.yaml |
查看日志
因为启动打印了日志,我们可以查看以下日志输出
1 | kubectl logs secret3-pod |
因为我们执行了
ls /etc/secrets
命令可以看到secret属性挂载成了文件
登录容器查看
我们还可以登录容器查看下内容
1 | kubectl exec -it secret3-pod -- /bin/sh |
可以看到文件内容就是我们配置的属性值
挂载指定文件
我们发现上面挂载是按照key为文件挂载的,我们想要挂在到指定的一个文件怎么办呢?
在 secretName
下面添加 items
指定 key
和 path
,假如不想以key名作为配置文件名可以引入items
字段,在其中逐个指定要用相对路径path
替换的key
创建资源清单
1 | vi secret4-pod.yaml |
1 | apiVersion: v1 |
应用配置
1 | kubectl apply -f secret4-pod.yaml |
查看日志
我们看下日志打印的信息
1 | kubectl logs secret4-pod |
说明文件已经被挂载上去了
登录容器查看
我们还可以登录容器查看下内容
1 | kubectl exec -it secret4-pod -- /bin/sh |
可以看到文件内容就是我们配置的属性值
配置更新
Secret有一个重要的功能就是进行配置更新,下面我们演示一下基于环境变量和挂载的配置更新
创建配置清单
环境变量
这里我们使用secret1-pod的配置
1 | vi secret1-pod.yaml |
1 | apiVersion: v1 |
执行命令并且应用配置
1 | kubectl apply -f secret1-pod.yaml |
我们可以登录容器查看环境变量信息,执行以下命令可以在容器内部执行命令
1 | kubectl exec secret1-pod -- env |
我们发现当前的环境变量的username是admin
挂载方式
我们这里使用secret3作为的配置
1 | vi secret3-pod.yaml |
这里我们将mysecret挂载到了
/etc/secrets
1 | apiVersion: v1 |
执行命令并且应用配置
1 | kubectl apply -f secret3-pod.yaml |
我们可以登录容器查看环境变量信息,执行以下命令可以在容器内部执行命令
1 | kubectl exec secret3-pod -- cat /etc/secrets/username |
我们发现username的值也是admin
修改secret
下面我们对secret进行修改,将username改为root
生成密钥
我们先生成root的base64的密钥
1 | echo -n "root"|base64 |
编辑资源清单
生成密钥后,我们就需要修改对应的secret了
1 | vi secret-demo.yaml |
1 | apiVersion: v1 |
应用配置
现在我们需要重新应用secret
1 | kubectl apply -f secret-demo.yaml |
这样我们就更新了secret
查看配置
最后我们看下配置是否剩下
环境变量查看
我们可以通过如下命令查看下配置是否生效
1 | kubectl exec secret1-pod -- env |
我们发现没有任何变化,说明基于环境变量的secret对于pod是不能够自动更新的需要手动更新
挂载方式查看
我们查看下挂载方式的配置更新是否生效
1 | kubectl exec secret3-pod -- cat /etc/secrets/username |
我们发现配置已经更新了
Secret和ConfigMap的区别
最后我们来对比下
Secret
和ConfigMap
这两种资源对象的异同点
相同点
- key/value的形式
- 属于某个特定的命名空间
- 可以导出到环境变量
- 可以通过目录/文件形式挂载
- 通过 volume 挂载的配置信息均可热更新
不同点
- Secret 可以被 ServerAccount 关联
- Secret 可以存储 docker register 的鉴权信息,用在 ImagePullSecret 参数中,用于拉取私有仓库的镜像
- Secret 支持 Base64 加密
- Secret 分为 kubernetes.io/service-account-token、kubernetes.io/dockerconfigjson、Opaque 三种类型,而 Configmap 不区分类型
使用注意
- 同样 Secret 文件大小限制为
1MB
(ETCD 的要求); - Secret 虽然采用
Base64
编码,但是我们还是可以很方便解码获取到原始信息,所以对于非常重要的数据还是需要慎重考虑,可以考虑使用 Vault 来进行加密管理。