概述

在 Kubernetes 的入门系列中,第 3,5,6,7,8,9 都是讲如何运行一个或者一系列 Container 相关的内容。在使用 Kubernetes 之前,我都是通过 Docker 或者 Docker-Compose 来运行 Container,在那个时候,如果在运行 Container 的时候,有两种方式可以给 Container 传递参数,分别是:

  1. [root@liqiang.io]# docker run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash
  1. [root@liqiang.io]# docker run -it nginx:latest nginx -v

那么,问题就来了,上 Kubernetes 之后,我该如何在运行时指定 Container 的参数,以前的方式是否还能够延续。

给 Pod 传递参数

很显然,作为流行的容器编排项目,Kubernetes 应该是可以实现容器的各种要求的,所以,对于我前面提到的两种方式,也是可以直接使用的,因此,这里我就通过 Pod 来演示一下如何实现这两种方式:

通过环境变量给 Pod 传递参数

要想通过环境变量给 Container 传递一个参数,可以通过在 Container 的描述文件中就加入一个 env 的参数,值是一个数组,每个元素都是一个键值对,其中键是 string 类型的,而值可以有多种选择,这里演示的是最简单的字符串:

当我以这种方式运行 Pod 之后,再进入到 Pod 之中:

  1. [root@liqiang.io]# kubectl exec -n liqiang-io -it busybox-pod-env /bin/sh
  2. / # env
  3. KUBERNETES_SERVICE_PORT=443
  4. KUBERNETES_PORT=tcp://10.43.0.1:443
  5. HOSTNAME=busybox-pod-env
  6. SHLVL=1
  7. HOME=/root
  8. TERM=xterm
  9. KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
  10. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  11. KUBERNETES_PORT_443_TCP_PORT=443
  12. KUBERNETES_PORT_443_TCP_PROTO=tcp
  13. INTERVAL=30s
  14. KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
  15. KUBERNETES_SERVICE_PORT_HTTPS=443
  16. KUBERNETES_SERVICE_HOST=10.43.0.1
  17. PWD=/

就可以发现我设置的环境变量已经被塞入 Container 中了,而 Container 中的应用程序也就可以直接使用这个环境变量了。

通过命令行参数给 Pod 传递参数

和通过环境变量传递值类似,要想指定命令行参数来给 Container 传递参数,也只需要在 Container 的描述文件中加入一个 args 的配置。但是,和环境变量不同之处在于,命令行参数的值是一个字符串数组,而不是键值对数组:

这个 Pod 的功能很简单,只是把命令行参数打印出来就好了,所以,可以直接查看 Pod 的输出即可:

  1. [root@liqiang.io]# kubectl logs -n liqiang-io busybox-pod-args
  2. hello world

这个结果就是我所预期的,两种传递参数的方式都正常工作,但是,你可能就纳闷了,明明标题是 ConfigMap,为什么要讲这两种参数的传递方式?原因其实就是 ConfigMap 的通用用法,ConfigMap 正如其名,通常情况下都被用来存储配置信息,Pod 之间共享数据等,所以,了解 Pod 的参数传递对于后面理解 ConfigMap 的功能有较大的帮助。

ConfigMap

终于说道了 ConfigMap 了,在 Kubernetes 中,ConfigMap 是作为一种资源存在,缩略词是 cm,所以可以这样简单的查看所有的 ConfigMap:

  1. [root@liqiang.io]# kubectl get cm
  2. No resources found in default namespace.

而 ConfigMap 作为一种资源,名字中也体现出来了,它是一个 Map,其实这就是一个存储了 KV 的资源,Key 很明显就是一个字符串类型的数据,而 Value 虽然是字符串类型,但是,表现形式可以各不一样,你既可以把它当作一个简单的字符串值,也可以当作文本文件内容处理。

ConfigMap 的操作

当然,get 用来获取 ConfigMap 只是一个最简单的操作,日常使用 ConfigMap 的功能要复杂得多,这里就介绍几种非常常见的 ConfigMap 使用方法:

直接设置 ConfigMap 的 KV

和其他的 Kubernetes 默认资源类似,可以通过 kubectl 直接设置 ConfigMap 的值:

  1. [root@liqiang.io]# kubectl create cm first-cm --from-literal=key00=value00
  2. configmap/first-cm created

这里有点奇怪的地方是为什么要用 --from-literal=key00=value00,而不是直接 key00=value00 就好了?后面当你看到其他的选项的时候就理解了,而现在,你只需要知道,你已经往一个 Map 中塞入了一个 KV,key 就是 key00,而 value 呢,则为:value00

直接获取 ConfigMap 的 KV

当把值塞入 ConfigMap 之后,下一步我将尝试直接把值读取出来:

  1. [root@liqiang.io]# kubectl get cm
  2. NAME DATA AGE
  3. first-cm 1 3m9s
  4. [root@liqiang.io]# kubectl get cm first-cm -o yaml
  5. apiVersion: v1
  6. data:
  7. key00: value00
  8. kind: ConfigMap
  9. metadata:
  10. creationTimestamp: "2019-11-01T16:03:47Z"
  11. name: first-cm
  12. namespace: default
  13. resourceVersion: "67761"
  14. selfLink: /api/v1/namespaces/default/configmaps/first-cm
  15. uid: 9403c19d-339d-4d97-b757-4c0bd7428c02

这个时候,可能会问号脸了,不是说好了取值的吗?怎么给我看的是这个,很遗憾,kubectl 没有直接获取某一个 Key 的简单方法,但是好用的方式,我们后面再用。

将一个文本内容设置为 Value

