Kubernetes in Action笔记 - (10) 使用ConfigMap与Secret传递应用配置
文章目录
向容器传递应用程序的配置参数
方法:
- 向容器传递命令行参数
- 为每个容器设置自定义环境变量
- 通过特殊类型的卷将配置文件挂载到容器中
向容器传递命令行参数
在Docker中定义命令与参数
容器中运行的完整指令由两部分组成:命令与参数。Dockerfile中的两种指令分别定义命令与参数这两个部分:
- ENTRYPOINT: 定义容器启动时被调用的可执行程序
- CMD: 指定传递给ENTRYPOINT的参数。
尽管可以直接使用CMD指令指定镜像运行时想要执行的命令, 正确的做法依旧 是借助ENTRYPOINT指令, 仅仅用CMD指定所需的默认参数。 这样, 镜像可以直 接运行, 无须添加任何参数
1docker run <image>
或者是添加一些参数, 覆盖Dockerile中任何由CMD指定的默认参数值:
1docker run <image> <arguments>
shell与exec的区别
上述两条指令均支持以下两种形式。两者的区别在于指定的命令是否是在shell中被调用。
- shell形式。如ENTRYPOINT node app.js。
- exec形式。如ENTRYPOINT ["node", "app.js"]。
下面用例子来看他们的区别
1# exec形式
2# 从返回的进程列表看出:这里是直接运行node进程,而并非在shell中执行。
3$ docker exec 4675d ps x
4PID TTY STAT TIME COMMAND
51 ? Ssl 0:00 node app.Js
612 ? Rs 0:00 ps x
7
8# shell形式
9# 可以看出,主进程(PID 1)是shell进程而非node进程,node进程(PID 7)于shell中启动。
10$ docker exec -it e4bad ps x
11PID TTY STAT TIME COMMAND
121 ? Ss 0:00 /bin/sh -c node app. J s
137 ? Sl 0:00 node app.js
1413 ? Rs+ 0:00 ps x
shell进程往往是多余的, 因此通常可以直接采用exec形式的ENTRYPOINT指令。
可配置化镜像中的间隔参数
编辑fortuneloop.sh脚本
1#!/bin/bash
2trap "exit" SIGINT
3INTERVAL=$1
4echo Configured to generate new fortune every $INTERVAL seconds
5mkdir -p /var/htdocs
6while
7do
8echo $(date) Writing fortune to /var/htdocs/index.html
9/usr/games/fortune > /var/htdocs/index.html
10sleep $INTERVAL
11done
编写docker file,采用exec形式的ENTRYPOINT命令,并设定默认参数
1FROM ubuntu:latest
2RUN apt-get update; apt-get -y install fortune
3ADD fortuneloop.sh /bin/fortuneloop.sh
4ENTRYPOINT ["/bin/fortuneloop.sh"]
5CMD ["10"] # 默认参数
启动docker时,另外指定参数值15覆盖默认的值
1docker run -it docker.io/luksa/fortune:latest 15
在 Kubernetes 中覆盖命令和参数
在 k8s 中定义容器时, 镜像的 ENTRYPOINT 和 CMD 均可以被覆盖, 仅需在容器定义中设置属性 command 和 args 的值.
1kind: Pod
2spec:
3 containers:
4 - image: some/image
5 command: ["/bin/command" l
6 args: ["argl", "arg2", "arg3"]
绝大多数情况下, 只需要设置自定义参数。命令一般很少被覆盖, 除非针对一些未定义 ENTRYPOINT 的通用镜像
为容器设置环境变量
注意:
- 环境变量被设置在pod的容器定义中,并非是pod级别
- 在每个容器中,k8s会自动暴露相同命名空间下每个service对应的环境变量。这些环境变量基本上可以被看作自动注入的配置。
1kind: Pod
2spec:
3 containers:
4 - image: luksa/fortune:env
5 #添加环境变量
6 env:
7 - name: INTERVAL
8 value: "30"
9 name: html-generator
可以采用$(VAR)语法在环境变量值中引用其他的环境变量
1env:
2- name: FIRST_VAR
3 value: "foo"
4- name: SECOND_VAR
5 value: "$(FIRST_VAR)bar"
缺点:硬编码环境变量,就意味着Pod定义必须可以有效区分生产环境与开发环境,否则会带来一些麻烦。
利用 ConfigMap 解耦配置
应用配置的关键在于能够在多个环境中区分配置边项,将配置从应用程序源码中分离,可频繁变更配置值。如果将 pod 定义描述看作是应用程序源代码,显然需要将配置移出 pod 定义
什么是ConfigMap
k8s允许将配置选项分离到单独的资源对象 ConfigMap 中, 它本质上就是一个键值对,值可以是短字面量,也可以是完整的配置文件。
应用无须直接读取 ConfigMap, 甚至根本不需要知道其是否存在。映射的内容并非直接传递给容器,可以通过下面的形式传递给容器:
- 环境变量
- 卷文件
- 通过k8s API直接按需读取ConfigMap条目。不推荐这么做,因为要保持应用对k8s无感知。
命令行参数的定义中可以通过 $ (ENV VAR )语法引用环境变量,因而可以达到将 ConfigMap 的条目当作命令行参数传递给进程的效果。
pod是通过名称引用 ConfigMap 的,因此可以在多环境下使用相同的 pod 定义描述,同时保持不同的配置值以适应不同环境。
注意:
- ConfigMap 中的 键名必须是一个合法的 DNS 子域,仅包含数字字母、破折号、下画线以及圆点。首位的圆点符号是可选的 。
- ConfigMap通常被用作存储非敏感数据
创建ConfigMap
通过字面量创建条目
1# 单条目
2kubectl create configmap fortune-config --from-literal=sleep-interval=25
3
4# 包含多条目
5kubectl create conf igmap myconfigmap --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
从文件内容创建 ConfigMap 条目
1# 在当前目录下查找 config-file.conf 文件, 并将文件内容存储在 ConfigMap 中以 config-file.conf 为键名的条目下
2kubectl create configmap my-config --from-file=config-file.conf
3
4# 手动指定键名
5kubectl create configmap my-config -from-file=customkey=config file.conf
从文件夹创建 ConfigMap
1# 为文件夹中的每个文件单独创建条目,仅限于那些文件名可作为合法 ConfigMap 键名的文件
2kubectl create configmap my -config --from-file=/path/to/dir
通过上述多种方式创建
1kubectl create configmap my-config --from-file=foo.json --from-file=foobar.conf --from-literal=some=thing
通过ConfigMap向容器传递参数
(1)以环境变量的方式
仅传递特定的条目
1env:
2- name: INTERVAL
3 valueFrom:
4 configMapKeyRef:
5 name: fortune-config
6 key: sleep-interval
默认情况下,引用不存在的 ConfigMap , 那容器会启动失败。可以标记对 ConfigMap 的引用是可选的(设置configMapKeyRef.optional: true )。这样,即使 ConfigMap 不存在,容器也能正常启动。
一次性传递 ConfigMap 的所有条目作为环境变量
1spec:
2 containers:
3 - image: some-image
4 envFrom:
5 - prefix: CONFIG_ # 所有环境变量包含前缀CONFIG_
6 configMapRef:
7 name: my-config-map
前缀设置是可选的,若不设直前缀佳,环境变量的名称与 ConfigMap 中的键名相同.
(2)以命令行参数的方式
在宇段 pod.spec.containers.args 中无法直接引用 ConfigMap 的条目,但是可以利用 ConfigMap 条目初始化某个环境变 量 ,然后再在参数字段中引用该环境变量
1apiVersion: vl
2kind: Pod
3metadata:
4 name: fortune-args-from-configmap
5spec:
6 containers:
7 - image: luksa/fortune:args
8 env:
9 - name: INTERVAL
10 valueFrom:
11 configMapKeyRef:
12 name: fortune-config
13 key : sleep-interval
14 args : ["$(INTERVAL)"]
(3) 将条目暴露为文件
环境变量或者命令行参数值作为配置值通常适用于变量值较短的场景。由于 ConfigMap 中可以包含完整的配置文件内容, 可以通过 configMap 卷将onfigMap 中的每个条目均暴露成一个文件。
1apiVersion: v1
2kind: Pod
3metadata:
4 name: fortune-configmap-volume
5spec:
6 containers:
7 - image: nginx:alpine
8 name: web-server
9 volumeMounts:
10 ...
11 # 挂载configMap
12 - name: config
13 mouthPath: /etc/nginx/conf.d
14 readOnly: true
15 ...
16 volumes:
17 ...
18 # 定义卷来应用configMap
19 - name: config
20 configMap:
21 name: fortune-config
卷可以仅暴露指定的ConfigMap条目
1volumes:
2- name: config
3 configMap:
4 name: fortune-config
5 items: # 选择暴露的条目
6 - key: my-nginx-config.conf # 该键对应的条目被包含
7 path: gzip.conf # 条目的值被存储在该文件中
为configMap卷中的文件设置权限
1volumes:
2- name: config
3 configMap:
4 name: fortune-config
5 defaultMode: "6600" #指定权限
注意:
- 挂载某一文件夹会隐藏该文件夹中已存在的文件
- ConfigMap独立条目作为文件被挂载且不隐藏文件夹中的其他文件
使用环境变量或者命令行参数作为配置源的弊端在于无法在进程运行时更新配置。将ConfigMap暴露为卷可以达到配置热更新的效果, 无须重新创建pod或者重启容器。ConfigMap被更新之后, 卷中引用它的所有文件也会相应更新, 进程发现文件被改变之后进行重载。但是,这仅仅是容器里面的文件发生的变化,如果应用程序本身需要重启才能加载这些文件,那还是要重启的。比如nginx.conf文件,虽然配置文件已经发生了变化,还是需要手动执行 nginx -s reload才正式生效。
使用 Secret 给容器传递敏感数据
Secret介绍
配置通常会包含一些敏感数据, 如证书和私钥, 需要确保其安全性。
Secret 结构与 ConfigMap 类似, 均是键/值对的映射,使用方法也相同。Secret 只会存储在节点的内存中, 永不写入物理存储, 这样从节点上删除 Secret 时就不需要擦除磁盘了。
从k8s 1.7开始, etcd 会以加密形式存储 Secret, 某种程度提高了系统的安全性。
如何选择 ConfigMap与Secret
- 采用 ConfigMap 存储非敏感的文本配置数据
- 采用 Secret 存储天生敏感的数据, 通过键来引用。如果一个配置文件同时包含敏感与非敏感数据, 该文件应该被存储在 Secret 中
默认secret卷
每个pod都会被自动挂载上一个默认的secret卷,这个Secret包含三个条目:ca.crt、namespace与token, 包含了从pod内部安全访问KubernetesAPI服务器所需的全部信息。尽管希望做到应用程序对k8s的完全无感知,然而在除了直连k8s别无他法的情况下,将会使用到secret卷提供的文件。
挂载secret卷
1volumes:
2- name: certs
3 secret:
4 secretName: fortune-https
暴露成环境变量
1env:
2- name: FOO_SECRET
3 valueFrom:
4 secretKeyRef:
5 name: fortune-https
6 key: foo
使用secret从私有仓库拉取镜像
先创建用于私有镜像仓库鉴权的secret
1kubectl create secret docker-registry mydockerhubsecret \
2--docker-username=myusername --docker-password=mypassword \
3--docker-email=[email protected]
然后在pod定义中使用该secret
1apiVersion: vl
2kind : Pod
3metadata:
4 name: private-pod
5spec:
6 imagePullSecrets:
7 - name: mydockerhubsecret
8 containers:
9 - image : username/private:tag
10 name : main
图书资料: