16 K8S ConfigMap 和 Secret 配置应用程序


配置容器化应用程序

1
2
3
4
# 应用程序 会将配置嵌入到程序本身 方式:
1. 命令行参数
2. 配置选项数量增多 -> 配置文件
3. 借助环境变量
1
2
3
4
5
6
7
8
9
10
11
12
# 为何使用 环境变量
1. 在 Docker 环境中采用 配置文件方式 有些困难,需要将配置文件打入镜像 或者是挂载包含该文件的卷
2. 打入镜像 相当于 硬编码配置,修改配置需要重新构建新的镜像
3. 挂载卷方便一些,但是在容器启动前就要先完成配置文件写入卷里
4. ConfigMap 可以做为存储配置数据的资源

# 无论是否使用 ConfigMap 存储配置数据 下面访问均可以用作配置应用程序
1. 向容器传递命令行参数
2. 为每个容器设置自定义环境变量
3. 通过特殊类型的卷 将配置文件挂载到容器里

# 如果我们存在敏感数据 如证书私钥、连接数据库的账户密码等,需要保持安全的数据,可以使用Secret

容器传递命令行参数

在 Docker 中定义命令与参数

1
2
3
4
5
6
7
8
1. 容器中运行完整的指令两部分组成: 命令与参数
2. Dockerfile 中的两种指令分别定义 命令与参数两个部分
ENTRYPOINT: 定义容器启动时,被调用的可执行程序
CMD: 指定传送给ENTRYPOINT的参数
3. 正确的做法应该是使用ENTRYPOINT指令,用CMD指定所需要的默认参数,这样可以让镜像直接运行
docker run <image>
如果需要附加参数,覆盖Dockerfile中的CMD指定的默认参数值
docker run <image> <arg>

了解 shell 和 exec 的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
shell: ENTRYPOINT node app.js
exec: ENTRYPOINT ["node","app.js"]

# 区别在于 命令是否在 shell中被调用

# exec 形式:
[root@k8s-master2 base]# vim Dockerfile
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node","app.js"]

[root@k8s-master2 ~]# docker run --name kubia-container -p 8080:8080 -d kubia
[root@k8s-master2 ~]# docker exec kubia-container ps -x
PID TTY STAT TIME COMMAND
1 ? Ssl 0:00 node app.js
11 ? Rs 0:00 ps -x

# Shell 形式
[root@k8s-master2 base]# vim Dockerfile

FROM node:7
ADD app.js /app.js
ENTRYPOINT node app.js

[root@k8s-master2 base]# docker build -t kubia-shell .
[root@k8s-master2 base]# docker run --name kubia-container-shell -p 8080:8080 -d kubia-shell
[root@k8s-master2 base]# docker exec kubia-container-shell ps -x
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/sh -c node app.js
6 ? Sl 0:00 node app.js
12 ? Rs 0:00 ps -x
1
2
# 主进程(pid=1) 是 shell进程 而非 node 进程,node(pid=7)进程于shell中启动
# shell 进程是多余的,因此通常可以直接采用exec形式的 ENTRYPOINT 命令

可配置化镜像中的间隔参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 修改循环延迟间隔,使其可配置
# $1 作为第一个命令行参数 传入到 脚本中,并且在Dockerfile中定义

[root@k8s-master2 args]# vim fortuneloop.sh

#!/bin/bash
trap "exit" SIGINT
mkdir -p /var/htdocs
NUM=$1
echo "设定的间隔是 ${NUM} 秒"
for i in {1..5}:
do
echo "$(date "+%H:%M:%S") $i" >> /var/htdocs/index.html
sleep $NUM;
done

[root@k8s-master2 args]# vim Dockerfile

FROM centos:7
ADD fortuneloop.sh /bin/fortuneloop.sh
ENTRYPOINT ["/bin/sh","/root/fortuneloop.sh"] # exec形式的 ENTRYPOINT
CMD ["5"] # 可执行程序的默认参数 5秒

[root@k8s-master2 args]# docker build -t 172.31.228.68/project/fortune:args .
[root@k8s-master2 args]# docker push 172.31.228.68/project/fortune:args

