概述

工作中,在几乎所有的应用开发中,都会涉及到配置文件的变更,比如服务需要配置MySQL、Redis等相关信息。而业务上线一般要经历开发环境、测试环境、预发布环境只到最终的线上环境,每一个环境一般都需要其独立的配置。如果我们不能很好的管理这些配置文件,运维工作将顿时变的无比的繁琐而且很容易出错。工作中最佳实践是将应用所需的配置信息于程序进行分离,这样可以使得应用程序被更好的复用,如将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式在创建容器时进行配置注入。我们可以使用如nacos这样的配置管理中心,kubernetes自身也提供了自己的一套方案,即ConfigMap,来实现对容器中应用的配置管理。

使用

ConfigMap:容器应用的配置管理

ConfigMap 供容器使用的典型用法如下:

  • 生成为容器的环境变量。
  • 以 Volume 的形式挂载为容器内部的文件或目录。

ConfiMap 以一个或多个 key:value 的形式保存在 Kubernetes 系统中提供应用使用,即可以用于表示一个变量的值(例如 apploglevel=info),也可以用于表示一个完整配置文件的内容(例如:server.xml=<?xml…>…)

创建 ConfigMap 资源对象

通过 yaml 配置文件方式创建

下面的例子 cm-appvars.yaml 描述了将几个应用所需的变量定义为 ConfigMap 的用法:

[root@k8s-m1 tmp]# cat appvar-cm.yml  
apiVersion: v1
kind: ConfigMap
metadata:
  name: appvars-cm
data:
  apploglevel: info
  appdatadir: /var/log

[root@k8s-m1 tmp]# kubectl apply -f appvar-cm.yml 
configmap/appvars-cm created

[root@k8s-m1 tmp]# kubectl get cm appvars-cm 
NAME         DATA   AGE
appvars-cm   2      25s
[root@k8s-m1 tmp]# kubectl describe cm appvars-cm 
Name:         appvars-cm
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
appdatadir:
----
/var/log
apploglevel:
----
info
Events:  <none>

也可以查看 yaml 形式数据:
[root@k8s-m1 tmp]# kubectl get cm appvars-cm -o yaml

通过 kubectl 命令行方式创建

如果不想使用 yaml 文件,可直接通过 kubectl 也可以创建 ConfigMap,可以使用参数 –from-file 或 –from-literal 指定内容,并且可以在一行命令中指定多个参数。

1、通过 --from-file 参数从文件中进行创建,可以指定 key 的名称,也可以在一个命令行中创建多个 key 的 ConfigMap,文件的内容被设置为 value,语法为:kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source,其中key可以省略,如果省略的话,默认的key就是文件名。
如:

[root@k8s-m1 tmp]# cat username.txt 
admin[root@k8s-m1 tmp]# cat password.txt 
Abcd@123[root@k8s-m1 tmp]# 
#指定key
[root@k8s-m1 tmp]# kubectl create configmap my-config-1 --from-file=username=/tmp/username.txt --from-file=password=/tmp/password.txt
configmap/my-config-1 created
#不指定key
[root@k8s-m1 tmp]# kubectl create configmap my-config-2 --from-file=/tmp/username.txt --from-file=/tmp/password.txt
configmap/my-config-2 created

2、通过 --from-file 参数从文件中进行创建,文件名都被设置为 key,文件的内容被设置为 value,语法为:kubectl create configmap NAME --from-file=config-files-dir
如:

#注意此方式目录下不能有其他不需要的文件,最好就新建一个单独的文件夹
[root@k8s-m1 configmap]# ll
total 8
-rw-r--r-- 1 root root 8 Jul  6 11:21 password.txt
-rw-r--r-- 1 root root 5 Jul  6 11:21 username.txt

[root@k8s-m1 configmap]# kubectl create configmap my-config-3 --from-file=./
configmap/my-config-3 created

3、–from-literal 从命令行中进行创建,直接将指定的 key:value 创建为 ConfigMap 的内容语法为: kubectl create configmap NAME --from-literal=keyl=valuel --from-literal= key2=value2