更高级得,ConfigMap 可以直接讲一个文件塞入 KV 的 Map 中,其中文件名是 key,文本内容是 value,例如:

  1. [root@liqiang.io]# cat test.txt
  2. Hello, I'm Liqiang Lau
  3. My blog is: https://liqiang.io
  4. [[email protected]]# kubectl create cm file-cm --from-file=test.txt
  5. configmap/file-cm created
  6. [[email protected]]# kubectl get cm file-cm -o yaml
  7. apiVersion: v1
  8. data:
  9. test.txt: |-
  10. Hello, I'm Liqiang Lau
  11. My blog is: https://liqiang.io
  12. kind: ConfigMap
  13. metadata:
  14. creationTimestamp: "2019-11-01T16:14:17Z"
  15. name: file-cm
  16. namespace: default
  17. resourceVersion: "67907"
  18. selfLink: /api/v1/namespaces/default/configmaps/file-cm
  19. uid: 113dd92a-6ad9-496d-8750-2496c31f118a

将一个目录转化成 KV

更过分得是,Kubernetes 居然允许将一个目录直接塞入 ConfigMap 之中,每个文件都是一个 KV:

  1. [root@liqiang.io]# tree
  2. ├── cm-dir
  3. ├── a.txt
  4. ├── b.txt
  5. └── subdir
  6. └── c.txt
  7. [root@liqiang.io]# kubectl create cm dir-cm --from-file=cm-dir
  8. configmap/dir-cm created
  9. [root@liqiang.io]# kubectl get cm dir-cm -o yaml
  10. apiVersion: v1
  11. data:
  12. a.txt: |
  13. a
  14. b.txt: |
  15. b
  16. kind: ConfigMap
  17. metadata:
  18. creationTimestamp: "2019-11-01T16:17:21Z"
  19. name: dir-cm
  20. namespace: default
  21. resourceVersion: "67950"
  22. selfLink: /api/v1/namespaces/default/configmaps/dir-cm
  23. uid: 052bc0de-c59c-4038-96ba-041a894fc532

可以看到,subdir 是会被忽略的!

ConfigMap 作为环境变量

好,那既然 ConfigMap 有了,光这么完也不是办法啊,所以,应用起来才是王道,所以,第一件事情我就是将 ConfigMap 中的某个值作为环境变量传递给容器:

  1. [root@liqiang.io]# kubectl create cm test-interval --from-literal=TEST_INTERVAL=30s
  2. configmap/test-interval created
  3. [root@liqiang.io]# kubectl apply -f 10-cm-env-pod.yaml
  4. pod/busybox-pod-cm-env created
  5. [root@liqiang.io]# kubectl exec -it busybox-pod-cm-env /bin/sh
  6. / # env
  7. KUBERNETES_PORT=tcp://10.43.0.1:443
  8. KUBERNETES_SERVICE_PORT=443
  9. HOSTNAME=busybox-pod-cm-env
  10. SHLVL=1
  11. HOME=/root
  12. TERM=xterm
  13. KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
  14. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  15. KUBERNETES_PORT_443_TCP_PORT=443
  16. KUBERNETES_PORT_443_TCP_PROTO=tcp
  17. INTERVAL=30s
  18. KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
  19. KUBERNETES_SERVICE_PORT_HTTPS=443
  20. KUBERNETES_SERVICE_HOST=10.43.0.1
  21. PWD=/

OK,这里我就演示成功了,如何将 ConfigMap 中的 KV 映射到 Container 中,可能你会觉得这个功能很鸡肋,但是,没关系,后面介绍 Secret 的时候你会看到类似方法的优点。

ConfigMap 作为命令行参数

作为命令行参数的行为也是类似的,但是,需要注意的是,args 没法直接引用 ConfigMap,所以得通过环境变量转换一次:

ConfigMap 作为文件挂载

重点的来了,更为普遍使用的 ConfigMap 的方式是用于挂载一个配置文件,例如 Prometheus 的配置是通过其他的 Container 修改 ConfigMap 之后再映射过来的。

这里可能还不太习惯的就是 Volume 的设置,没关系,你可以随便理解一下,在 Kubernetes 入门阶段的最后一篇中我会再次介绍 Volume 的。

  1. [root@liqiang.io]# kubectl apply -f 12-cm-file-pod.yaml
  2. pod/busybox-pod-args created
  3. [root@liqiang.io]# kubectl exec -it busybox-pod-args /bin/sh
  4. / # cat /tmp/a.txt/test.txt
  5. Hello, I'm Liqiang Lau
  6. My blog is: https://liqiang.io/

ConfigMap 作为目录挂载

ConfigMap 可以保存一个目录,同样的,也可以将这个目录直接挂载到指定的目录,挂载方式和 File 的方式一模一样,但是,这里有一点需要注意的是,无论是 File 的形式还是目录的形式挂载,对应挂载的目录下的原来的文件都将被清空!

Secret

虽然 ConfigMap 很好用了,但是,经过一些实践之后,大家发现 ConfigMap 有一些不足之处:

用法方面,Secret 和 ConfigMap 基本一致,所以就不多作介绍了。

但是,需要强调的一个就是 secret 并不 secret,也不要想着用它来安全存储敏感数据,因为它也仅仅是以非人类可直观阅读的方式来存储数据的,并没有对数据进行加密。即使你通过 RBAC 控制了访问权限,但是还是有不少方法可以绕过这个控制的,实在不济还可以从 Node 的挂载 volume 中查看。

现在除了你增强各种控制之外,比较通用的方式是通过第三方的服务实现,有两种比较主流可靠的也还算简单的方式: