0%

k8s 中强大的配置中心 -- ConfigMap

关于 Kubernetes 系列其他文章的传送门

  1. 《 部署 Kubernetes 也能如此简单吗? 》

  2. 《 初识 k8s,新时代的宠儿 》

  3. 《 深度学习 Pod 》

  4. 《 学会这 5 种 Pod 控制器,搞定发布、更新、回滚,从此告别人肉运维!! 》

  5. 《 k8s 中的网络入口 – Service 资源管理 》

  6. 《 k8s 中的 7 层调度 – Ingress 》

  7. 《 k8s 中的存储系统 – Volumes 》

  8. 《 k8s 中强大的配置中心 – ConfigMap 》     您当前所在位置

  9. 《 k8s 中的部署神器 – Statefulset 控制器 》

  10. 《 k8s 中的安全守护神 – 你不知道不代表它不在 》

  11. 《 k8s 中的图形接口 – Dashboard 》

  12. 《 k8s 中的网络插件 – flannel 和 Calico 》

  13. 《 k8s 中的调度器 – 亲和性、污点和容忍性 》

  14. 《 k8s 中对资源的监控 – 资源指标、Prometheus、弹性伸缩器 HPA 》

  15. 《 k8s 中的云原生应用管理利器 – Helm 》

  16. 《 理解 k8s 高可用,让你的集群稳如泰山 》



logo

一、什么是 ConfigMap 以及它的作用

ConfigMap 的作用是把配置文件提升为标准的 k8s 资源,之后可以被 Pod 所引用,加载为容器内程序的配置文件,这类似于一个配置中心。

Pod 加载引用 ConfigMap 资源的方式有两种:

  • 通过环境变量:某些容器支持通过在启动时传递一个环境变量来生成配置文件中的某些参数。对于这种容器,在创建 Pod 时可以通过引用 ConfigMap 资源的方式来获取值。但是这种方式只能在容器启动时加载一次,之后如果配置修改了,需要容器里的进程重载或重启才能加载;
  • 通过存储卷:让 Pod 加载 ConfigMap 类型的存储卷,并挂载到容器的指定的目录或文件上;
    • 注意:Pod 和要引用的 CM 必须都在同一名称空间下

ConfigMap 资源的创建方式有两种:

  1. 使用 kubectl create configmap 命令直接在命令行创建:

    • from-literal=[]:直接在命令行给出;
    • from-file=[]:从文件加载。
  2. 通过配置清单配置,示例如下:

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
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
cni-conf.json: | # “|”这个符号代表接下来有大段的配置文字,并且配置参数也要遵循 yaml 格式
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}

通过环境变量引用 ConfigMap 的示例

先创建 ConfigMap 资源

创建一个 cm,并在命令行直接指定键值:

1
2
3
4
~]# kubectl create ns config

~]# kubectl create configmap filebeat-cfg -n config --from-literal=redis_host="redis.default.svc.cluster.local" --from-literal=log_level="Info"
configmap/filebeat-cfg created

查看刚才创建的 cm,可以看到 data 字段有我们定义的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
~]# kubectl get cm -n config -o yaml
apiVersion: v1
items:
- apiVersion: v1
data:
log_level: Info
redis_host: redis.default.svc.cluster.local
kind: ConfigMap
metadata:
creationTimestamp: "2019-11-13T05:20:13Z"
name: filebeat-cfg
namespace: config
resourceVersion: "390355"
selfLink: /api/v1/namespaces/config/configmaps/filebeat-cfg
uid: 5fd2f0db-c24d-4e27-907b-7f55c632d407
kind: List
metadata:
resourceVersion: ""
selfLink: ""

引用 ConfigMap

这里使用一个 filebeat 镜像作为示例,这个镜像启动时可传入两个环境变量 REDIS_HOSTLOG_LEVEL 来配置容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~]# vim pod-cfg.yaml

