ConfigMap和Secret

ConfigMapSecret

向容器传递命令行参数

Docker中定义命令与参数

容器中运行的完整指令由两部分组成, 命令与参数.

了解ENTRYPOINTCMD

ENTRYPOINT定义容器启动时被调用的可执行程序.

CMD指定传递给ENTRYPOINT的参数.

了解shellexec形式的区别

shell形式–如ENTRYPOINT node app.js.

exec形式– 如ENTRYPONT ["node", "app.js"].

两者的区别在于指定的命令是否在shell中被调用, 确定PID为1的进程是谁.

Kubernetes中覆盖命令和参数

Kubernetes中定义容器时, 镜像的ENTRYPOINTCMD均可以被覆盖, 仅需要在容器定义中设置属性commandargs的值.

1
2
3
4
5
6
kind: Pod
spec:
containers:
- image: some/image
command: ["/bin/command"]
args: ["args1", "args2", "args3"]

绝大多数情况下, 只需要设置自定义参数. 命令一般很少被覆盖, 除非针对一些未定义ENTRYPINT的通用镜像, 例如busybox.

commandargs字段在pod创建后无法被修改.

为容器设置环境变量

Kubernetes允许为pod中每一个容器都指定自定义的环境变量集合.

与容器的命令和参数设置相同, 环境变量列表无法在pod创建后被修改.

在容器定义中指定环境变量

1
2
3
4
5
6
7
8
kind: Pod
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
value: "30"
name: html-generator

在每个容器中, Kubernetes会自动暴露相同命名空间下每个service对应的黄金变量. 这些环境变量基本上可以被看作自动注入的配置.

了解硬编码环境变量的不足之处

pod定义硬编码意味着需要有效区分生产环境与开发过程中的pod定义. 为了能在多个环境下复用pod的定义, 需要将配置从pod定义描述中解耦出来.

利用ConfigMap解耦配置

应用配置的关键在于能够在多个环境中区分配置选项, 将配置从应用程序源码中分离, 可频繁变更配置值.

ConfigMap介绍

Kubernetes允许将配置选项分离到单独的资源对象ConfigMap中, 本质上就是一个键/值对映射, 值可以是短字面量, 也可以是完整的配置文件.

应用无须直接读取ConfigMap, 甚至根本不需要知道其是否存在. 映射的内容通过环境变量或者卷文件的形式传递给容器, 而非直接传递给容器. 命令行参数的定义中可以通过$(ENV_VAR)语法引用环境变量, 因而可以达到将ConfigMap条目当作命令行参数传递给进程的效果.

当然, 应用程序同样可以通过Kubernetes Rest API按需直接读取ConfigMap的内容. 不过除非是需求如此, 应尽可能使你的应用保持对Kubernetes的无感知.

创建ConfigMap

使用指令kubectl创建ConfigMap

1
kubectl create configmap <configMap的名字> --from-literal=<键>=<值>

键只能包含数字, 字母, 破折号, 下画线以及圆点. --from-literal参数用于指定多个条目.

1
2
3
4
5
6
apiVersion: v1
kind: ConfigMap
metadata:
name: <configMap的名字>
data:
<键>: <值>

从文件内容创建ConfigMap条目

ConfigMap同样可以存储粗粒度的配置数据, 比如完整的配置文件.

1
kubectl create configmap <configmap的名字> --from-file=<配置文件名>

运行上述命令时, kubectl会在当前目录下查找配置文件, 并将文件内容存储在ConfigMap中以配置文件名为键名的条目下. 也可以手动指定键名

1
kubectl create configmap <configmap的名字> --from-file=<键>=<配置文件名>

从文件夹创建ConfigMap

1
kubectl create configmap <configmap的名字> --from-file=/path/to/dir

这种情况下, kubectl会为文件夹中的每个文件单独创建条目, 仅限于那些文件名可作为合法ConfigMap键名的文件.

给容器传递ConfigMap条目作为环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: <Pod名称>
spec:
containers:
- image: some/image
env:
- name: <环境变量名>
valueFrom:
configMapKeyRef:
name: <configMap的名称>
key: <configMap的值>

引用不存在ConfigMap的容器会启动失败. 当之后创建了这个缺失的ConfigMap, 失败的容器会自动启动, 无须重新创建pod.

可以标记对ConfigMap的引用时可选的(设置configMapKeyRef.optional: true). 这样, 即便ConfigMap不存在, 容器也能正常启动.

一次性传递ConfigMap的所有条目作为环境变量

1
2
3
4
5
6
7
spec:
containers:
- image: some-image
envFrom:
- prefix: CONFIG_
configMapRef:
name: <configMap的名称>