[root@k8s-master2 args]# docker run -it 172.31.228.68/project/fortune:args
设定的间隔是 5 秒

[root@k8s-master2 args]# docker exec 7756f62121a4 ps x
PID TTY STAT TIME COMMAND
1 pts/0 Ss+ 0:00 /bin/sh /root/fortuneloop.sh 5
12 pts/0 S+ 0:00 sleep 5
13 ? Rs 0:00 ps x
1
2
3
4
5
6
7
8
9
# 传递一个间隔参数覆盖默认值
[root@k8s-master2 args]# docker run -it --rm 172.31.228.68/project/fortune:args 10
设定的间隔是 10 秒

[root@k8s-master2 ~]# docker exec f51246e5faaf ps x
PID TTY STAT TIME COMMAND
1 pts/0 Ss+ 0:00 /bin/sh /root/fortuneloop.sh 10
12 pts/0 S+ 0:00 sleep 10
13 ? Rs 0:00 ps x

在 K8S 中覆盖命令和参数

1
2
3
4
5
6
7
8
9
10
11
1. 在k8s定义容器时,镜像的ENTRYPOINT 和 CMD 是可以被覆盖的
2. 需要在容器属性中定义 command 和 args 值
3. command 和 args字段在Pod创建后无法修改

spec:
containers:
- image: centos:7 # 容器1 写数据到 /var/htdocs/index.html
name: html-generator
# command: ["bash","-c","for i in {1..100};do echo $i >> /var/htdocs/index.html;sleep 10;done"]
command: ["/bin/command"]
args: ["arg1","arg2","arg3"]
1
2
3
Docker          K8S             描述
ENTRYPOINT command 容器中运行的可执行文件
CMD args 传给可执行文件的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@k8s-master1 forloop]# vim fortune-pod.yaml 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:
- image: 172.31.228.68/project/fortune:args
name: html-generator
# command:
args: ["6"] # 参数会在pod运行时传递给容器
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}

# 多个值
args:
- foo
- bar
- "10"

# 字符串无需用引号标记,数值需要
# 通过命令行传参指定参数是给容器传递配置选项的其中一种方法,下面是另一种通过环境变量完成配置

[root@k8s-master1 forloop]# kubectl logs fortune-t6c87 -c html-generator
设定的间隔是 6 秒

容器设置环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 脚本中删除 命令行传参 NUM=$1
# 如果是Python编写 获取变量的方法 os.environ['NUM']

[root@k8s-master2 env]# vim fortuneloop.sh

#!/bin/bash
trap "exit" SIGINT
mkdir -p /var/htdocs
echo "设定的间隔是 ${NUM} 秒"
for i in {1..5}:
do
echo "$(date "+%H:%M:%S") $i" >> /var/htdocs/index.html
sleep $NUM;
done

[root@k8s-master2 env]# docker build -t 172.31.228.68/project/fortune:env .
[root@k8s-master2 env]# docker push 172.31.228.68/project/fortune:env

[root@k8s-master1 forloop-env]# vim fortune-pod.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:
- image: 172.31.228.68/project/fortune:env
name: html-generator
# command:
# args: ["6"]
env:
- name: NUM
value: "22"
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}

[root@k8s-master1 forloop-env]# kubectl logs fortune-d82g5 -c html-generator
设定的间隔是 22 秒

[root@k8s-master1 forloop-env]# kubectl exec -it fortune-d82g5 bash -c html-generator

[root@fortune-d82g5 /]# ps -x
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/sh /root/fortuneloop.sh 5 # dockerfile 里面传了 cmd
16 ? S 0:00 sleep 22
22 pts/0 Ss 0:00 bash
35 pts/0 R+ 0:00 ps -x

环境变量引用其他环境变量

1
2
3
4
5
6
7
8
env:
- name: NUM
value: "22"
- name: Name_NUM
value: "$(NUM)Leo"

# Name_NUM 的值是 22Leo
# command 和 args 也可以这样使用

硬编码环境变量