apiVersion: v1
kind: Pod
metadata:
name: pod-cfg-demo
namespace: config
spec:
containers:
- name: filebeat
image: ikubernetes/filebeat:5.6.5-alpine
env:
- name: REDIS_HOST # 向容器的 REDIS_HOST 环境变量传递一个值
valueFrom: #
configMapKeyRef: # 这个值来自一个 ConfigMap 资源
name: filebeat-cfg # 资源的名字为 filebeat.cfg
key: redis_host # 使用这个资源内一个叫 redis_host 的键对应的值
- name: LOG_LEVEL # 定义向环境变量 LOG_LEVEL 传递的的第二个值
valueFrom: #
configMapKeyRef: #
name: filebeat-cfg #
key: log_level # 使用 log_level 的键对应的值

先创建 Pod:

1
2
~]# kubectl apply -f pod-cfg.yaml
pod/pod-cfg-demo created

之后进入容器内查看环境变量是否加载成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
~]# kubectl exec -it pod-cfg-demo -n config -- /bin/sh

/ # printenv
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
LOG_LEVEL=Info # 配置已经成功
HOSTNAME=pod-cfg-demo
SHLVL=1
HOME=/root
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.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
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
PWD=/
KUBERNETES_SERVICE_HOST=10.96.0.1
REDIS_HOST=redis.default.svc.cluster.local # 配置已经成功
FILEBEAT_VERSION=5.6.5

这种方式的配置参数只能在容器启动时加载一次,之后如果配置修改了,需要手动将容器里的进程重载或重启才能加载;

通过存储卷引用 ConfigMap

1. 创建 ConfigMap 资源

创建 2 个 nginx 的示例配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
~]# vim server1.conf
server {
server_name www.monster.com;
listen 80;
location / {
root "/html/monster";
}
}

~]# vim server2.conf
server {
server_name www.ilinux.com;
listen 80;
location / {
root "/html/ilinux";
}
}

把上面两个配置文件创建为 CM 资源:

1
2
3
                                                              # ↓ 当直接指明使用哪个文件时,以文件名为键,文件的内容为值
~]# kubectl create configmap nginx-cfg -n config --from-file=./server1.conf --from-file=server-ilinux.conf=./server2.conf
# ↑ 也可以自定义键的名字,然后指定以哪个文件的内容为值

查看刚刚创建的 CM 资源:

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
~]# kubectl get cm nginx-cfg -n config -o yaml

apiVersion: v1
data:
server-ilinux.conf: |
server {
server_name www.ilinux.com;
Listen 80;
Location / {
root "/html/ilinux";
}
}
server1.conf: |
server {
server_name www.monster.com;
Listen 80;
Location / {
root "/html/monster";
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2019-11-13T08:26:05Z"
name: nginx-cfg
namespace: config
resourceVersion: "404230"
selfLink: /api/v1/namespaces/config/configmaps/nginx-cfg
uid: 9b386a13-c753-4997-ad99-14cff55d233a

2. 引用 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-> # vim nginx-pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: config
spec:
containers:
- name: nginx
image: nginx:1.14-alpine
volumeMounts:
- name: config # 指定把下方的名为 config 的存储卷
mountPath: /etc/nginx/conf.d/ # 挂载到容器的什么地方,注意默认的 nginx 镜像内此目录有默认配置文件,会被覆盖
volumes:
- name: config # 定义存储卷的别名
configMap: # 使用 CM 类型的存储卷
name: nginx-cfg # 指明使用哪个 CM,名字是创建 CM 时定义的
items:
- key: server1.conf # 使用上面指定的 CM 资源中,哪个键的值作为内容
path: server-monster.com # 挂载时可以把这个键映射为别的名字
- key: server-ilinux.conf # 指定第二个键
path: server-ilinux.conf # 不改名

配置变更

如果需要对配置进行变更,修改 CM 资源即可;

使用下面的命令进入一个 vi 界面修改:

1
~]# kubectl edit cm nginx-cfg -n config

listen 80; 改为 listen 8080;;保存退出后,会被 Pod 中的容器自动加载到,并重载程序,这通常需要一点时间,一般在一分钟左右。如果无法自动重载程序,可以使用比如 nginx -s reload 的命令来手动完成。

不过要注意,所有引用了这个资源的 Pod,都会被更改。

二、什么是 Secret 以及它的作用

Secret 是一个将密码等敏感信息加密保存的资源。

Secret 的使用逻辑,和 ConfigMap 没什么区别,唯一不同的地方在于 Secret 的数据是被 base64 编码以后存放的。

Secret 有三种类型:

  • TLS:专用于打包 SSl 中的证书和私钥,一般为 X509 格式的;而且不管之前证书和私钥的文件叫什么名字,都会被统一保存为 tls.crttls.key
  • generic:保存非 TLS 类型的普通类型的敏感信息,比如 MySQL 的管理员密码;该类型创建后会显示为 Opaque 类型,因为 k8s 将内容使用 base64 编码加密存储,不过可以通过 base64 --decode 解码获得原始数据,因此安全性弱。
  • docker-registry:使被调度的目标节点上的 kubelet 进程,在加载私有仓库中的镜像时,能认证到私有仓库服务器上。

generic 的使用示例

创建一个 MySQL 的 root 密码的 Secret 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~]# kubectl create secret generic mysql-root-password -n config --from-literal=password=monster

