kubernetes实践(八)Kubernetes的Configmap和Secret
一.Configmap
1.简介
核心用途就是容器和配置的分离解耦。
应用场景:镜像往往是一个应用的基础,还有很多需要自定义的参数或配置,例如资源的消耗、日志的位置级别等等,这些配置可能会有很多,因此不能放入镜像中,Kubernetes中提供了Configmap来实现向容器中提供配置文件或环境变量来实现不同配置,从而实现了镜像配置与镜像本身解耦,使容器应用做到不依赖于环境配置。
如启用一个mysql容器,mysql容器重要的文件有两部分,一部分为存储数据文件,一部分为配置文件my.cnf,存储数据可以用持久存储实现和容器的分离解耦,配置文件也能够实现和容器的分离解耦,也就是说mysql容器能够直接读取并使用预先配置好的配置文件(而不是使用容器中默认自带的配置文件).这就是configMap的功能。
2.创建ConfigMap
创建ConfigMap的方式有4种:
- 通过直接在命令行中指定configmap参数创建,即
--from-literal
- 通过指定文件创建,即将一个配置文件创建为一个ConfigMap
--from-file=<文件>
- 通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,
--from-file=<目录>
- 事先写好标准的configmap的yaml文件,然后kubectl create -f 创建
2.1 通过命令行参数--from-literal
创建
示例
创建命令:
kubectl create configmap my-config1 --from-literal=db.port='3306'
详情信息:
kubectl get configmap my-config1 -o yaml
结果如data内容所示:
apiVersion: v1 data: db.port: "3306" kind: ConfigMap metadata: creationTimestamp: "2020-03-03T02:12:29Z" name: my-config1 namespace: default resourceVersion: "608193" selfLink: /api/v1/namespaces/default/configmaps/my-config1 uid: 6f1e515b-5cf4-11ea-b1e8-5254008afee6
2.2 指定文件创建
示例
配置文件app.properties的内容:
name=xiaoxiao age=18
创建命令(可以有多个--from-file
):
kubectl create configmap my-config2 --from-file=./app.properties
详情信息:
kubectl get configmap my-config2 -o yaml
结果如data内容所示:
apiVersion: v1 data: app.properties: | name=xiaoxiao age=18 kind: ConfigMap metadata: creationTimestamp: "2020-03-03T02:21:26Z" name: my-config2 namespace: default resourceVersion: "609204" selfLink: /api/v1/namespaces/default/configmaps/my-config2 uid: af389bb6-5cf5-11ea-b1e8-5254008afee6
可以看到指定文件创建时configmap会创建一个key/value对,key是文件名,value是文件内容。
假如不想configmap中的key为默认的文件名,还可以在创建时指定key名字:
kubectl create configmap my-config3 --from-file=<my-key-name>=<path-to-file>
2.3 指定目录创建
示例
configs 目录下的config1和config2内容如下所示:
config1
aaaaa bbbbb cc=cc dd=dd
config2
11111 22222 33=33 44=44
创建命令:
kubectl create configmap my-config4 --from-file=./configs
详情信息:
kubectl get configmap my-config4 -o yaml
结果如data内容所示:
apiVersion: v1 data: config1: | aaaaa bbbbb cc=cc dd=dd config2: | 11111 22222 33=33 44=44 kind: ConfigMap metadata: creationTimestamp: "2020-03-03T02:23:15Z" name: my-config4 namespace: default resourceVersion: "609412" selfLink: /api/v1/namespaces/default/configmaps/my-config4 uid: f00cadb7-5cf5-11ea-b1e8-5254008afee6
可以看到指定目录创建时configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。
2.4 通过yaml文件创建
configmaps.yaml文件如下:
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO
创建命令:
kubectl apply -f configmaps.yaml
详情信息:
kubectl get configmap special-config -o yaml kubectl get configmap env-config -o yaml
结果如data内容所示:
apiVersion: v1 data: special.how: very kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"special.how":"very"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"special-config","namespace":"default"}} creationTimestamp: "2020-03-03T02:27:42Z" name: special-config namespace: default resourceVersion: "609916" selfLink: /api/v1/namespaces/default/configmaps/special-config uid: 8f967787-5cf6-11ea-b1e8-5254008afee6
apiVersion: v1 data: log_level: INFO kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"log_level":"INFO"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"env-config","namespace":"default"}} creationTimestamp: "2020-03-03T02:27:42Z" name: env-config namespace: default resourceVersion: "609917" selfLink: /api/v1/namespaces/default/configmaps/env-config uid: 8f973a81-5cf6-11ea-b1e8-5254008afee6
3.使用ConfigMap
使用ConfigMap有三种方式:
- 第一种是通过环境变量的方式,直接传递给pod
- 使用configmap中指定的key
- 使用configmap中所有的key
- 第二种是通过在pod的命令行下运行的方式(启动命令中)
- 第三种是作为volume的方式挂载到pod内
注意:
- ConfigMap必须在Pod使用它之前创建
- 使用envFrom时,将会自动忽略无效的键
- Pod只能使用同一个命名空间的ConfigMap
3.1 通过环境变量使用
(1)使用valueFrom
、configMapKeyRef
、name
、key
指定要用的key
test-pod.yaml文件如下
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: LOG_LEVEL valueFrom: configMapKeyRef: name: env-config key: log_level restartPolicy: Never
创建pod
kubectl apply -f test-pod.yaml
查询pod
kubectl logs dapi-test-pod
(2)还可以通过envFrom
、configMapRef
、name
使得configmap中的所有key/value对都自动变成环境变量:
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] envFrom: - configMapRef: name: special-config restartPolicy: Never
3.2用作命令行参数
在命令行下引用时,需要先设置为环境变量,之后可以通过$(VAR_NAME)设置容器启动命令的启动参数:
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: SPECIAL_LEVEL - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: SPECIAL_TYPE restartPolicy: Never
3.3作为volume挂载使用
(1)将创建的ConfigMap直接挂载至Pod的/etc/config目录下,其中每一个key-value键值对都会生成一个文件,key为文件名,value为内容
pod-myconfigmap-v2.yaml文件
apiVersion: v1 kind: Pod metadata: name: pod-configmap2 spec: containers: - name: test-container image: busybox command: [ "/bin/sh", "-c", "ls /etc/config/" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config restartPolicy: Never
创建
kubectl apply -f pod-myconfigmap-v2.yaml
进入pod查看
kubectl exec -it pod-configmap2 bash
进入容器中/etc/config查看:
cd /etc/config ls special.how cat special.how very
可以看到,在/etc/config文件夹下以每一个key为文件名value为值创建了多个文件。
查询日志
kubectl logs pod-configmap2
结果
special.how
(2)假如不想以key名作为配置文件名可以引入items字段,在其中逐个指定要用相对路径path替换的key:
volumes: - name: config-volume configMap: name: special-config items: - key: special.how path: special_how
kubectl exec -it pod-configmap2 bash
进入容器中/etc/config查看:
cd /etc/config ls special_how cat special_how very
查询日志
kubectl logs pod-configmap2
结果
special_how
备注:
删除configmap后原pod不受影响;然后再删除pod后,重启的pod的events会报找不到cofigmap的volume;
pod起来后再通过kubectl edit configmap …修改configmap,过一会pod内部的配置也会刷新。
在容器内部修改挂进去的配置文件后,过一会内容会再次被刷新为原始configmap内容
3.4configmap的热更新研究
更新 ConfigMap 后:
使用该 ConfigMap 挂载的 Env 不会同步更新
使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新
ENV 是在容器启动的时候注入的,启动之后 kubernetes 就不会再改变环境变量的值,且同一个 namespace 中的 pod 的环境变量是不断累加的,参考 Kubernetes中的服务发现与docker容器间的环境变量传递源码探究。为了更新容器中使用 ConfigMap 挂载的配置,可以通过滚动更新 pod 的方式来强制重新挂载 ConfigMap,也可以在更新了 ConfigMap 后,先将副本数设置为 0,然后再扩容。
3.4ConfigMap在Ingress Controller中实战
在之前ingress网络中的mandatory.yaml文件中使用了ConfigMap,于是我们可以打开
可以发现有nginx-configuration、tcp-services等名称的cm 而且也可以发现最后在容器的参数中使用了这些cm
containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1 args: - /nginx-ingress-controller - --configmap=$(POD_NAMESPACE)/nginx-configuration - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --publish-service=$(POD_NAMESPACE)/ingress-nginx - --annotations-prefix=nginx.ingress.kubernetes.io
验证
#查找ingress的CONTAINER ID docker ps | grep ingress #进入容器 docker exec -it 86739e3e4240 bash #验证配置文件 ls /etc/nginx #查询配置文件内容 cat nginx.conf | grep cicd.demo.princeli.com server_name cicd.demo.princeli.com ;
从上面验证过程可以看出
nginx ingress controller就是一个nginx,而所谓的ingress.yaml文件中 配置的内容像cicd.demo.princeli.com就会对应到nginx.conf中。
修改配置
如果需要修改配置是否只需要修改对应的ConfigMap 文件就可以了
新建nginx-config.yaml文件
kind: ConfigMap apiVersion: v1 metadata: name: nginx-configuration namespace: ingress-nginx labels: app: ingress-nginx data: proxy-read-timeout: "208"
创建
kubectl apply -f nginx-config.yaml
验证
#查找ingress的CONTAINER ID docker ps | grep ingress #进入容器 docker exec -it 86739e3e4240 bash #验证配置文件 ls /etc/nginx #查询配置文件内容 cat nginx.conf |grep proxy_read_timeout proxy_read_timeout 208s;
其实configmap定义规则都在nginx ingress controller的官网中
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
二.Secret
Secret有三种类型:
- Opaque:使用base64编码存储信息,可以通过
base64 --decode
解码获得原始数据,因此安全性弱。 - kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息。
- kubernetes.io/service-account-token:用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。
1. Opaque Secret
Opaque类型的Secret的value为base64位编码后的值
1.1从文件创建
(1)创建username.txt,password.txt
echo -n "admin" > ./username.txt echo -n "1f2d1e2e67df" > ./password.txt
(2)创建secret
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
(3)查询
kubectl get secret db-user-pass
(4)结果
NAME TYPE DATA AGE db-user-pass Opaque 2 28s
1.2使用yaml创建
(1)对数据进行64位编码
echo -n 'admin' | base64 echo -n '1f2d1e2e67df' | base64
(2)定义mysecret.yaml文件
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
(3)根据yaml文件创建资源并查看
kubectl create -f ./secret.yaml kubectl get secret kubectl get secret mysecret -o yaml
1.3 Secret的使用
创建好Secret之后,可以通过两种方式使用:
以Volume方式
以环境变量方式
(1)将Secret挂载到Volume中
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret
进入Pod查看挂载的Secret:
kubectl exec -it mypod bash # ls /etc/foo password username # cat /etc/foo/username admin # cat /etc/foo/password 1f2d1e2e67df
也可以只挂载Secret中特定的key:apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username
在这种情况下:
- username 存储在/etc/foo/my-group/my-username中
- password未被挂载
(2)将Secret设置为环境变量
apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: mycontainer image: redis env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password restartPolicy: Never
2.kubernetes.io/dockerconfigjson
kubernetes.io/dockerconfigjson用于存储docker registry的认证信息,可以直接使用kubectl create secret命令创建:
kubectl create secret docker-registry myregistrykey \ -n namespace \ --docker-server=DOCKER_REGISTRY_SERVER \ --docker-username=DOCKER_USER \ --docker-password=DOCKER_PASSWORD \ --docker-email=DOCKER_EMAIL \
例如
kubectl create secret docker-registry aliyun-key \ -n demo \ --docker-server=registry.cn-hangzhou.aliyuncs.com \ --d[email protected] \ --docker-password=xxxxxx \ [email protected]
可在yaml文件中使用拉取image
... spec: imagePullSecrets: - name: aliyun-key ...
3. kubernetes.io/service-account-token
用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。
# 可以看到service-account-token kubectl get secret kubectl run nginx --image nginx kubectl get pods kubectl exec -it nginx-pod-name bash ls /run/secrets/kubernetes.io/serviceaccount ca.crt namespace token kubectl get pods pod-name -o yaml # 找到volumes选项,定位到-name,secretName volumes: - name: default-token-8jfch secret: defaultMode: 420 secretName: default-token-8jfch # 找到volumeMounts选项,定位到mountPath volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-8jfch readOnly: true
小结:无论是ConfigMap,Secret,还是DownwardAPI,都是通过ProjectedVolume实现的,可以通过APIServer将 信息放到Pod中进行使用。