1
2
3
4
1. pod中定义硬编码环境变量 无法有效区分 生产环境与开发环境的pod定义
2. 为了能够复用pod的定义。需要将配置从pod中解耦出来
3. ConfigMap 资源对象可以完成解耦
4. 用valueFrom 字段代替 value字段,使ConfigMap成为环境变量值的来源

使用 ConfigMap 解耦配置

1
2
3
4
5
6
7
8
1. K8S 可以将配置选项分离到 单独的资源对象 ConfigMap中
2. 本质是 键值对映射
3. 值可以使配置字段 也 可以是 完整的配置文件
4. 应用无法直接读取 ConfigMap
5. 映射内容通过 环境变量 或者 卷文件的形式传送给容器
6. pod 通过环境变量 或者 ConfigMap卷 来使用ConfigMap
7. 有助于区分不同环境(开发、测试、生产)下多分不同的配置清单
8. pod 通过 ConfigMap 名称来引用,多环境下使用相同的pod定义,保持不同的ConfigMap使用不同的环境配置

创建 ConfigMap

通过 kubectl 创建 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. ConfigMap的键名 必须是一个合法的DNS子域 数字字母、破折号、下划线和圆点

[root@k8s-master1 demo]# kubectl create configmap fortune-config --from-literal=sleep-interval=25
configmap/fortune-config created

[root@k8s-master1 demo]# kubectl get configmap
NAME DATA AGE
fortune-config 1 107s

# 2. 创建多映射键值对的 ConfigMap

[root@k8s-master1 demo]# kubectl create configmap my-config --from-literal=name=leo --from-literal=age=29
configmap/my-config created

[root@k8s-master1 demo]# kubectl get cm
NAME DATA AGE
fortune-config 1 4m22s
my-config 2 10s

通过 yaml 文件创建 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master1 configmap]# kubectl get cm fortune-config -o yaml > fortune-config.yaml
[root@k8s-master1 configmap]# vim fortune-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: fortune-config # 通过name引用configmap
namespace: default
data: # 数据 键值对
sleep-interval: "25"

[root@k8s-master1 configmap]# kubectl create -f fortune-config.yaml

[root@k8s-master1 configmap]# kubectl get cm
NAME DATA AGE
fortune-config 1 14s

从文件内容创建 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master1 configmap]# cat my.conf 
leo

[root@k8s-master1 configmap]# kubectl create configmap my-config --from-file=my.conf
configmap/my-config created

[root@k8s-master1 configmap]# kubectl describe cm my-config
Name: my-config
Namespace: default

Data
====
my.conf: # 健
----
leo # 值
1
2
3
4
5
6
7
8
9
10
11
12
13
# 指定键名

[root@k8s-master1 configmap]# kubectl create configmap my-config --from-file=name=my.conf
configmap/my-config created
[root@k8s-master1 configmap]# kubectl describe cm my-config
Name: my-config
Namespace: default

Data
====
name:
----
leo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master1 configmap]# cat redis.conf 
redis.hosts=127.0.0.1
redis.port=6379
redis.password=123456

[root@k8s-master1 configmap]# kubectl describe cm reids-config
Name: reids-config
Namespace: default
...

Data
====
redis.conf:
----
redis.hosts=127.0.0.1
redis.port=6379
redis.password=123456

使用 变量方式 传给容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[root@k8s-master1 configmap]# vim fortune-pod.yaml 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune-env
spec:
selector:
matchLabels:
app: fortune-env
template:
metadata:
labels:
app: fortune-env
spec:
containers:
- image: 172.31.228.68/project/fortune:env
name: html-generator
env:
- name: NUM # 设置环境变量NUM
valueFrom: # valueFrom 替换 value
configMapKeyRef: # 使用 configMap
name: fortune-config # configMap 名称
key: sleep-interval # configMap 下的键sleep-interval的值 赋值给 NUM
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}

[root@k8s-master1 configmap]# kubectl logs fortune-env-t5nfq -c html-generator
设定的间隔是 25 秒

在 Pod 中引用不存在的 ConfigMap