前缀设置是可选的, 若不设置前缀值, 环境变量的名称与ConfigMap中的键名相同.

传递Configmap条目作为命令行参数

在字段pod.spec.containers.args中无法直接引用ConfigMap的条目, 但是可以利用ConfigMap条目初始化某个环境变量, 然后再在参数字段中引用该环境变量.

使用ConfigMap卷将条目暴露为文件

环境变量或者命令行参数值作为配置值通常适用于变量值较短的场景.

configMap卷会将ConfigMap中每个条目均暴露成一个文件. 运行在容器中的进程可通过读取文件内容获得对应的条目值.

1
2
3
4
volumes:
- name: config
configMap:
name: <configMap的名称>

卷内暴露指定的ConfigMap条目

1
2
3
4
5
6
7
volumes:
- name: config
configMap:
name: <configMap的名称>
items:
- key: <configMap中的键>
path: <cofigMap中键的新名称>

ConfigMap独立条目作为文件被挂载且不隐藏文件夹中的其他文件

假设拥有一个包含文件myconfig.confconfigMap卷, 希望能将奇添加为/etc文件夹下的文件someconfig.conf. 通过属性subPath可以将该文件挂载的同时又不影响文件夹中的其他文件.

1
2
3
4
5
6
7
spec:
containers:
- image: some/image
volumeMounts:
- name: myvolume
mountPath: /etc/someconfig.conf
subPath: myconfig.conf

挂载任意一种卷时均可以使用subPath属性. 可以选择挂载部分卷而不是挂载完整的卷. 捕获这种独立文件的挂载方式会带来文件更新上的缺陷.

configMap卷中的文件设置权限

configMap卷中所有文件的权限默认被设置为644. 可以通过卷规格定义中的defaultMode属性改变默认权限.

1
2
3
4
5
volumes:
- name: config
configMap:
name: <configMap的名称>
defaultMode: "6600"

更新应用配置且不重启应用程序

使用环境变量或命令行参数作为配置源的弊端在于无法在进程运行时更新配置. 将ConfigMap暴露为卷可以达到配置热更新的效果, 无须重新创建pod或者重启容器.

ConfigMap被更新之后, 卷中引用它的所有文件也会相应更新, 进程发现文件被改变之后进行重载(这要进程自己实现).

挂载至已存在文件夹的文件不会更新

如果挂载的是容器中的单个文件而不是完整的卷, ConfigMap更新之后对应的文件不会被更新.

如果现在你需要挂载单个文件并且在修改源ConfigMap的同时会自动修改这个文件, 一种方案是挂载完整卷至不同的文件夹并创建指向文件的符号链接. 符号链接可以原生创建在容器镜像中, 也可以在容器启动时创建.

使用Secret给容器传递敏感数据

SecretConfigMap相似, 均是键/值对的映射, 但用于保存一些敏感数据.

默认令牌Secret介绍

每个pod都会默认自动挂载上一个Secret卷, 引用一个默认的Secret.

为二进制数据创建Secret

采用Base64编码的原因很简单. Secret的条目可以涵盖二进制数据, 而不仅仅是纯文本. Base64编码可以将二进制数据转换为纯文本, 以YAMLJSON格式展示.

Secret甚至可以被用来存储非敏感二进制数据, Secret的大小限于1MB.

stringData字段介绍

由于并非所有敏感数据都是二进制形式, Kubernetes允许通过SecretstringData字段设置条目的纯文本值.

1
2
3
4
5
6
7
apiVersion: v1
kind: Secret
stringData:
fool: plain text
data:
https.cert: LSOtLSAAA
https.key: LSOtLSAAA

通过kubectl get -o yaml查看Secret时, stringData会出现在data字段.

pod中读取Secret条目

通过secret卷将Secret暴露给容器之后, Secret条目的值会被解码并以真实形式(纯文本或二进制)写入对应的文件. 通过环境变量暴露Secret条目也是如此. 应用程序无序主动解码, 可直接读取文件内容或查找环境变量.

Secret卷存储于内存

Secret卷采用内存文件系统列出容器的挂载点, 由于使用的是tmpfs, 存储在Secret中的数据不会写入磁盘, 这就无法被窃取.

通过环境变量暴露Secret条目

1
2
3
4
5
6
env:
- name: FOO_SECRET
valueFrom:
secretKeyRef:
name: <secret的名字>
key: <secret的键>

kubernetes允许通过环境变量暴露Secret, 然而此特性的使用往往不是一个好注意. 应用程序通常会在错误报告时转储环境变量, 或者是在启动时打印在应用日志中, 无意中暴露了Secret信息.

-------------本文结束感谢您的阅读-------------