配置管理
配置管理
引入
程序的配置管理问题
传统架构中配置文件都写在程序内部,如果依赖的中间件如MySQL配置变更了,则程序也需要重新修改配置文件并编译打包部署,非常的麻烦!

云原生之配置分离
云原生架构下,提倡把配置从程序中分离出来并放到统一的配置管理中心进行管理!例如SpringCloud的配置中心,携程开源的Apollo配置中心
如果没有统一的配置管理中心,可以使用k8s提供的ConfigMap和Secret来实现配置分离,ConfigMap 和 Secret可以挂载到Pod上去供程序使用

以Nginx为例,在传统架构中部署了一个Nginx集群,如果需要修改配置则要到每一个Nginx节点上修改配置。在k8s中pod重启或更新会变会部署到其他节点,即容器所在的节点是不固定的。如果要修改容器配置还要重新生成镜像并部署,非常的麻烦。所以在k8s中不能使用传统架构模式的配置与程序一体!

什么是ConfigMap
ConfigMap也是K8s的一种资源,主要用于存储配置数据,如程序的环境变量、配置文件等。ConfigMap可以实现把应用程序的配置信息从容器镜像或者代码中分离出来,从而可以更容易的管理和更新配置,而不必重新构建应用程序。
主要用途:
- 存储配置信息:剥离应用程序配置,实现配置分离
- 动态配置:动态更改程序的配置,无需重新部署整个应用
- 共享配置:数据共享,多个Pod可以共享ConfigMap数据
什么是Secret
和ConfigMap类似,Secret也是K8s的一种资源,同样可以存储配置数据。和ConfigMap不同的是Secret提供了一种 相对安全 的方式来管理这些数据。同时相对于ConfigMap,Secret也有不同的类型,不同的类型具有不同的使用场景。比如Docker Registry Secret可以用来管理镜像仓库的用户名密码。
Secret中的配置文件全部使用base64加密过后的内容,Secret加密数据库密码,在外面看是加密的,进入容器内部查看就是解密的了(安全性有限,相对安全)
主要用途:
- 存储敏感信息:可以存储密码、Key等信息
- 数据保护:Secret的数据可以被加密存储,防止未经授权的访问
- 动态配置:和ConfigMap类似
- 共享配置:和ConfigMap类似
注意:
ConfigMap&Secret 也具有命名空间隔离性ConfigMapzzzzzzzs
ConfigMap-配置管理
创建ConfigMap
基于 yaml 创建
configMap配置
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# 属性形式的keys,每一个key映射到一个简单的值
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# 文件形式的keys
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=truepod中使用configMap
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# 定义环境变量
- name: PLAYER_INITIAL_LIVES
valueFrom:
configMapKeyRef:
# 该值来自的ConfigMap
name: game-demo
# 获取的键
key: player_initial_lives
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# 在Pod级别设置卷,然后将它们挂载到Pod内的容器中
- name: config
configMap:
# 提供要挂载的ConfigMap的名称
name: game-demo
# 要作为文件创建的ConfigMap中的键数对
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"基于文件创建
# 基于单个文件
kubectl create cm nginx-conf --from-file=nginx.conf
# 基于文件创建并更改 key 名
kubectl create cm nginx-conf2 --from-file=nginxconf=nginx.conf
# 基于多个文件创建
kubectl create cm multile-file --from-file=redis.conf --from-file=my.cnf
# 基于多个文件创建并修改 key 名
kubectl create cm multile-file2 --from-file=redisconf=redis.conf --fromfile=mysqlconf=my.cnf基于文件夹创建
如果某个文件夹内有多个配置文件,可以基于文件夹创建
$ ls conf/
my.cnf nginx.conf redis.conf
$ kubectl create cm dirconfig --from-file=conf/
configmap/dirconfig created
$ kubectl get cm dirconfig
NAME DATA AGE
dirconfig 3 64s基于环境变量创建
创建 ConfigMap 时,可以通过--from-env-file 创建环境变量类型的 ConfigMap。创建环境变量类型的 ConfigMap,只需要在文件内写入 KEY=VALUE 格式的内容即可创建
$ cat env-file
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASS=password
$ kubectl create cm env-cm --from-env-file=env-file
configmap/env-cm created
$ kubectl get cm env-cm
NAME DATA AGE
env-cm 4 8m38s
$ kubectl get cm env-cm -oyaml
apiVersion: v1
data:
DB_HOST: localhost
DB_PASS: password
DB_PORT: "3306"
DB_USER: root基于 literal 创建
Literal 一般用于创建环境变量形式的 ConfigMap
$ kubectl create configmap example-config --from-literal=key1=config1 --from-literal=key2=config2
configmap/example-config created
$ kubectl describe cm example-config
Name: example-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
key1:
----
config1
key2:
----
config2更新 ConfigMap
基于 Yaml 文件修改
$ cat basic-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: basic-config
data:
key1: xxxx1
key2: xxxx2
$ kubectl replace -f basic-cm.yaml
configmap/basic-config replaced
$ kubectl get -f basic-cm.yaml -oyaml
apiVersion: v1
data:
key1: xxxx1
key2: xxxx2直接通过 edit 修改
直接通过 kubectl edit 编辑 ConfigMap
通过源文件修改
首先修改本地文件
$ cat nginx.conf
user monap;
# 先转成 yaml 再进行 replace
$ kubectl create cm nginx-conf --from-file=nginx.conf=nginx.conf --dry-run=client - oyaml | kubectl replace -f -
configmap/nginx-conf replaced基于 literal 创建的方法类似
kubectl create configmap example-config --from-literal=key1=config1 --fromliteral=key2=config2 --dry-run=client -oyaml挂载 ConfigMap
以文件形式挂载
在 Volume 中添加 ConfigMap,即可在 Pod 中的多个容器中挂载
# cat nginx-test-cm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test-cm
labels:
app: nginx-test-cm
spec:
replicas: 1
selector:
matchLabels:
app: nginx-test-cm
template:
metadata:
labels:
app: nginx-test-cm
spec:
volumes:
- name: conf
configMap:
name: multile-file
containers:
- name: nginx-test-cm
image: registry.cn-beijing.aliyuncs.com/monap/nginx:1.15.12
volumeMounts:
- name: conf
mountPath: /mnt查看挂载详情,默认情况下会以 ConfigMap 中的 Key 名作为挂载的文件名
$ kubectl exec -ti nginx-test-cm-dcd5c47b6-x5f4q -- bash
root@nginx-test-cm-dcd5c47b6-x5f4q:/# ls /mnt/
my.cnf redis.conf指定挂载的文件名
挂载 ConfigMap 时,可以指定挂载的文件名字
volumes:
- name: conf
configMap:
name: multile-file
items:
- key: my.cnf
path: mysql.conf此时挂载的文件名会被更改为 mysql.conf,同时只会挂载 items 配置的 key
$ kubectl exec -ti nginx-test-cm-65bbc96865-kh8l9 -- bash
root@nginx-test-cm-65bbc96865-kh8l9:/#
root@nginx-test-cm-65bbc96865-kh8l9:/# ls /mnt/
mysql.conf默认情况下,如果指定的 Key 名不存在,Pod 会一直处于 ContainerCreating
volumes:
- configMap:
defaultMode: 420
items:
- key: my.cnfxxx
path: mysql.conf
name: multile-file
name: conf
$ kubectl get po -l app=nginx-test-cm
NAME READY STATUS RESTARTS AGE
nginx-test-cm-594489b5b6-kzl6d 1/1 Running 0 4m58s
nginx-test-cm-7b5565d77f-hdx8x 0/1 ContainerCreating 0 44s此时 describe 查看详情可以看到如下报错
MountVolume.SetUp failed for volume "conf" : configmap references non-existent config key: my.cnfxxx可以添加 optional 字段,把 key 必须存在改为非必须
volumes:
- name: conf
configMap:
name: multile-file
items:
- key: my.cnfxxx
path: mysql.conf
optional: true自定义挂载权限
ConfigMap 和 Secret 挂载时,默认的挂载权限是 644(八进制,和 Linux 的进制一致,十进制为 420)
# 在容器中执行
$ pwd
/mnt
$ ls ..data/mysql.conf -l
-rw-r--r-- 1 root root 305 Sep 8 08:06 ..data/mysql.conf可以在挂载时,指定挂载权限
volumes:
- name: conf
configMap:
name: multile-file
items:
- key: redis.conf
path: redis.conf
- key: my.cnf
path: mysql.conf
mode: 0777 # 局部权限,对单个 item 配置权限,八进制
defaultMode: 420 # 默认权限,对所有的 item 生效,十进制再次查看权限
$ cd /mnt/..data
$ ls -l
total 8
-rwxrwxrwx 1 root root 305 Sep 8 08:22 mysql.conf
-rw-r--r-- 1 root root 44 Sep 8 08:22 redis.conf文件挂载覆盖的问题
ConfigMap 和 Secret 挂载时,会直接覆盖原目录的内容,所以在挂载时需要注意。 比如需要挂载 nginx.conf 时,如果直接挂载至/etc/nginx 目录,会导致 nginx 无法启动
$ kubectl get po -l app=nginx-test-cm
NAME READY STATUS RESTARTS AGE
nginx-test-cm-74c5dcb76d-spbvx 1/1 Running 0 18m
nginx-test-cm-79c5d69f66-qcj2g 0/1 Error 3 (36s ago) 51s
$ kubectl logs -f nginx-test-cm-79c5d69f66-qcj2g
[emerg] 1#1: open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)此时/etc/nginx 下只有 nginx.conf,解决挂载覆盖
volumeMounts:
- mountPath: /etc/nginx/nginx.conf # 指定挂载具体的文件路径
name: nginxconf
subPath: nginx.conf # 指定挂载具体的文件以环境变量形式挂载
挂载指定的环境变量
env:
- name: DB_HOST # 变量名
valueFrom:
configMapKeyRef:
name: env-cm # ConfigMap 名称
key: DB_HOST # Key 名
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: env-cm
key: DB_PORT查看生成的环境变量
$ kubectl exec -ti nginx-test-cm-7b9c4fdb45-2bt6t -- bash
root@nginx-test-cm-7b9c4fdb45-2bt6t:/# env | grep DB
DB_PORT=3306
DB_HOST=localhost注意:Key 不存在同样会导致 Pod 一直处于 CreateContainerConfigError 状态。 Describe 可以看到如下报错:Error: couldn't find key DB_HOST2 in ConfigMap。
批量生成环境变量
containers:
# env:
# - name: DB_HOST
# valueFrom:
# configMapKeyRef:
# key: DB_HOST
# name: env-cm
# - name: DB_PORT
# valueFrom:
# configMapKeyRef:
# key: DB_PORT
# name: env-cm
# 批量生成环境变量配置(注意如果有相同key,env指定的优先级更高)
envFrom:
- configMapRef:
name: env-cm
prefix: CM_ # 变量前缀,可选Secret-敏感数据管理
Secret 常用类型
Opaque:通用型 Secret,默认类型kubernetes.io/dockerconfigjson:下载私有仓库镜像使用的 Secret,和宿主机的 /root/.docker/config.json 一致,宿主机登录后即可产生该文件kubernetes.io/tls:用于存储 HTTPS 域名证书文件的 Secret,可以被 Ingress 使用bootstrap.kubernetes.io/token:一种简单的 bearer token,用于创建新集群或将新 节点添加到现有集群,在集群安装时可用于自动颁发集群的证书kubernetes.io/basic-auth:用于使用基本认证(账号密码)的 Secret,可以使用 Opaque 取代kubernetes.io/ssh-auth:用于存储 ssh 密钥的 Secret,可以使用 Opaque 取代kubernetes.io/service-account-token:作用于 ServiceAccount,包含一个令牌,用于标识 API 服务账户
注意:我们一般常用的就只有前三种
创建Secret
使用 literal 创建
可以使用 literal 创建,创建后会自动加密
kubectl create secret generic basic-auth-secret \
--from-literal=username=admin \
--from-literal=password=securepassword查看创建的 Secret 资源
$ kubectl get secret
NAME TYPE DATA AGE
basic-auth-secret Opaque 2 93s
$ kubectl get secret basic-auth-secret -oyaml
apiVersion: v1
data:
password: c2VjdXJlcGFzc3dvcmQ= # 加密后的数据
username: YWRtaW4= # 加密后的数据
kind: Secret
metadata:
name: basic-auth-secret
namespace: default
type: Opaque解密数据
echo "c2VjdXJlcGFzc3dvcmQ=" | base64 -d基于 Yaml 文件创建
基于 yaml 文件形式创建,有两种方式,第一种直接写在 data 下,需要对数据进行先加密
$ echo "dar3adada" | base64
ZGFyM2FkYWRhCg==
$ cat basic-secret.yaml
apiVersion: v1
data:
ssh-key: ZGFyM2FkYWRhCg==
kind: Secret
metadata:
name: secret2
namespace: default
type: Opaque
$ kubectl create -f basic-secret.yaml
secret/secret2 created第二种是写在 stringData 下,无需进行先加密
$ cat stringDataSecret.yaml
apiVersion:v1
stringData:
ssh-key: password
kind: Secret
metadata:
name: string-data
namespace: default
type: Opaque
$ kubectl create -f stringDataSecret.yaml
secret/string-data created创建后自动加密
$ kubectl get secret string-data -oyaml
apiVersion: v1
data:
ssh-key: cGFzc3dvcmQ=基于文件和目录创建
基于文件和目录创建和 ConfigMap 类似
kubectl create secret generic nginxconf --from-file=conf/nginx.conf创建后文件内容会被加密
$ kubectl get secret nginxconf -oyaml
apiVersion:v1
data:
nginx.conf: dXNlciBuZ2lueD……基于目录创建
kubectl create secret generic createbydir --from-file=conf/Secret 管理镜像仓库密钥
如果需要拉取私有仓库的镜像,需要给 deploy 等资源配置镜像仓库的密钥,此时可以先创建镜像仓库的密钥
docker-registry:指定 Secret 的类型myregistrykey: Secret 名称DOCKER_REGISTRY_SERVER:镜像仓库地址DOCKER_USER:镜像仓库用户名,需要有拉取镜像的权限DOCKER_PASSWORD:镜像仓库密码DOCKER_EMAIL:邮箱信息,可以为空
kubectl create secret docker-registry myregistrykey \
--docker-server=DOCKER_REGISTRY_SERVER \
--docker-username=DOCKER_USER \
--docker-password=DOCKER_PASSWORD \
--docker-email=DOCKER_EMAIL之后在 Pod 字段下添加该 Secret 即可
spec:
imagePullSecrets:
- name: myregistrykey
containers:Secret 管理域名证书
Secret 可以使用 kubernetes.io/tls 类型管理域名的证书,然后用于 Ingress
# 生成测试证书
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=test.com"
# 创建 Secret
kubectl create secret tls nginx-test-tls \
--key=tls.key \
--cert=tls.crt之后即可在 Ingress 中使用
tls:
- secretName: nginx-test-tls挂载 Secret
挂载 Secret 和 ConfigMap 类似,也可以以文件和配置变量挂载。
以文件形式挂载
使用 Volume 挂载 Secret,和 ConfigMap 类似,主要注意选择 Secret 的字段由 name 变成了 secretName,volumeMounts 配置相同
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test-cm
namespace: default
labels:
app: nginx-test-cm
spec:
replicas: 1
selector:
matchLabels:
app: nginx-test-cm
template:
metadata:
labels:
app: nginx-test-cm
spec:
volumes:
- name: nginxconfsecret
secret:
secretName: nginxconf
containers:
- name: nginx-test-cm
image: registry.cn-beijing.aliyuncs.com/monap/nginx:1.15.12
volumeMounts:
- name: nginxconfsecret
mountPath: /mnt挂载后,文件内容会被解密
$ kubectl exec -ti nginx-test-cm-6886795f7-g2l5c -- bash
root@nginx-test-cm-6886795f7-g2l5c:/# cat /mnt/nginx.conf
user nginx;
worker_processes auto;以环境变量形式挂载
Secret 以环境变量形式挂载和 ConfigMap 类似,只是一些字段上的差异
# 批量方式
envFrom:
- prefix: SECRET_
secretRef:
name: basic-auth-secret
# 单个方式
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: basic-auth-secret
key: username使用 Secret 拉取私有仓库的镜像
假设有个镜像在私有仓库中,未使用账号密码是无法拉取镜像的,此时可以在部署资源中, 添加 docker-registry 类型的 Secret。
假设创建一个 deployment,镜像所在的位置是私有仓库,如果未配置密钥,则会有如下报错:
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
namespace: default
labels:
app: test
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: test
image: 192.168.181.200/monap/java:v1报错信息
Failed to pull image "192.168.181.200/monap/java:v1": failed to pull and unpack image "192.168.181.200/monap/java:v1": failed to resolve reference
"192.168.181.200/monap/java:v1": pull access denied, repository does not exist or may require authorization: authorization failed: no basic auth credentials Warning Failed 23s (x3 over 66s) kubelet Error: ErrImagePull添加 Secret
kubectl create secret docker-registry myregistrykey \
--docker-server=192.168.181.200 \
--docker-username=admin \
--docker-password=Dukuan12345 \
--docker-email=xx@qq.com更改 Deployment 添加 imagePullSecrets,更新后即可下载镜像
spec:
containers:
- name: test
image: 192.168.181.200/monap/java:v1
imagePullSecrets:
- name: myregistrykeyConfigMap 和 Secret 只读配置
ConfigMap 和 Secret 可以使用 immutable 字段把资源设置为只读模式,这样就不能再更新 data 下的任何数据,immutable也不能再改为 false 了。但是标签和注解还是可以变更的。
apiVersion: v1
kind: ConfigMap
metadata:
name: env-cm
data:
DB_HOST: localhost
DB_PASS: password
DB_PORT: "3306"
DB_USER: root
immutable: true配置为 true 后,再次修改数据会报如下错误
# configmaps "env-cm" was not valid:
# * data: Forbidden: field is immutable when `immutable` is SetConfigMap 和 Secret 备份还原
ConfigMap 和 Secret 目前并不支持备份和还原,不过可以使用 git 仓库管理 ConfigMap 和 Secret 的版本。
ConfigMap 和 Secret 注意事项
- 需提前创建 ConfigMap 和 Secret
- ConfigMap 和 Secret 必须要和 Pod 或者是引用它资源在同一个命名空间
- 如果引用了某个 key,需要确保引用的 Key 必须存在
- envFrom、valueFrom 无法热更新
环境变量,subPath也是无法热更新的 - envFrom 配置环境变量,如果 key 是无效的,它会忽略掉无效的 key
- ConfigMap 和 Secret 最好不要太大
- ConfigMap 和 Secret 支持热更新,但是程序未必支持