1
2
3
1. k8s 正常调度尝试所有容器
2. 引用不存在的ConfigMap容器会失败,其余容器正常启动,直到创建缺失的ConfigMap
3. configMapKeyRef.optional: true 设置该项,即使ConfigMap不存在也不影响启动容器

使用 envFrom 一次性传入所有ConfigMap下的键值对

1
2
3
4
5
6
7
8
9
10
[root@k8s-master1 configmap]# vim my.conf 

apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
namespace: default
data:
name: Leo
age: "28"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@k8s-master1 configmap]# vim my-pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: busybox
image: busybox
command: [ "/bin/sh", "-c", "echo $(CONFIG_NAME) $(CONFIG_AGE)" ]
envFrom:
- prefix: CONFIG_
configMapRef:
name: my-config

restartPolicy: Never


# envFrom 代替 env
# configMapRef 代替 configMapKeyRef
# - prefix: CONFIG_ 所有的环境变量都增加了前缀 CONFIG_
# 引用名称为 my-config 的 configMap
# k8s 不会主动转换键名 环境变量中不要带 - 破折号,如果格式不正确该键值对会被忽略,也不发送通知
# 前缀设置是可选的,如果不添加前缀,就使用configmap中的键名

[root@k8s-master1 configmap]# kubectl logs mypod
Leo 28

使用 命令行参数 传给容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[root@k8s-master1 forloop]# vim fortune-pod.yaml 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:
- image: 172.31.228.68/project/fortune:args
name: html-generator
env:
- name: NUM
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
args: ["$(NUM)"]
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}


# 使用参数读取的镜像
# 引入环境变量env
# 在参数设置中引用环境变量

[root@k8s-master1 forloop]# kubectl logs fortune-zt854 -c html-generator
设定的间隔是 25 秒

使用 卷形式暴露 ConfigMap

1
2
1. 环境变量和命令行传参 作为配置值通常适用于变量值较短的场景
2. 如果想要暴露一个完整的配置文件 可以使用ConfigMap 卷的形式传入容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master1 configmap-file]# vim my-nginx-config.conf 

server {
listen 80;
server_name www.kubia-example.com;

gzip on;
gzip_types text/plain application/xml;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

}
1
2
[root@k8s-master1 configmap-file]# cat sleep-interval 
30
1
2
3
4
5
6
7
[root@k8s-master1 configmap-file]# cat redis.cfg
redis.hosts=127.0.0.1
redis.port=6379
redis.password=123456


# 之前叫做 redis.conf 的时候挂载configmap到nginx目录下 结果nginx读取该文件报错了
1
2
# 现在我有三段不同的配置文件 nginx 延迟值 和 redis配置 
# 我现在想把他们打到一个 configmap里

将整个文件夹下的所有配置文件 创建 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[root@k8s-master1 configmap-file]# kubectl create configmap fortune-config --from-file=/opt/demo/configmap-file/
configmap/fortune-config created

# 使用yaml文件查看
[root@k8s-master1 configmap-file]# kubectl get cm fortune-config -o yaml > fortune-config.yaml
[root@k8s-master1 configmap-file]# vim fortune-config.yaml

apiVersion: v1
data:
my-nginx-config.conf: |
server {
listen 80;
server_name www.kubia-example.com;

gzip on;
gzip_types text/plain application/xml;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

}
redis.cfg: |
redis.hosts=127.0.0.1
redis.port=6379
redis.password=123456
sleep-interval: |
30
kind: ConfigMap
metadata:
name: fortune-config
namespace: default


# 1. 三条数据 他们的键名都是各自的文件名
# 2. 所有条目数据的第一行最后的 “|” 管道符号 标识后续的健值是多行

