概述
在 Kubernetes 的入门系列中,第 3,5,6,7,8,9 都是讲如何运行一个或者一系列 Container 相关的内容。在使用 Kubernetes 之前,我都是通过 Docker 或者 Docker-Compose 来运行 Container,在那个时候,如果在运行 Container 的时候,有两种方式可以给 Container 传递参数,分别是:
- 通过环境变量
[root@liqiang.io]# docker run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash
- 或者通过 CMD 参数的形式:
[root@liqiang.io]# docker run -it nginx:latest nginx -v
- CMD 的相关知识可以查看一下我以前的这篇:Entrypoint 和 CMD in docker
那么,问题就来了,上 Kubernetes 之后,我该如何在运行时指定 Container 的参数,以前的方式是否还能够延续。
给 Pod 传递参数
很显然,作为流行的容器编排项目,Kubernetes 应该是可以实现容器的各种要求的,所以,对于我前面提到的两种方式,也是可以直接使用的,因此,这里我就通过 Pod 来演示一下如何实现这两种方式:
通过环境变量给 Pod 传递参数
要想通过环境变量给 Container 传递一个参数,可以通过在 Container 的描述文件中就加入一个 env
的参数,值是一个数组,每个元素都是一个键值对,其中键是 string 类型的,而值可以有多种选择,这里演示的是最简单的字符串:
当我以这种方式运行 Pod 之后,再进入到 Pod 之中:
[root@liqiang.io]# kubectl exec -n liqiang-io -it busybox-pod-env /bin/sh
/ # env
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.43.0.1:443
HOSTNAME=busybox-pod-env
SHLVL=1
HOME=/root
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
INTERVAL=30s
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.43.0.1
PWD=/
就可以发现我设置的环境变量已经被塞入 Container 中了,而 Container 中的应用程序也就可以直接使用这个环境变量了。
通过命令行参数给 Pod 传递参数
和通过环境变量传递值类似,要想指定命令行参数来给 Container 传递参数,也只需要在 Container 的描述文件中加入一个 args
的配置。但是,和环境变量不同之处在于,命令行参数的值是一个字符串数组,而不是键值对数组:
这个 Pod 的功能很简单,只是把命令行参数打印出来就好了,所以,可以直接查看 Pod 的输出即可:
[root@liqiang.io]# kubectl logs -n liqiang-io busybox-pod-args
hello world
这个结果就是我所预期的,两种传递参数的方式都正常工作,但是,你可能就纳闷了,明明标题是 ConfigMap,为什么要讲这两种参数的传递方式?原因其实就是 ConfigMap 的通用用法,ConfigMap 正如其名,通常情况下都被用来存储配置信息,Pod 之间共享数据等,所以,了解 Pod 的参数传递对于后面理解 ConfigMap 的功能有较大的帮助。
ConfigMap
终于说道了 ConfigMap 了,在 Kubernetes 中,ConfigMap 是作为一种资源存在,缩略词是 cm
,所以可以这样简单的查看所有的 ConfigMap:
[root@liqiang.io]# kubectl get cm
No resources found in default namespace.
而 ConfigMap 作为一种资源,名字中也体现出来了,它是一个 Map,其实这就是一个存储了 KV 的资源,Key 很明显就是一个字符串类型的数据,而 Value 虽然是字符串类型,但是,表现形式可以各不一样,你既可以把它当作一个简单的字符串值,也可以当作文本文件内容处理。
ConfigMap 的操作
当然,get 用来获取 ConfigMap 只是一个最简单的操作,日常使用 ConfigMap 的功能要复杂得多,这里就介绍几种非常常见的 ConfigMap 使用方法:
直接设置 ConfigMap 的 KV
和其他的 Kubernetes 默认资源类似,可以通过 kubectl
直接设置 ConfigMap 的值:
[root@liqiang.io]# kubectl create cm first-cm --from-literal=key00=value00
configmap/first-cm created
这里有点奇怪的地方是为什么要用 --from-literal=key00=value00
,而不是直接 key00=value00
就好了?后面当你看到其他的选项的时候就理解了,而现在,你只需要知道,你已经往一个 Map 中塞入了一个 KV,key 就是 key00
,而 value 呢,则为:value00
。
直接获取 ConfigMap 的 KV
当把值塞入 ConfigMap 之后,下一步我将尝试直接把值读取出来:
[root@liqiang.io]# kubectl get cm
NAME DATA AGE
first-cm 1 3m9s
[root@liqiang.io]# kubectl get cm first-cm -o yaml
apiVersion: v1
data:
key00: value00
kind: ConfigMap
metadata:
creationTimestamp: "2019-11-01T16:03:47Z"
name: first-cm
namespace: default
resourceVersion: "67761"
selfLink: /api/v1/namespaces/default/configmaps/first-cm
uid: 9403c19d-339d-4d97-b757-4c0bd7428c02
这个时候,可能会问号脸了,不是说好了取值的吗?怎么给我看的是这个,很遗憾,kubectl 没有直接获取某一个 Key 的简单方法,但是好用的方式,我们后面再用。
将一个文本内容设置为 Value
更高级得,ConfigMap 可以直接讲一个文件塞入 KV 的 Map 中,其中文件名是 key,文本内容是 value,例如:
[root@liqiang.io]# cat test.txt
Hello, I'm Liqiang Lau
My blog is: https://liqiang.io
[[email protected]]# kubectl create cm file-cm --from-file=test.txt
configmap/file-cm created
[[email protected]]# kubectl get cm file-cm -o yaml
apiVersion: v1
data:
test.txt: |-
Hello, I'm Liqiang Lau
My blog is: https://liqiang.io
kind: ConfigMap
metadata:
creationTimestamp: "2019-11-01T16:14:17Z"
name: file-cm
namespace: default
resourceVersion: "67907"
selfLink: /api/v1/namespaces/default/configmaps/file-cm
uid: 113dd92a-6ad9-496d-8750-2496c31f118a
将一个目录转化成 KV
更过分得是,Kubernetes 居然允许将一个目录直接塞入 ConfigMap 之中,每个文件都是一个 KV:
[root@liqiang.io]# tree
├── cm-dir
│ ├── a.txt
│ ├── b.txt
│ └── subdir
│ └── c.txt
[root@liqiang.io]# kubectl create cm dir-cm --from-file=cm-dir
configmap/dir-cm created
[root@liqiang.io]# kubectl get cm dir-cm -o yaml
apiVersion: v1
data:
a.txt: |
a
b.txt: |
b
kind: ConfigMap
metadata:
creationTimestamp: "2019-11-01T16:17:21Z"
name: dir-cm
namespace: default
resourceVersion: "67950"
selfLink: /api/v1/namespaces/default/configmaps/dir-cm
uid: 052bc0de-c59c-4038-96ba-041a894fc532
可以看到,subdir 是会被忽略的!
ConfigMap 作为环境变量
好,那既然 ConfigMap 有了,光这么完也不是办法啊,所以,应用起来才是王道,所以,第一件事情我就是将 ConfigMap 中的某个值作为环境变量传递给容器:
[root@liqiang.io]# kubectl create cm test-interval --from-literal=TEST_INTERVAL=30s
configmap/test-interval created
[root@liqiang.io]# kubectl apply -f 10-cm-env-pod.yaml
pod/busybox-pod-cm-env created
[root@liqiang.io]# kubectl exec -it busybox-pod-cm-env /bin/sh
/ # env
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=busybox-pod-cm-env
SHLVL=1
HOME=/root
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
INTERVAL=30s
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.43.0.1
PWD=/
OK,这里我就演示成功了,如何将 ConfigMap 中的 KV 映射到 Container 中,可能你会觉得这个功能很鸡肋,但是,没关系,后面介绍 Secret 的时候你会看到类似方法的优点。
ConfigMap 作为命令行参数
作为命令行参数的行为也是类似的,但是,需要注意的是,args 没法直接引用 ConfigMap,所以得通过环境变量转换一次:
ConfigMap 作为文件挂载
重点的来了,更为普遍使用的 ConfigMap 的方式是用于挂载一个配置文件,例如 Prometheus 的配置是通过其他的 Container 修改 ConfigMap 之后再映射过来的。
这里可能还不太习惯的就是 Volume 的设置,没关系,你可以随便理解一下,在 Kubernetes 入门阶段的最后一篇中我会再次介绍 Volume 的。
[root@liqiang.io]# kubectl apply -f 12-cm-file-pod.yaml
pod/busybox-pod-args created
[root@liqiang.io]# kubectl exec -it busybox-pod-args /bin/sh
/ # cat /tmp/a.txt/test.txt
Hello, I'm Liqiang Lau
My blog is: https://liqiang.io/
ConfigMap 作为目录挂载
ConfigMap 可以保存一个目录,同样的,也可以将这个目录直接挂载到指定的目录,挂载方式和 File 的方式一模一样,但是,这里有一点需要注意的是,无论是 File 的形式还是目录的形式挂载,对应挂载的目录下的原来的文件都将被清空!
Secret
虽然 ConfigMap 很好用了,但是,经过一些实践之后,大家发现 ConfigMap 有一些不足之处:
- ConfigMap 的键值对都是明文存储和显示,而 Secret 是编码过的;
- ConfigMap 只能纯文本,而 Secret 可以存储二进制;
用法方面,Secret 和 ConfigMap 基本一致,所以就不多作介绍了。
但是,需要强调的一个就是 secret 并不 secret,也不要想着用它来安全存储敏感数据,因为它也仅仅是以非人类可直观阅读的方式来存储数据的,并没有对数据进行加密。即使你通过 RBAC 控制了访问权限,但是还是有不少方法可以绕过这个控制的,实在不济还可以从 Node 的挂载 volume 中查看。
现在除了你增强各种控制之外,比较通用的方式是通过第三方的服务实现,有两种比较主流可靠的也还算简单的方式:
- sidecar 支持
- 定制 CSI provider