容器中应用启动或运行需要依赖很多环境变量或参数,传统应用下, 都是把配置文件和应用打成同意的jar包,每次修改配置都要重新部署应用。在云容器时代,可以通过以下集中方式来处理配置变量:容器镜像、pod定义、ConfigMap解耦配置。

1、在docker镜像中定义命令行与参数

在docker中运行镜像之前,需要先通过dockerfile做好镜像文件,在做镜像文件时,可以把应用的启动命令与参数做在镜像中,如下dockerfile所示

FROM busybox
ENTRYPOINT ["sleep"]
CMD ["30"]

在容器中通过镜像启动时,通过ENTRYPOINT 指定默认执行的命令,CMD指定默认参数,容器启动后默认执行 sleep 30。
把命令与参数做到镜像中缺点:每次修改参数时,都需要重新做镜像,在生产上就需要停应用,启动新的镜像。

2、在k8s的pod资源清单中定义容器执行的命令和参数

2.1、通过pod定义传递命令参数

可以在k8s中的资源清单中传递命令和参数,当需要修改应用命令和参数时,只需要修改k8s的资源清单即可。

apiVersion: v1
kind: Pod
metadata:
  name: busybox1
spec:
  containers:
  - image: busybox
    name: buysbox1
    imagePullPolicy: IfNotPresent
    command: ["sleep"]
    args: ["300"]

如上所示,只需要在定义pod时,通过command和args传入应用启动需要的命令和参数即可。
优点:修改参数,只需重启应用即可,不用重新做镜像。
缺点:改应用启动的命令和参数时,虽说不用重新做镜像,只修改pod资源清单即可,但修改清单后,容器会执行重启策略,修改命令参数导致了应用重启。

2.2、通过pod定义传入环境变量

在定义pod的资源清单时,可以通过env传入环境变量到容器中
pod_test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: busybox1
spec:
  containers:
  - image: busybox
    name: buysbox1
    imagePullPolicy: IfNotPresent
    command: ["sleep"]
    args: ["300"]
    env:   #通过env把model传递到容器中
    - name: model
      value: dev

创建上面的资源清单kubectl apply -f pod_test.yaml
进入pod中容器,查看内部环境变量
在这里插入图片描述
发现pod资源订单中定义的env的环境变量已经传递到容器中,在容器中就可以获取model的值进行运用了。
优点:修改应用环境变量,不用重新做镜像,只需重启应用即可。
缺点:修改应用环境变量,虽说不用重新做镜像,只修改pod资源清单即可,但修改清单后,容器会执行重启策略,修改命令参数导致了应用重启。

2.3、pod定义的环境变量引用其他环境变量

如下所示,在定义file的环境变量时引用model的环境变量。

apiVersion: v1
kind: Pod
metadata:
  name: busybox1
spec:
  containers:
  - image: busybox
    name: buysbox1
    imagePullPolicy: IfNotPresent
    command: ["sleep"]
    args: ["300"]
    env:
    - name: model
      value: "dev"
    - name: file
      value: "$(model)_file"  #引用其他化境变量

优点:修改环境变量不需要重新做镜像
缺点:修改环境变量会重启应用,环境变量与应用源码耦合在一起,并且生产测试环境不同,有时要维护多套资源定义清单。

3、利用ConfigMap解耦配置

前面介绍不管是把配置定义在镜像中还是在pod中,如果修改配置就要重新做镜像或修改pod定义,尤其是维护多套环境时需要经常修改配置,尤其不方便,现可以通过ConfigMap把配置分离出来,修改配置时只需要修改ConfigMap即可。ConfigMap其实就是键值对,值可以时字面量值,也可以是整个文件。

3.1、创建ConfigMap

  • 通过指定字面量值映射环境变量,创建configmap
kubectl create configmap myconfig1 --from-literal=model=prod 

创建myconfig1时,指定了环境变量model=prod。
注:创建的ConfigMap的名字可以由字母、数字、破折号、下划线以及圆点组成。

  • 通过yaml文件创建ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfig2
data:  	
  model: prod
  file: prod_file

执行 kubectl apply -f config_test.yaml
如上所示,执行完创建命令后,会创建名字为myconfig2的ConfigMap,指定了model和file的环境变量。

  • 通过文件内容创建ConfigMap
    例如,有env_test.conf文件,内容如下,
model:prod
file:prod_file
author:lzj

现从该文件创建ConfigMap,执行kubectl create ConfigMap myconfig3 --from-file=envkey=env_test.conf,创建的myconfig3的键名为envkey。如果要从多个文件创建ConfigMap,可以用多个–from-file指定。

  • 通过文件夹创建ComfigMap
    比如在env_work目录下存在env_test1.conf 和env_test.conf两个文件,env_test.conf内如如上面示例所示,env_test1.conf内容如下:
level:info
application:loan

现从env_work目录创建configmap,执行命令kubectl create configmap myconfig4 --from-file=env_work/,创建成功后,会以文件名作为键名。

  • 通过混合项创建ConfigMap
    可以通过以上几种混合方式创建ConfigMap,如下所示
  kubectl create configmap myconfig5 --from-literal=model=prod  --from-file=env_test2.conf --from-file=env_work/

3.2 通过ConfigMap给容器传递环境变量

3.2.1 向容器传入ConfigMap单个key环境变量

现将上面创建的ConfigMap名为myconfig1中的model=prod传入到容器中,例如

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod1
spec:
  containers:
  - image: busybox
    name: buysbox1
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "sleep 300"]
    env: 						#指定环境变量
    - name: MODEL
      valueFrom:
        configMapKeyRef:
          name: myconfig1
          key: model

通过上述示例,指定该了容器中的MODEL变量的值来源于myconfig1中的model变量的值,即MODEL=prod
在这里插入图片描述

3.2.2 向容器传递ConfigMap的所有环境变量

将上面创建的名字为myconfig3的ConfigMap传入容器中,myconfig3中键为文件env_test.conf名,值为文件内容,把文件中所有的环境变量都传递到了容器中

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod2
spec:
  containers:
  - image: busybox
    name: buysbox2
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "sleep 300"]
    envFrom: 			#将整个ConfigMap传递进来
    - configMapRef:
        name: myconfig3

通过该示例,env_test.conf文件被传递到容器中
在这里插入图片描述

3.2.3 通过ConfigMap向容器传递命令行参数

可以利用ConfigMap初始化某个环境变量,然后在命令行参数中引用该环境变量。如下所示

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - image: busybox
    name: buysbox2
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "echo $(model_env) > /root/test.txt & sleep 300"]  #引用model_env环境变量的值
    env:
    - name: model_env
      valueFrom:
        configMapKeyRef:  	#从myconfig1中传递model环境变量
          name: myconfig1
          key: model

利用上面yaml创建pod并查看容器执行的命令

[root@k8s-master01 pod_work]# kubectl exec -it configmap-pod -- /bin/sh
/ # cd root
~ # ls
test.txt
~ # cat test.txt 
prod

3.2.4 通过ConfigMap挂载数据卷

前面都是把键值对或文件传递到容器的环境变量中,本小节还可以通过ConfigMap把文件挂在到容器中指定的目录下。例如上面创建的myconfig3下挂在了env_test.conf文件,现把该文件挂在到容器中的/root/lzj目录下,如下所示

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - image: busybox
    name: buysbox4
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "sleep 300"]
    volumeMounts:
    - name: config-volume   #名字要和volumes中的name一致
      mountPath: /root/lzj  #挂载卷目录
  volumes:
  - name: config-volume
    configMap:
      name: myconfig3

上述yaml,把volumes卷中的myconfig3中的文件挂载到容器中的/root/lzj目录。注意volumeMounts要和volumes中的name一致。下面查看挂载情况

[root@k8s-master01 pod_work]# kubectl apply -f pod4.yaml 
pod/configmap-pod created
[root@k8s-master01 pod_work]# kubectl exec -it configmap-pod -- /bin/sh
/ # cd root
~ # ls
lzj
~ # cd lzj
~/lzj # ls
env_test.conf
~/lzj # vi env_test.conf 

3.2.5 通过ConfigMap的指定key挂载数据卷

可以通过ConfigMap把指定key文件以一个新的文件名挂载到容器指定目录,例如把myconfig4下的env_test1.conf和env_test.conf两个文件挂载到容器中/root/lzj目录下的env1.conf和env.conf文件。如下所示

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - image: busybox
    name: buysbox5
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "sleep 300"]
    volumeMounts:
    - name: config-volume
      mountPath: /root/lzj
  volumes:
  - name: config-volume
    configMap:
      name: myconfig4
      items:    		#指定myconfig4的key,只把指定的key文件挂载到容器中
      - key: env_test1.conf
        path: env1.conf
      - key: env_test.conf
        path: env.conf

查看挂载的文件

[root@k8s-master01 pod_work]# kubectl apply -f pod5.yaml 
pod/configmap-pod created
[root@k8s-master01 pod_work]# kubectl exec -it configmap-pod -- /bin/sh
/ # cd /root/lzj
~/lzj # ls
env.conf   env1.conf

从示例可知,myconfig4下指定的两个key文件分别挂载到了容器/root/lzj目录下的env.conf和env1.conf文件。

3.2.6 ConfigMap的热更新

使用ConfigMap挂载卷到容器目录还有一个优点,修改ConfigMap不必重启pod,pod会热加载ConfigMap中修改的文件,如下所示

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: configmap-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: config-volume
          mountPath: /usr/share/nginx/html/
      volumes:
      - name: config-volume
        configMap:
          name: myconfig6

创建资源清单,并进入容器查看挂载的文件名为key的hello.html内容为hello world

[root@k8s-master01 pod_work]# kubectl apply -f test.yaml 
deployment.apps/configmap-deployment created
[root@k8s-master01 pod_work]# kubectl exec -it configmap-deployment-676f46ffd8-hlk2j -- /bin/sh
# cd /usr/share/nginx/html
# ls
hello
# cat hello
hello world

修改myconfig6挂载的文件kubectl edit cm myconfig6

apiVersion: v1
data:
  hello: |
    hello shanghai
kind: ConfigMap

过一段时间,再进入容器查询容器挂载的文件,发现挂载文件内容变为hello shanghai
注:此时等待了很长一段时间。

[root@k8s-master01 pod_work]# kubectl exec -it configmap-deployment-676f46ffd8-hlk2j -- /bin/sh
# cat cd /usr/share/nginx/html^C
# ls
hello
# cat hello
hello shanghai

3.3 利用ConfigMap传递变量需要注意的问题

3.3.1 向容器挂载文件会隐藏原文件夹下内容

通过ConfigMap把文件挂载到容器指定目录时,如果目录下有内容就会隐藏掉原来的内容。例如通过nginx镜像创建pod,并把一个hello.html文件挂载到/usr/share/nginx/html文件夹下,那么hello.html文件就会隐藏掉/usr/share/nginx/html目录下原来所有的文件。

首先利用hello.html创建一个ConfigMap

kubectl create configmap myconfig6 --from-file=hello=hello.html

然后创建pod的资源清单,指定把myconfig6中的hello.html挂载到容器/usr/share/nginx/html目录下

apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
spec:
  containers:
  - name: my-nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      mountPath: /usr/share/nginx/html
  volumes:
  - name: config-volume
    configMap:
      name: myconfig6
      items:
      - key: hello
        path: hello.html

查看挂载后情况,发现/usr/share/nginx/html目录下只有hello.html文件,如下所示,原来所有文件已被隐藏。

[root@k8s-master01 pod_work]# kubectl apply -f pod6.yaml 
pod/nginx-configmap created
[root@k8s-master01 pod_work]# kubectl exec -it nginx-configmap -- /bin/sh
# cd /usr/share/nginx/html
# ls
hello.html

如果向容器挂载文件且不想隐藏原目录下文件解决办法:
向容器挂载时指定挂载的具体文件名字,需要借助subPath标签,如下所示指定把hello.html文件挂载到/usr/share/nginx/html目录下的helloworld.html文件,这样/usr/share/nginx/html目录下原来的文件都还在。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
spec:
  containers:
  - name: my-nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      mountPath: /usr/share/nginx/html/helloworld.html #挂载到容器中的文件名
      subPath: hello.html    #myconfig6中需要挂载的文件
  volumes:
  - name: config-volume
    configMap:
      name: myconfig6
      items:
      - key: hello
        path: hello.html

3.3.2 设置ConfigMap挂载到容器卷文件读写权限

把ConfigMap中文件挂载到容器目录时,可以通过标签defaultMode设置文件读写权,例如把文件设置成读写权限,如下所示

apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
spec:
  containers:
  - name: my-nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      mountPath: /usr/share/nginx/html/helloworld.html
      subPath: hello.html
  volumes:
  - name: config-volume
    configMap:
      name: myconfig6
      defaultMode: 0666   #设置myconfig6中所有挂载文件的默认权限
      items:
      - key: hello
        path: hello.html

查看挂载后文件的权限,发现挂载到容器后的helloworld.html文件变成了读写权限

[root@k8s-master01 pod_work]# kubectl apply -f pod7.yaml 
pod/nginx-configmap created
[root@k8s-master01 pod_work]# kubectl exec -it nginx-configmap -- /bin/sh
# cd /usr/share/nginx/html/
# ls -l
total 12
-rw-r--r--. 1 root root 494 May 26 15:00 50x.html
-rw-rw-rw-. 1 root root  12 Aug 16 08:48 helloworld.html
-rw-r--r--. 1 root root 612 May 26 15:00 index.html

3.3.3 pod中引用不存在的ConfigMap

如果在创建Pod时引用了不存在的ConfigMap,当创建Pod后,Pod会启动容器失败,如果之后创建了不存在的ConfigMap,失败的容器会自动启动,无需重新创建Pod。


参考k8s in action

Logo

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

更多推荐