[root@k8s-m1 configmap]# kubectl create configmap my-config-4 --from-literal=username=admin --from-literal=password=Admin@123
configmap/my-config-4 created

容器应用对 ConfigMap 的使用有以下两种方法。

  • 通过环境变量获取 ConfigMap 中的内容。(不支持动态更新)
  • 通过 Volume 挂载的方式将 ConfigMap 中的内容关在为容器内部的文件或目录。(支持动态更新)

第一种方式示例(通过环境变量的方式使用 ConfigMap):

方法一:可以单独引用某个key

[root@k8s-m1 tmp]# cat configmap-env-pod.yml 
apiVersion: v1
kind: Pod
metadata:
  name: cm-env-pod
spec:
  containers:
  - name: cm-env-pod
    image: busybox
    command: ["/bin/sh","-c","sleep 3600s"]
    env:
    - name: APPLOGLEVEL             # 定义环境变量的名称
      valueFrom:                    # key "apploglevel" 对应的值
        configMapKeyRef:
          name: appvars-cm          # 环境变量的值取自 appvars-cm 中
          key: apploglevel          # key 为 "apploglevel"
    - name: APPDATADIR
      valueFrom:
        configMapKeyRef:
          name: appvars-cm
          key: appdatadir
  restartPolicy: Never

#部署
[root@k8s-m1 tmp]# kubectl  apply  -f configmap-env-pod.yml 
pod/cm-env-pod created
#检查
[root@k8s-m1 tmp]# kubectl exec -it cm-env-pod -- /bin/sh
/ # env|egrep -i 'app'
APPDATADIR=/var/log
APPLOGLEVEL=info
#通过检查发现容器内部的环境变量使用 ConfigMap appvars-cm 中的值进行了正确的设置

方法二:直接引用所有的key
从 Kubernetes 1.6 开始,引入了一个新的字段 envFrom,实现在 Pod 环境内将 ConfigMap(也可以用于 Secret 资源对象)中所有定义的 key=value 自动生成为环境变量:

[root@k8s-m1 tmp]# cat configmap-env-pod-all.yml 
apiVersion: v1
kind: Pod
metadata:
  name: cm-env-pod-all
spec:
  containers:
    - name: cm-env-pod-all
      image: busybox
      command: ["/bin/sh","-c","sleep  3600"]
      envFrom:
      - configMapRef:
          name: appvars-cm   # 根据 cm-appvars 中的 key=value 自动生成环境变量
  restartPolicy: Never
#部署
[root@k8s-m1 tmp]# kubectl apply  -f configmap-env-pod-all.yml 
pod/cm-env-pod-all created
#检查
[root@k8s-m1 tmp]# kubectl exec -it cm-env-pod-all -- /bin/sh
/ # env|egrep -i app
apploglevel=info
appdatadir=/var/log
#通过结果发现configmap appvars-cm中所有的key都被引用,大小写也保持原有的格式

注:需要说明的是 ,环境变量的名称受 POSIX 命名规范([a-zA-Z][a-zA-Z0-9]*)约束 ,不能以数字开头。如果包含非法字符,则系统将跳过该条环境变量的创建,并记录一个 Event 来描 述环境变量无法生成,但并不阻止 Pod 的启动。

第二种方式示例(通过 VolumeMount 使用 ConfigMap)

挂载多个文件
使用volume将ConfigMap作为文件或目录直接挂载,其中每一个key-value键值对都会生成一个文件,key为文件名,value为文件内容,下面是一个示例:(当一个configmap中有多个key-value键值对时要注意每一个key-value键值对都会生成一个文件)

[root@k8s-m1 tmp]# cat configmap-volume-opod.yml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-pod
  namespace: default
  labels:
    app: nginx1