Volume 卷形式挂载到 pod 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[root@k8s-master1 configmap-file]# vim fortune-pod-cm-volume.yaml 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:
- image: 172.31.228.68/project/fortune:env
name: html-generator
env:
- name: NUM
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
- name: config
configMap:
name: fortune-config
1
2
3
4
5
6
7
8
9
10
# 测试
[root@k8s-master1 configmap-file]# curl -H "Accept-Encoding: gzip" -I 10.0.0.82
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Sun, 22 Mar 2020 02:27:26 GMT
Content-Type: text/html
Last-Modified: Sun, 22 Mar 2020 02:26:56 GMT
Connection: keep-alive
ETag: W/"5e76ccf0-90"
Content-Encoding: gzip # 响应压缩
1
2
3
4
5
6
7
8
9
# 查看挂载的文件
[root@k8s-master1 configmap-file]# kubectl exec fortune-dncw4 -c web-server ls /etc/nginx/conf.d
my-nginx-config.conf
redis.cfg
sleep-interval

# configmap的三条数据都被作为文件挂载到指定目录下
# 采用多个configmap去配置同一个pod中的不同容器的配置 不太合理
# 同一个pod中的容器是紧密相联的,需要被当做一个整体单元来看

卷内暴露指定的 ConfigMap 数据

1
2
3
4
5
# 想让my-nginx-config.conf作为文件暴露在 /etc/nginx/conf.d下
# sleep-interval 作为 环境变量传入
# 如果将来 有两个镜像 要通过cm 挂载不同路径的配置文件
# 首先 两个地方都要配置 挂载点 都挂载同一个cm
# 通过items 去区分挂载的文件
1
2
3
4
5
6
7
8
9
volumes:
- name: html
emptyDir: {}
- name: config
configMap:
name: fortune-config
items:
- key: my-nginx-config.conf # 要暴露的文件
path: gzip.conf # 该文件的键值对会被赋予到这个文件中
1
2
[root@k8s-master1 configmap-file]# kubectl exec fortune-kbddc -c web-server ls /etc/nginx/conf.d
gzip.conf

注意点

1
2
3
4
1. 挂载某一个文件会隐藏该文件夹中已存在的文件
2. 如果我们之前镜像中 /etc/nginx/conf.d 目录下已经有了文件,通过卷挂载原本目录下的文件会被隐藏
3. Linux系统挂载文件系统至非空文件夹通常如此,想想nfs和mount挂载
4. 如果我们挂载到 /etc 目录就非常危险.会导致容器系统损坏,添加文件至某个文件夹 可千万不要到系统目录啊

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

1
2
3
1. 环境变量和命令行传参作为配置源的弊端在于无法修改pod中的配置
2. 将configmap 暴露为卷可以达到配置热更新的效果,无需重新创建Pod或者重启容器
3. configmap 更新后 卷中引用所有文件也会更新,进程发现文件更新后重新加载
1
2
3
[root@k8s-master1 configmap-file]# kubectl edit cm fortune-config
# 修改这行 关闭压缩
gzip off;
1
2
3
[root@k8s-master1 configmap-file]# kubectl exec fortune-kbddc -c web-server cat /etc/nginx/conf.d/gzip.conf
# 查看容器中文件已经被修改了
gzip off;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 手动通知 nginx 重启程序
[root@k8s-master1 configmap-file]# kubectl exec fortune-kbddc -c web-server -- nginx -s reload

[root@k8s-master1 configmap-file]# curl -H "Accept-Encoding: gzip" -I 10.0.0.82
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Sun, 22 Mar 2020 07:18:45 GMT
Content-Type: text/html
Content-Length: 660
Last-Modified: Sun, 22 Mar 2020 07:18:44 GMT
Connection: keep-alive
ETag: "5e771154-294"
Accept-Ranges: bytes

# 无需重启容器或者重建Pod,修改了配置

Secret 给容器传递敏感数据

1
2
3
4
5
1. Secret 可作为环境变量传值给容器
2. Secret 可作为卷 传文件给容器
3. ConfigMap 存储非敏感数据
4. Secret 存储敏感数据 类似 证书私钥等
5. 如果一个配置文件即有敏感,又有一般数据,该文件应该被存储在Secret中

默认令牌 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1. 默认被挂载只所有容器的 Secret 
# 查看任意 pod

[root@k8s-master1 configmap-file]# kubectl describe pod fortune-qcvv4

default-token-r29ch:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-r29ch
Optional: false

[root@k8s-master1 configmap-file]# kubectl get secret
NAME TYPE DATA AGE
default-token-r29ch kubernetes.io/service-account-token 3 12d

[root@k8s-master1 configmap-file]# kubectl describe secret
Name: default-token-r29ch
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 076d498a-c439-4148-b7c1-c727eeb441b6

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1359 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IllYeGFSNlE5RjhBR1UtX2tSanpmT0JJeUdZX05hb2pQck9pNWdmMUVvUVUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tcjI5Y2giLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjA3NmQ0OThhLWM0MzktNDE0OC1iN2MxLWM3MjdlZWI0NDFiNiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.OYVDkBJXBKmQs171S3_MdynbhrZjrGT0xOZ8T4h0DkwabOOxIhshyJlCwwD4zeR-wc3BfeWP8Pvg9YWvaY4vCqZC34khLv8GupLvW8qCGp21pbNPRL9E8EDKPuAk3YGs2lnFOsFaRIXKdJI3dqDyUzEVxKu2c5UZ0hul0wsJoabtZhyjSQUoenSX-am2vFNqYecfUxKMUye2HnRdQX_rtk71xZO_WGfjHMJhdlcPO0G4WMBwoNqzZ3fxi_zkakJvLah2Em2TpL1HmBjzJblOTGSsGHE0LiL9RgUIzyN4stEtGy2nDzwlALwIjAsBSSFbCLNavDf305st1E5SRCCnfg

# 三段数据
# ca.crt
# namespace
# token

# 他们是包含了从Pod内部安全访问K8s API服务器所需要的信息 挂载位置在
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-r29ch (ro)

[root@k8s-master1 configmap-file]# kubectl exec fortune-qcvv4 ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token

# 应用程序可以通过他们访问API服务器
# default-token-r29ch 被自动创建,且对应的卷被自动挂载到每个Pod上
# 默认令牌 Secret

创建 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 测试将https 的证书秘钥传送给Nginx容器
# 自制证书
# 自制证书
[root@k8s-master1 secret]# openssl genrsa -out https.key 2048
[root@k8s-master1 secret]# openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia.example.com
[root@k8s-master1 secret]# echo bar > foo

[root@k8s-master1 secret]# ls -l
total 12
-rw-r--r-- 1 root root 4 Mar 22 15:49 foo
-rw-r--r-- 1 root root 1127 Mar 22 15:49 https.cert
-rw-r--r-- 1 root root 1675 Mar 22 15:48 https.key

# 创建3个文件的 Secret
[root@k8s-master1 secret]# kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo
secret/fortune-https created

[root@k8s-master1 secret]# kubectl get secret fortune-https -o yaml
apiVersion: v1
data:
foo: YmFyCg==
https.cert: LS0tLS1CRUdJTiBDRVJUSUZJ...
https.key: LS0tLS1CRUdJTiBSU0E...
kind: Secret
metadata:
creationTimestamp: "2020-03-22T07:51:37Z"
name: fortune-https
namespace: default
resourceVersion: "210797"
selfLink: /api/v1/namespaces/default/secrets/fortune-https
uid: 0ef65bf7-45e1-4dd8-9286-5634eaee6b4d
type: Opaque

# Secret中的内容会被以 Base64格式编码
# ConfigMap 直接以纯文本显示

在 Pod 中读取 Secret

1
2
3
4
1. 通过secret卷 将Secret暴露给容器
2. Secret中的数据会被解码并且以真实形式(纯文本或者二进制)写入对应的文件
3. 通过环境变量暴露也是如此
4. 应用程序无需主动解码,直接读取文件内容或者使用环境变量

在 Pod 中使用 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 开启之前nginx配置的 https选项
[root@k8s-master1 secret]# kubectl get cm

[root@k8s-master1 secret]# kubectl edit cm fortune-config

...
apiVersion: v1
data:
my-nginx-config.conf: |
server {
listen 80;
listen 443 ssl;
server_name www.kubia-example.com;
ssl_certificate certs/https.cert;
ssl_certificate_key certs/https.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
gzip off;
gzip_types text/plain application/xml;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

}
...

# 证书的位置在 /etc/nginx/certs 下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 挂载 secret 到 pod里 
[root@k8s-master1 secret]# vim fortune-https-pod.yaml

metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:
- image: 172.31.228.68/project/fortune:env
name: html-generator
env:
- name: NUM
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:1.16
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: certs # nginx从/etc/nginx/certs/下读取证书和秘钥文件
mountPath: /etc/nginx/certs/ # 需要将 secret卷挂载到这个目录下
readOnly: true
ports:
- containerPort: 80
protocol: TCP
- containerPort: 443
volumes:
- name: html
emptyDir: {}
- name: config
configMap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: gzip.conf
- name: certs # 引用fortune-https Secret 来定义 secret 卷
secret:
secretName: fortune-https
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@k8s-master1 secret]# vim fortune-svc.yaml 

apiVersion: v1
kind: Service
metadata:
name: fortune
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30123
- name: https
port: 443
targetPort: 443
nodePort: 30443
selector:
app: fortune
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 测试
[root@k8s-master1 secret]# kubectl create -f fortune-https-pod.yaml
[root@k8s-master1 secret]# kubectl create -f fortune-svc.yaml
[root@k8s-master1 secret]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
fortune NodePort 10.0.0.167 <none> 80:30123/TCP,443:30443/TCP 93s
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 12d

[root@k8s-master1 secret]# curl -k https://10.0.0.167:443

[root@k8s-master1 secret]# curl -k -v https://10.0.0.167:443
* About to connect() to 10.0.0.167 port 443 (#0)
* Trying 10.0.0.167...
* Connected to 10.0.0.167 (10.0.0.167) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
* subject: CN=www.kubia.example.com # 匹配证书
* start date: Mar 22 07:49:14 2020 GMT
* expire date: Mar 20 07:49:14 2030 GMT
* common name: www.kubia.example.com
* issuer: CN=www.kubia.example.com
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.0.0.167
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Sun, 22 Mar 2020 08:32:47 GMT
< Content-Type: text/html
< Content-Length: 168
< Last-Modified: Sun, 22 Mar 2020 08:32:17 GMT
< Connection: keep-alive
< ETag: "5e772291-a8"
< Accept-Ranges: bytes



# https://www.kubia-example.com:30443/

Secret 卷 存储于内存

1
2
3
4
[root@k8s-master1 secret]# kubectl exec fortune-95lsq -c web-server -- mount | grep certs
tmpfs on /etc/nginx/certs type tmpfs (ro,relatime)

# 由于是 tmpfs ,存储在secret中的数据不会写入磁盘,无法被窃取

在私有镜像仓库中 使用 Secret

1
2
3
4
5
6
7
8
9
0. 镜像仓库 设置为 私有仓库,并且上传一个镜像
1. 定义一个 类型为 docker-registry 的 Secret ,需要指定 用户名 密码 邮箱
2. pod 定义中 引用 Secret 镜像仓库凭据,imagePullSecrets

[root@k8s-master1 secret]# kubectl create secret docker-registry my-harbor-secret --docker-username=admin --docker-password=lx@68328153 --docker-email=253911339@qq.com --docker-server=172.31.228.68

[root@k8s-master1 secret]# kubectl get secret
NAME TYPE DATA AGE
my-harbor-secret kubernetes.io/dockerconfigjson 1 17s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@k8s-master1 secret]# vim base.yaml 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: base
spec:
selector:
matchLabels:
app: base
template:
metadata:
labels:
app: base
spec:
imagePullSecrets: # 通过 my-harbor-secret 凭证从私有镜像中拉取镜像
- name: my-harbor-secret
containers:
- image: 172.31.228.68/game/base
name: html-generator
ports:
- containerPort: 80
protocol: TCP


[root@k8s-master1 secret]# kubectl get pods
NAME READY STATUS RESTARTS AGE
base-z5h5v 1/1 Running 0 12s

不需要为每个 Pod 指定镜像拉取 Secret

1
2
1. 如果系统中运行大量Pod,是否每个Pod都要配置镜像拉取Secret 
2. 后面可以学习通过添加Secret至ServiceAccount使所有Pod都能自动添加镜像拉取Secret