ConfigMap and Secret
1. Introduction
Container技术(例如Docker)提供了三种简易方式来为运行在container中的应用提供configuration:
- 为container传递command-line arguments
- 为container设置不同的environment variables
- 将configuration file以Volume的形式挂在container上
Kubernetes也提供了两种Resources为container提供configuration:
- ConfigMap: 用于存储配置信息
- Secret: 用于存储敏感信息
2. Pass Command-line Arguments to Containers
Kubernetes允许传递给Pod中的container一些command-line arguments.
2.1 Define the Command and Arguments in Docker
Dockerfile提供了两个instructions:
- ENTRYPOINT: 当container启动时运行的command
- CMD: 主要为ENTRYPOINT提供参数, 也可执行command
当运行**docker run <image>时会自动调用ENTRYPOINT和CMD, 也可以使用docker run <image> <arguments>**来覆盖CMD中的参数.
- ENTRYPOINT存在两种形式:
- exec: ["executable", "param1", "param2"]
- shell: command param1 param2
- CMD存在三种形式:
- exec: ["executable", "param1", "param2"]
- shell: command param1 param2
- parameters to ENTRYPOINT: ["param1", "param2"]
exec形式会直接开启一个进程来执行command, shell形式会先开启一个shell进程, 再在shell进程中执行command. 一般情况下使用exec形式即可.
2.2 Override the Command and Arguments in Kubernetes
Kubernetes提供了command和args来覆盖image中的ENTRYPOINT和CMD:
... |
所以一般不用定义ENTRYPOINT和CMD, 直接在Pod中定义即可. 注意: args中的数字必须用double quote(双引号)包裹, 字符串则不必.
3. Set Environment Variables for a Container
Kubernetes允许为Pod中的每个container定制不同的environment variables, 如下图:
Environment variables之所以能作为配置参数, 因为所有脚本和语言都可以获取系统的environment variables; 但与command-line argument相同, 一旦container被启动, 则很难修改.
... |
Kubernetes还支持在YAML中使用之前设置的environment variables: 使用**$(VAR)**语法即可调用之前定义的environment variables:
... |
上述例子中, FIRST_VAR为foo, SECOND_VAR为foobar. 由于environment variable无法随时修改, 所以不同需求的Pod需要多个的YAML文件来标注不同的environment variables.
4. ConfigMap
ConfigMap作为Kubernetes的resource之一, 可将configuration中的内容与Pod定义分离, 以key/value的map形式作为configuration的格式. Container并不会直接读取ConfigMap中的配置信息, 而是通过environment variables或volume形式来读取配置信息, 如下图:
ConfigMap不与某个Pod绑定, 只与namespace绑定, 所以同一namespace下的Pod共享ConfigMap.
4.1 Create a ConfigMap
$ kubectl create configmap fortune-config --from-literal=sleep-interval=25 |
通过kubectl create configmap command可直接创建ConfigMap, 上述指令创造了一个名为fortune-config的ConfigMap, 包含一个entry, 其中key为sleep-interval, value为25. 也可在ConfigMap中创建多个entries:
$ kubectl create configmap myconfigmap --from-literal=foo=bar \ |
调用kubectl get configmap command可查看当前ConfigMap
$ kubectl get configmap fortune-config -o yaml |
--from-literal表示直接写入Entry的key和value. 还可使用**--from-file**从文件中导入entry, 例如:
$ kubectl create configmap my-config --from-file=config-file.conf |
上述新建的ConfigMap名为my-config, 包含一条entry, 其中key为config-file.conf(也就是文件名), value为config-file.conf文件里的内容. 还可从directory中将所有文件加入到ConfigMap中:
$ kubectl create configmap my-config --from-file=/path/to/dir |
所有目录下的文件都会加入到ConfigMap中, 其中key为文件名, value为文件内容. ConfigMap也支持这几种方式的混合使用, 如下:
$ kubectl create configmap my-config \ |
结果如下图:
4.2 Pass a ConfigMap Entry to a Container as an Environment Variable
在Pod中将ConfigMap中的entry传给container的environment variable是container读取ConfigMap的最简单方式:
apiVersion: v1 |
上述例子中, container中设置environment variable的key为INTERVAL, value从名为fortune-config的ConfigMap中获取key为sleep-interval的value值. 若YAML文件中标注的ConfigMap不存在, 则该container不会被启动, 但Pod中的其他container照常运行; 当缺失的ConfigMap被创建后, container也会自动启动. 若在YAML中设置configMapKeyRef.optional: true, 即使ConfigMap不存在, container依然会启动.
4.3 Pass all Entries of a ConfigMap as Environment Variable
Kubernetes 1.6提供了一种可以将ConfigMap中所有entries导入到Container的environment variable的方法:
spec: |
注意, environment variable不支持一些特殊符号. 若ConfigMap中entry的key或value含有不合规的符号(例如: foo-bar), 则该entry不被导入environment variable.
4.4 Pass a ConfigMap Entry as a Command-line Argument
ConfigMap中的entry也可作为参数传入container:
apiVersion: v1 |
结果如下图:
4.5 Use a ConfigMap Volume to Expose ConfigMap Entries as Files
将ConfigMap挂载为volume只需要将volume的类型设置为configMap即可. 假设现在给Nginx添加配置文件my-nginx-config.conf, 文件内容如下:
server { |
将该文件和名为sleep-interval的文件放入同一文件夹中, 如下图:
调用kubectl create configmmap创建新的ConfigMap:
$ kubectl create configmap fortune-config --from-file=configmap-files |
Nginx从**/etc/nginx/nginx.conf**路径中读取文件并添加到配置, 因此需要将ConfigMap挂在该路径上:
apiVersion: v1 |
结果如下:
也可以指定ConfigMap中的某一个或几个entry挂在volume上:
volumes: |
需要注意的是, 上述使用ConfigMap作为volume会覆盖挂载路径中的所有文件. 若**/etc/nginx/conf.d中原本有其他文件, 则会因为fortune-config的挂载而消失. 为了保证不覆盖原本路径中的文件, 需在YAML中添加subPath**属性:
spec: |
这样就可以保证ConfigMap Volume只有myconfig.conf被挂载到container的/etc/someconfig.conf路径中, 如下图:
也可以在YAML中设置ConfigMap Volume中文件的权限:
volumes: |
4.6 Update Config without Restarting the App
Environment variable和command-line argument的缺陷在于无法及时更新, 一旦Pod运行则无法修改. ConfigMap Volume则可无需container重启的同时更新配置信息. 调用kubectl edit可修改ConfigMap:
$ kubectl edit configmap fortune-config |
由于Nginx不会主动查看配置文件是否更新, 所以需要向Nginx发送信号来重新加载:
$ kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload |
ConfigMap的修改和读取都是原子性的, 所以不必担心container会读取修改中的配置文件. Kubernetes会等待ConfigMap修改完毕后将新的文件写入新的文件夹中, 并relink到新的文件夹中. 注意: 如果在container中使用subPath而不是mount整个volume, 则文件无法随着ConfigMap的修改而刷新.
若Pod中的container不支持自动检查ConfigMap是否被修改, 则不建议修改当前ConfigMap. 因为Pod随时可能被重新启动, 重启后的container会使用新的配置信息, 而旧的container还会使用旧的配置信息.
5. Screts
ConfigMap已经为container提供了完整的配置获取方案, 但有时需要存储一些敏感信息, 如: 秘钥, 证书. 为此Kubernetes提供Secrets, 可通过两种方式来获取信息:
- 将Secret entry作为environment variables传入container
- 将Secret entry作为volume挂在Pod上
5.1 default token Secret
为保证Secret的安全性, Secret永远不会写入物理存储设备, 只会保留在内存中. 每个Pod都会自带一些Secret, 可通过调用kubectl describe pod command来查看Pod绑定了哪些Secret:
$ kubectl describe pod <pod_name> |
也可通过kubectl get secrets command来查看当前namespace下的所有secrets:
$ kubectl get secrets |
若需要查看某个secret的具体信息, 可调用kubectl describe secret:
$ kubectl describe secret default-token-t75t5 |
其中ca.crt和token是为了让Pod与Kubernetes API server进行安全通信. 该Secret作为volume会被挂靠在Pod的**/var/run/secrets/kubernetes.io/serviceaccount**路径上.
5.2 Create a Secret
以之前的Nginx container为例, 可通过Secret在container中配置certificate和private key来让Nginx支持HTTPS. 首先需要生成certificate和private key并将其放置文件之中:
$ openssl genrsa -out https.key 2048 |
为更好的表现Secret的性质, 再添加另外一个文件:
$ echo bar > foo |
最后通过kubectl create secret command来创建一个Secret:
$ kubectl create secret generic fortune-https --from-file=https.key \ |
5.3 Compare ConfigMap and Secret
虽然ConfigMap和Secret都是ky-value的map格式, 但其实存在很大差异:
- Secret entry中的key依然是明文形式, 但value使用Base43-encoded形式编码
- Secret支持二进制数据, 因为Base64可对二进制数据进行编码
- Secret支持stringData属性来放置该entry的value被Base64编码
- 当Secret的entry被当做environment variable或volume时, entry会被自动解码, 因此container内的进程无需解码操作
5.4 Use the Secret in a Pod
为了让Nginx支持HTTPS, 需要将secret中的certificate和private key传给Nginx:
apiVersion: v1 |
结果如下:
也可以将Secret entry写入environment variable中:
... |