spec:
  containers:
  - name: configmap-volume-pod
    image: nginx
    volumeMounts:
      - name: config      # 指定要挂在的名称
        mountPath: /etc/nginx/conf.d/    #指定要挂在到哪里
    imagePullPolicy: IfNotPresent
    ports:
     - name: http
       containerPort: 80
     - name: https
       containerPort: 443
  volumes:
    - name: config         # 定义一个挂在名称
      configMap:
        name: appvars-cm         #configmap name
#部署
[root@k8s-m1 tmp]# kubectl apply -f configmap-volume-opod.yml 
pod/configmap-volume-pod created
#检查
[root@k8s-m1 tmp]# kubectl exec -it configmap-volume-pod -- /bin/bash
root@configmap-volume-pod:/# ls /etc/nginx/conf.d/
appdatadir  apploglevel
root@configmap-volume-pod:/# 
exit
command terminated with exit code 130

#编辑configmap[root@k8s-master volumes]# kubectl edit cm cm-appvarsconfigmap/cm-appvars edited

#查看是否编辑生效(修改已经生效)[root@master-1 app]# kubectl exec -it nginx-test4 – printenv

挂载单个文件
使用volume将ConfigMap作为文件或目录直接挂载到某个服务上做服务的配置文件,例如下面将www.conf配置文件使用configmap的方式挂载到nginx的配置文件目录下作为nginx的配置文件。此方式还支持动态修改

[root@k8s-m1 tmp]# cat www.conf 
server {
    server_name ;
    listen 80;
    root /data/web/html;
}
#部署configmap
[root@k8s-m1 tmp]#kubectl create configmap www-config --from-file=./www.conf 

[root@k8s-m1 tmp]# cat configmap-volume-pod.yml 
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-pod
  namespace: default
  labels:
    app: nginx1
spec:
  containers:
  - name: configmap-volume-pod
    image: nginx
    volumeMounts:
      - name: config      # 指定要挂在的名称
        mountPath: /etc/nginx/conf.d/    #指定要挂在到哪里
    imagePullPolicy: IfNotPresent
    ports:
     - name: http
       containerPort: 80
     - name: https
       containerPort: 443
  volumes:
    - name: config         # 定义一个挂在名称
      configMap:
        name: www-config  #configmap name
#使用新的configmap www-config部署
[root@k8s-m1 tmp]# kubectl apply -f configmap-volume-pod.yml 
pod/configmap-volume-pod created

[root@k8s-m1 tmp]# kubectl exec -it configmap-volume-pod -- nginx -T
#查看最后几行,端口现在是80
# configuration file /etc/nginx/conf.d/www.conf:
server {
    server_name ;
    listen 80;
    root /data/web/html;
}

#修改configmap www-config的端口设置,将80端口 改成 443端口
[root@k8s-m1 tmp]# kubectl edit cm www-config 

#隔一段时间检查nginx是否加载新的配置,大概几十秒
# configuration file /etc/nginx/conf.d/www.conf:
server {
    server_name ;
    listen 443;
    root /data/web/html;
}

加粗样式

使用 ConfigMap 的限制条件

  • ConfigMap 必须在 Pod 之前创建。
  • ConfigMap 受 Namespace 限制,只有处于同 Namespace 的 Pod 可以引用它。
  • kubelet 只支持可以被 API Server 管理的 Pod 使用 ConfigMap。
  • kubelet 在本 Node 上通过 --manifest-url 或-config 自动创建的静态 Pod 将无法引用 ConfigMap。
  • 在 Pod 对 ConfigMap 进行挂载(VolumeMount)操作时,容器内部只能挂载为 ”目录“,无法挂在为文件。在挂载到容器内部后,目录中将包含 ConfigMap 定义的每个 item,如果该目录下原来还有其他文件(包括文件夹),则容器内的该目录将会被挂载的 ConfigMap 覆盖。如果应用程序要挂载的文件夹下面有其他的文件或者文件夹,则需要进行额外的处理。可以将 ConfigMap 挂载到容器内部的临时目录,在通过启动脚本将配置文件复制或者链接到(cp 或者 link、ln 命令)应用所用的实际配置目录下。