概念
ConfigMap的功能在k8s1.2版本中引入的,许多应用程序会从配置文件,命令行参数或环境变量中读取配置信息。ConfigMap API会给我们提供了向容器中注入配置信息的机制,ConigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制的对象
1. 为什么需要configmap
我们经常都需要为我们的应用程序配置一些特殊的数据,比如密钥、Token 、数据库连接地址或者其他私密的信息。你的应用可能会使用一些特定的配置文件进行配置,比如settings.py文件,或者我们可以在应用的业务逻辑中读取环境变量或者某些标志来处理配置信息。我们要做到这个,有好多种方案,比如:
- 我们可以直接在打包镜像的时候写在应用配置文件里面,但是这种方式的坏处显而易见而且非常明显。
- 我们可以在配置文件里面通过 env 环境变量传入,但是这样的话我们要修改 env 就必须去修改 yaml 文件,而且需要重启所有的 container 才行。(通过env设置不同的环境,使用不同的yaml配置文件)
- 我们可以在应用启动的时候去数据库或者某个特定的地方拿,没问题!但是第一,实现起来麻烦;第二,如果配置的地方变了怎么办?
当然还有别的方案,但是各种方案都有各自的问题。
而且,还有一个问题就是,如果说我的一个配置,是要多个应用一起使用的,以上除了第三种方案,都没办法进行配置的共享,就是说我如果要改配置的话,那得一个一个手动改。假如我们有 100 个应用,就得改 100 份配置,以此类推……
kubernetes 对这个问题提供了一个很好的解决方案,就是用 ConfigMap 和 Secret。
有很多种方案可以将项目 和 项目的配置解耦:
configMap就是让项目镜像 和 配置、环境变量、配置文件等配置数据 解耦,保证镜像的可移植性
- ConfigMap功能在kubernetes1.2版本中引入,许多应用程序会从配置文件,命令行参数或环境变量中读取配置信息,ConfigAPI给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。
- ConfigMap对像是一系列配置的集合,k8s会将这一集合注入到对应的Pod对像中,并为容器成功启动使用。注入的方式一般有两种,一种是挂载存储卷,一种是传递变量。ConfigMap被引用之前必须存在,属于名称空间级别,不能跨名称空间使用,内容明文显示。ConfigMap内容修改后,对应的pod必须重启或者重新加载配置(支持热更新的应用,不需要重启)。
- Secret类似于ConfigMap,是用Base64加密,密文显示,一般存放敏感数据。一般有两种创建方式,一种是使用kubectl create创建,一种是用Secret配置文件。
2. 应用场景
应用场景:镜像往往是一个应用的基础,还有很多需要自定义的参数或配置,例如资源的消耗、日志的位置级别等等,这些配置可能会有很多,因此不能放入镜像中,Kubernetes中提供了Configmap来实现向容器中提供配置文件或环境变量来实现不同配置,从而实现了镜像配置与镜像本身解耦,使容器应用做到不依赖于环境配置。
向容器传递参数:
Kubernetes如果需要向容器传递参数,可以在Yaml文件中通过command和args或者环境变量的方式实现。(不使用configMap的场景)
示例: demo-mpconft.yaml
apiVersion: v1 kind: Pod metadata: name: print-mapenv spec: containers: - name: env-print-demo image: centos:7.9.2009 imagePullPolicy: IfNotPresent env: #添加了一个环境变量 - name: GREETING value: "hello" - name: HONORIFIC value: "welcome" - name: NAME value: "Kubernetes is a good product" command: - "echo" #执行echo命令 args: - "$(GREETING) $(HONORIFIC) $(NAME)" #命令中传入了这些参数,引用的就是上面env中定义的
等待一会儿时间后,看到STATUS是Completed,Ready是0/1,pod中的容器已经结束了 因为我们yaml文件里就要求容器运行以后只执行一条指令,执行完成后就结束了
# 创建后,命令 echo hello welcome Kubernetes is a good product 将在容器中运行,也就是环境变量中的值被传递到了容器中。 # 查看pod就可以看出 # [root@k8s-master yaml]# kubectl logs print-mapenv hello welcome Kubernetes is a good product
现在测试的这种情况是将配置数据和yaml资源文件耦合在一起了,接下来我们使用configMap分离
3. 创建ConfigMap
3.1 help文档
通过hepl命令可以查看configmap的用法:
# [root@k8s-master-155-221 configmap]# kubectl create configmap --help ...... Aliases: configmap, cm #可以使用cm替代 Examples: # Create a new configmap named my-config based on folder bar # 从目录创建 文件名称为键 文件内容为值 kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config with specified keys instead of file basenames on disk # 从文件创建 key1为键 文件内容为值 kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt # Create a new configmap named my-config with key1=config1 and key2=config2 # 直接命令行给定,键为key1 值为config1 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2 # Create a new configmap named my-config from the key=value pairs in the file # 从文件创建 文件名为键 文件内容为值 kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config from an env file kubectl create configmap my-config --from-env-file=path/to/bar.env Usage: kubectl create configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none] [options] Use "kubectl options" for a list of global command-line options (applies to all commands).
3.2 使用目录创建
# 指定目录 # 创建configmap目录 # ls /opt/yaml/configmap # 创建game.properties文件 enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 # ui.propertes文件 color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice # 创建configmap ,指令 # game-config-test :configmap的名称 # --from-file:指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容 # [root@k8s-master configmap]# kubectl create configmap game-config-test --from-file=/opt/yaml/configmap configmap/game-config-test created # 查看configmap文件 # [root@k8s-master configmap]# kubectl get cm NAME DATA AGE game-config-test 2 24s # 查看详细信息 -o 指定输出格式为yaml # [root@k8s-master configmap]# kubectl get cm game-config-test -o yaml apiVersion: v1 data: game.properties: | # | 前是文件名; | 后是文件内容 enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 ui.propertes: | # | 前是文件名; | 后是文件内容 color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice kind: ConfigMap # 类型 metadata: creationTimestamp: "2021-06-04T06:45:42Z" name: game-config-test # 名称 namespace: default # 命名空间 resourceVersion: "625034" uid: 8a7f0064-f816-4e70-a367-bd37371dcaec
data就是数据区,存放的就是配置文件,key就是文件名,后面”|”后跟的就是配置文件内容,这个是yaml语法
# 描述configmap # [root@k8s-master configmap]# kubectl describe cm game-config-test -n default Name: game-config-test Namespace: default Labels: <none> Annotations: <none> Data ==== game.properties: ---- enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 ui.propertes: ---- color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice Events: <none>
清空环境 [root@k8s-master configmap]# kubectl delete cm/game-config-test -n default
3.3 根据文件创建
只需要指定为一个文件就可以从单个文件中创建ConfigMap
# 指定创建的文件即可 kubectl create configmap game-config-test2 --from-file=/opt/yaml/configmap/game.properties #查看 kubectl get cm game-config-test2 -o yaml
--from-file
这个参数可以使用多次,可以分别指定game.properties,ui.propertes,效果和指定整个目录是一样的。
# [root@k8s-master configmap]# kubectl create configmap game-config-test2 --from-file=/opt/yaml/configmap/game.properties --from-file=/opt/yaml/configmap/ui.propertes configmap/game-config-test2 created # [root@k8s-master configmap]# kubectl get cm -n default NAME DATA AGE game-config-test2 2 8s # [root@k8s-master configmap]# kubectl get cm game-config-test2 -n default -o yaml # [root@k8s-master configmap]# kubectl describe cm game-config-test2 -n default
清理环境 kubectl delete cm/game-config-test2 -n default
3.4 文字创建
# 使用--from-literal 方式直接创建configmap # Create the ConfigMap # [root@k8s-master configmap]# kubectl create configmap test-config-1 --from-literal=key1=value1 --from-literal=key2=value2 configmap/test-config-1 created # 获取cm # [root@k8s-master configmap]# kubectl get cm -n default NAME DATA AGE test-config-1 2 17s # 以yaml格式查看cm # Get the ConfigMap Details for my-config # [root@k8s-master configmap]# kubectl describe cm/test-config-1 -n default Name: test-config-1 Namespace: default Labels: <none> Annotations: <none> Data ==== key1: ---- value1 key2: ---- value2 Events: <none> [root@k8s-master configmap]# kubectl get cm test-config-1 -n default -o yaml apiVersion: v1 data: key1: value1 key2: value2 kind: ConfigMap metadata: creationTimestamp: "2021-06-04T07:07:50Z" name: test-config-1 namespace: default resourceVersion: "627044" uid: 8f904ca7-94f7-4262-98d9-69385130df28 # 描述cm # [root@k8s-master configmap]# kubectl describe cm/test-config-1 -n default Name: test-config-1 Namespace: default Labels: <none> Annotations: <none> Data ==== key1: ---- value1 key2: ---- value2 Events: <none>
使用文字方式创建,利用 --from-literal
参数传递配置信息,该参数可以使用多次。
3.5 直接方法(yaml文件方式创建)
ConfigMap 是一个 API 对象, 让你可以存储其他对象所需要使用的配置。 和其他 Kubernetes 对象都有一个 spec 不同的是,ConfigMap 使用 data 和 binaryData 字段。这些字段能够接收键-值对作为其取值。data 和 binaryData 字段都是可选的。data 字段设计用来保存 UTF-8 字节序列,而 binaryData 则 被设计用来保存二进制数据。
ConfigMap 的名字必须是一个合法的 DNS 子域名。
data 或 binaryData 字段下面的每个键的名称都必须由字母数字字符或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下 出现的键名有重叠。
从 v1.19 开始,你可以添加一个 immutable[长期不变的 / 不可改变的] 字段到 ConfigMap 定义中,创建 不可变更的 ConfigMap。
示例:
# 直接通过配置文件的方式创建 # vim configmap-test1.yaml apiVersion: v1 kind: ConfigMap metadata: name: game-config namespace: default data: game.properties: | enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 ui.properties: | color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice # [root@k8s-master configmap]# kubectl apply -f configmap-test1.yaml configmap/configmap-test-one created # [root@k8s-master configmap]# kubectl get cm/configmap-test-one -n default NAME DATA AGE configmap-test-one 2 23s # [root@k8s-master configmap]# kubectl get cm/configmap-test-one -n default -o yaml apiVersion: v1 data: game.properties: | firstkey=firstvaule secondkey=secondvalue thirdkey=thirdvalue forthkey=forthvalue ui.properties: | key11=value11 key21=value21 key31=value31 key41=value41 kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"game.properties":"firstkey=firstvaule\nsecondkey=secondvalue\nthirdkey=thirdvalue\nforthkey=forthvalue\n","ui.properties":"key11=value11\nkey21=value21\nkey31=value31\nkey41=value41\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"configmap-test-one","namespace":"default"}} creationTimestamp: "2021-06-04T07:22:57Z" name: configmap-test-one namespace: default resourceVersion: "628411" uid: 5f6bc92c-ac17-401d-8948-44c1fdcab7ea # [root@k8s-master configmap]# kubectl describe cm/configmap-test-one -n default Name: configmap-test-one Namespace: default Labels: <none> Annotations: <none> Data ==== ui.properties: ---- key11=value11 key21=value21 key31=value31 key41=value41 game.properties: ---- firstkey=firstvaule secondkey=secondvalue thirdkey=thirdvalue forthkey=forthvalue Events: <none>
4. pod中应用
你可以使用四种方式来使用 ConfigMap 配置Pod 中的容器:
- 容器 entrypoint 的命令行参数
- 容器的环境变量
- 在只读卷里面添加一个文件,让应用来读取
- 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap
这些不同的方法适用于不同的数据使用方式。 对前三个方法,kubelet 使用 ConfigMap 中的数据在 Pod 中启动容器。
第四种方法意味着你必须编写代码才能读取 ConfigMap 和它的数据。然而, 由于你是直接使用 Kubernetes API,因此只要 ConfigMap 发生更改,你的应用就能够通过订阅来获取更新,并且在这样的情况发生的时候做出反应。 通过直接进入 Kubernetes API,这个技术也可以让你能够获取到不同的名字空间里的 ConfigMap。
先创建两个configMap
# 创建第一个configMap, special.how: very 键名:键值 # vim configmap-for-pod.yaml --- apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm # 创建第二个configMap --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO
清空环境,删除之前操作创建的configmap:
# 创建configMap: # [root@k8s-master configmap]# kubectl apply -f config-for-pod.yaml configmap/special-config created configmap/env-config created # [root@k8s-master configmap]# kubectl get cm -n default NAME DATA AGE env-config 1 12m special-config 2 12m
4.1 第一种方式: 在pod中使用ConfigMap来替代环境变量
- valueFrom -> configMapKeyRef 通过valueFrom 导入
- envFrom -> configMapRef 通过envFrom 导入
# vim configmap-instead-podenv.yaml # 第一种方式: 在pod中使用configmap配置,使用ConfigMap来替代环境变量 apiVersion: v1 kind: Pod metadata: name: configmap-instead-podenv spec: containers: - name: cm-instead-podenv image: centos:7.9.2009 imagePullPolicy: IfNotPresent command: - "/bin/sh" - "-c" - "env" # 容器一但运行会执行命令/bin/sh -c env,会在控制台打印一下 env: # 第一种导入方式:在env中导入 - name: SPECIAL_LEVEL_KEY # name是当前容器环境变量的key valueFrom: # 通过valueFrom导入 configMapKeyRef: name: special-config # name是要引入哪个configMap的name key: special.how # key是要引入该configMap中的哪个key - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: special.type envFrom: # 第二种导入方式,直接使用envFrom导入 - configMapRef: name: env-config restartPolicy: Never # 查看日志可以发现,环境变量注入到了容器中了,打印env就结束了 # [root@k8s-master configmap]# kubectl apply -f configmap-instead-podenv.yaml pod/configmap-instead-podenv created # [root@k8s-master configmap]# kubectl get po NAME READY STATUS RESTARTS AGE configmap-instead-podenv 0/1 Completed 0 15s # [root@k8s-master configmap]# kubectl logs pod/configmap-instead-podenv -n default MYNGINX_PORT_80_TCP=tcp://10.96.28.181:80 HOSTNAME=configmap-instead-podenv MYNGINX_PORT=tcp://10.96.28.181:80 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP_PORT=443 MYSERVICE_SERVICE_PORT=80 MYNGINX_PORT_80_TCP_PROTO=tcp KUBERNETES_SERVICE_PORT=443 KUBERNETES_SERVICE_HOST=10.96.0.1 MYNGINX_PORT_80_TCP_PORT=80 MYSERVICE_PORT_80_TCP_ADDR=10.96.17.78 SPECIAL_TYPE_KEY=charm # 这里的env是导入的 MYSERVICE_PORT_80_TCP=tcp://10.96.17.78:80 SPECIAL_LEVEL_KEY=very # 这里的env是导入的 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/ MYSERVICE_PORT=tcp://10.96.17.78:80 SHLVL=1 HOME=/root log_level=INFO # 这里的env是导入的 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_SERVICE_PORT_HTTPS=443 MYSERVICE_PORT_80_TCP_PORT=80 MYSERVICE_PORT_80_TCP_PROTO=tcp MYNGINX_SERVICE_HOST=10.96.28.181 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 MYNGINX_PORT_80_TCP_ADDR=10.96.28.181 MYSERVICE_SERVICE_HOST=10.96.17.78 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 MYNGINX_SERVICE_PORT=80 _=/usr/bin/env
清除环境:kubectl delete -f configmap-instead-podenv.yaml
4.2 第二种方式:用ConfigMap设置命令行参数
用作命令行参数,将 ConfigMap 用作命令行参数时,需要先把 ConfigMap 的数据保存在环境变量中,然后通过 $(VAR_NAME) 的方式引用环境变量.
#第二种方式:用ConfigMap设置命令行参数 # vim configmap-for-cmdargs.yaml apiVersion: v1 kind: Pod metadata: name: configmap-for-cmdargs spec: containers: - name: configmap-for-cmdargs image: centos:7.9.2009 imagePullPolicy: IfNotPresent command: - "/bin/sh" - "-c" - "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" # 命令行中通过${}获取环境变量参数 env: - name: SPECIAL_LEVEL_KEY # name是当前容器环境变量的key valueFrom: # 通过valueFrom引入 configMapKeyRef: name: special-config # name是要引入哪个configMap的name key: special.how # key是要引入该configMap中的哪个key - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: special.type restartPolicy: Never # [root@k8s-master configmap]# kubectl apply -f configmap-for-cmdargs.yaml pod/configmap-for-cmdargs created # [root@k8s-master configmap]# kubectl get pods -n default NAME READY STATUS RESTARTS AGE configmap-for-cmdargs 0/1 Completed 0 21s # [root@k8s-master configmap]# kubectl logs pod/configmap-for-cmdargs -n default very charm
4.3 第三种方式:通过数据卷挂载ConfigMap
在数据卷里面使用这个ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容
,即在 Pod 中将 ConfigMap 当做文件使用。
# 第三种方式:通过数据卷挂载ConfigMap # vim configmap-pass-volume.yaml apiVersion: v1 kind: Pod metadata: name: configmap-pass-volume spec: containers: - name: configmap-pass-volume image: centos:7.9.2009 imagePullPolicy: IfNotPresent command: - "/sbin/init" volumeMounts: # 挂载数据卷 - name: configmap-volume # 指定数据卷名 mountPath: /etc/config # 表示把conifg-volume数据卷挂载到容器的/etc/config目录下 volumes: # 定义数据卷 - name: configmap-volume #给数据卷起名 configMap: #数据卷挂载configmap name: special-config #挂载的configmap名字 restartPolicy: Never # [root@k8s-master configmap]# kubectl apply -f configmap-pass-volume.yaml pod/configmap-pass-volume created # [root@k8s-master configmap]# kubectl get pods -n default NAME READY STATUS RESTARTS AGE configmap-pass-volume 1/1 Running 0 8s
登录容器查看/etc/config目录下是否挂载成功
# 进入容器中 # [root@k8s-master configmap]# kubectl exec -it pod/configmap-pass-volume -n default -- bash # 查看挂载 # [root@configmap-pass-volume /]# ls /etc/config/ special.how special.type # [root@configmap-pass-volume /]# cd /etc/config/ # [root@configmap-pass-volume config]# cat special.how special.type verycharm
清除环境: kubectl delete -f configmap-pass-volume.yaml
4.4 被挂载的 ConfigMap 内容会被自动更新-热更新:
# ConfigMap的热更新 # vim configmap-hot-update.yaml --- # 创建一个configmap apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- # 创建pod apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 3 selector: matchLabels: run: my-nginx template: metadata: labels: run: my-nginx spec: restartPolicy: Always containers: - name: my-nginx image: nginx:1.20.0 imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: http-port protocol: TCP volumeMounts: # 挂载数据卷 - name: configmap-volume-nginx mountPath: /etc/config # 挂载到容器的/etc/config目录下 volumes: - name: configmap-volume-nginx configMap: # 数据卷挂载了上面的ConfigMap name: log-config # [root@k8s-master configmap]# kubectl apply -f configmap-hot-update.yaml configmap/log-config created deployment.apps/my-nginx created # [root@k8s-master configmap]# kubectl get pods -n default NAME READY STATUS RESTARTS AGE my-nginx-6fd588dfc-f5dzc 1/1 Running 0 45s # 进入容器查看挂载情况 # [root@k8s-master configmap]# kubectl exec -it pod/my-nginx-6fd588dfc-f5dzc -n default -- bash # root@my-nginx-6fd588dfc-f5dzc:/# ls /etc/config/ log_level # key是文件名 # root@my-nginx-6fd588dfc-f5dzc:/# cat /etc/config/log_level INFO # value是文件内容
修改ConfigMap
# kubectl edit configmap log-config -n default # 修改log-level的值为DEBUG等待大概10秒钟时间,再次查看环境变量的值 # [root@k8s-master configmap]# kubectl edit configmap log-config -n default configmap/log-config edited # 再次查看环境变量的值发生了变化 # [root@k8s-master configmap]# kubectl exec pod/my-nginx-6fd588dfc-f5dzc -n default -- cat /etc/config/log_level DEBUG