~]# kubectl get secret mysql-root-password -n config -o yaml
apiVersion: v1
data:
password: bW9uc3Rlcg==
kind: Secret
metadata:
creationTimestamp: "2019-11-14T08:44:10Z"
name: mysql-root-password
namespace: config
resourceVersion: "416363"
selfLink: /api/v1/namespaces/config/secrets/mysql-root-password
uid: fb06f569-3a73-4dfd-a4b5-7d3ee03a7e3f
type: Opaque

创建 MySQL 的 Pod,并引用 Secret。

MySQL 的镜像初始化时必须传递一个变量 MYSQL_ROOT_PASSWORD 来配置数据库密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: mysql-secret
namespace: config
spec:
containers:
- name: mysql
image: mysql:5.6
env:
- name: MYSQL_ROOT_PASSWORD # 向容器的 MYSQL_ROOT_PASSWORD 环境变量传递一个值
valueFrom:
secretKeyRef: # 这个值来自一个 secretKeyRef 资源
name: mysql-root-password # 资源的名字为 filebeat.cfg
key: password # 使用这个资源内一个叫 password 的键对应的值

-> # kubectl apply -f mysql-secret.yaml
pod/mysql-secret created

创建后连入 Pod 的交互式接口,使用刚才定义的 MySQL 密码 monster 测试能否登录:

1
2
~]# kubectl exec -it -n config mysql-secret -- /bin/sh
# mysql -pmonster

查看环境变量应该也可以看到相关的配置:

1
~]# printenv

TLS 的使用示例

创建证书

1
2
3
~]# openssl genrsa -out myapp.key 2048

~]# openssl req -new -x509 -key myapp.key -out myapp.crt -subj /C=CN/ST=Beijing/L=Beijing/O=Ops/CN=tls.com -days 365

导入证书到 k8s:

1
~]# kubectl create secret tls tls-cert --cert=myapp.crt --key=myapp.key

在 Ingress 中引用 TLS 的方法

创建一个新的 Ingress Controller,使用刚刚创建的证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~]# vim myapp-tls-ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapp-tls
namespace: config
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- tls.com
secretName: tls-cert
rules:
- host: tls.monster.com
http:
paths:
- path: /
backend:
serviceName: myapp
servicePort: 80

在 Pod 中使用存储卷引用 TLS 证书的方法

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
apiVersion: v1
kind: Pod
metadata:
name: config
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d/
- name: tls
mountPath: /etc/nginx/certs/ # 把证书挂载到这个目录
volumes:
- name: config # 这里引用的是 CM 资源中的 nginx 的配置文件
configMap:
name: nginx-cfg
items:
- key: server1.conf
path: server-monster.com
- key: server-ilinux.conf
path: server-ilinux.conf
- name: tls # 这里引用的是刚才创建的 secret 类型的证书
secret:
secretName: mysql-cert
items:
- key: tls.crt
path: myapp.crt
- key: tls.key
path: myapp.key
mode: 0600