Kubernetes: k8s 基础篇-配置管理
- TAGS: Kubernetes
k8s-基础篇-配置管理
ConfigMap
云原生要素-配置分离
- Java Out of Code
- SpringCloud ConfigServer
- Apollo
- ConfigMap&Secret
- SpringCloud ConfigServer
什么是configmap
一般用 ConfigMap 去管理一些配置文件,或者一些大量的环境变量信息。
ConfigMap 将配置和 Pod分开,有一个nginx, nginx.conf-> configmap, nginx 去读取configmap的信息。更易于配置文件的自动更新和管理。
Secret: Secret更倾向于存储和共享敏感、加密的配置信息。
配置文档:https://kubernetes.io/docs/concepts/configuration/configmap/
创建 ConfigMap 的几种形式
基于目录或文件创建 ConfigMap
`–from-file`可指定单个文件和目录创建,指定目录会创建包含该目录中所有文件的configmap:
# 指定目录 kubectl create configmap *** --from-file=/path # 指定文件或多个文件 kubectl create configmap *** --from-file=file1 #其中,--from-file可以使用多次,比如: kubectl create configmap *** --from-file=file1 --from-file=file2 # 自定义 key 名称, 默认为文件名 kubectl create configmap *** --from-file=keyname=file1
- 范例基于目录创建 ConfigMap
准备文件
$ mkdir conf $ cat conf/game1.conf lives=3 secret.code=true $ cat conf/game2.conf color.good=purple user=tomcat
基于目录创建 ConfigMap
$ kubectl create cm cmfromdir --from-file=conf/ $ kubectl get cm cmfromdir -oyaml apiVersion: v1 data: game1.conf: | lives=3 secret.code=true game2.conf: | color.good=purple user=tomcat kind: ConfigMap metadata: name: cmfromdir namespace: default
创建自定义文件名称的 ConfigMap
$ kubectl create cm cmspecfile --from-file=my=conf/game2.conf $ kubectl get cm cmspecfile -oyaml apiVersion: v1 data: my: | color.good=purple user=tomcat kind: ConfigMap metadata: name: cmspecfile namespace: default
基于key-value字符串的环境变量创建 ConfigMap
常用于 Pod 的环境变量
kubectl create configmap *** --from-literal=config1=123 --from-literal=PASSWORD=234 kubectl create cm *** --from-env-file=conf/game1.conf
`–from-literal` 变量比较少时使用
通过 yaml / json文件创建(推荐)
这种是我比较推荐的方式,创建configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: test-conf
namespace: test
data:
test-conf: |+
SESSION_LIFETIME: 3600
URL: "http://test-server:8080"
注意:查看 ConfigMap 格式是乱码,一般为 yaml 文件内容换行前有空格,去掉即可。
使用 ConfigMap
准备 deployment 文件
kubectl create deploy dp-cm --image=nginx --dry-run=client -oyaml > dp-cm.yaml $ cat dp-cm.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: dp-cm name: dp-cm spec: replicas: 1 selector: matchLabels: app: dp-cm template: metadata: labels: app: dp-cm spec: containers: - image: nginx name: nginx
准备 ConfigMap 文件
$ cat app-config.yaml apiVersion: v1 data: TZ: Asia/Calcutta APP_NAMESPACE: ludo-prd APP_ENV: prod APP_JVM_CONFIG: |- -Dfile.encoding=utf-8 -server -XX:+UseG1GC -XX:+ExitOnOutOfMemoryError -XX:InitialRAMPercentage=75.0 -XX:MinRAMPercentage=75.0 -XX:MaxRAMPercentage=75.0 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs/ PreStop.sh: |- #! /bin/bash sleep 5 curl --connect-timeout 5 --max-time 5 -s -i "http://localhost:8080/admin/maintain" sleep 40 curl --connect-timeout 5 --max-time 5 -s -i -H "Content-Type: application/json" -X POST http://localhost:8099/actuator/shutdown sleep 60 kind: ConfigMap metadata: name: app-config
使用 valueFrom 定义环境变量
$ cat dp-cm.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: dp-cm name: dp-cm spec: replicas: 1 selector: matchLabels: app: dp-cm template: metadata: labels: app: dp-cm spec: containers: - env: # 定义环境变量 - name: TZ # 请注意这里可以和 ConfigMap 中的键名不一样的 valueFrom: configMapKeyRef: name: app-config # 这个值来自 ConfigMap key: TZ # 需要取值的键 - name: KAFKA_TOPIC_NAME valueFrom: configMapKeyRef: name: app-config key: APP_NAMESPACE - name: SERVICE_NAME value: "ludo-user" image: nginx name: nginx
查看 Pod 环境变量
$ kubectl apply -f app-config.yaml $ kubectl apply -f dp-cm.yaml $ kubectl exec -it dp-cm-f86b8cdf-glfnt -- env |grep -iE "TZ|KAFKA" KAFKA_TOPIC_NAME=ludo-prd TZ=Asia/Calcutta
自带的变量:
# 根据 kubecet get po -ojson 查看自带的变量 - name: NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: POD_NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - name: POD_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.name - name: POD_IP valueFrom: fieldRef: apiVersion: v1 fieldPath: status.podIP
使用 envFrom 批量生产环境变量
envFrom 将所有 ConfigMap 的数据定义为容器环境变量
$ cat dp-cm.yaml ... spec: containers: - env: - name: SERVICE_NAME value: "ludo-user" envFrom: - configMapRef: name: app-config image: nginx name: nginx
注意:在 linux 中变量名不能包含点`.`的
以文件的形式挂载 ConfigMap
$ cat dp-cm.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: dp-cm name: dp-cm spec: replicas: 1 selector: matchLabels: app: dp-cm template: metadata: labels: app: dp-cm spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /opt/PreStop.sh name: app-config # volumes 中对应的名称 subPath: PreStop.sh # 取 key 对应的 value 值,文件权限511 - mountPath: /opt/a/PreStop.sh name: test subPath: PreStop.sh.bak - mountPath: /opt/conf.d/ name: test # 挂载目录,目录本身文件因挂载而消失,可自动更新文件 volumes: - configMap: defaultMode: 0777 name: app-config name: app-config - configMap: defaultMode: 0644 items: # 只使用 ConfigMap 部分的 key - key: PreStop.sh # 指定 ConfigMap 中的 key path: PreStop.sh # 指定挂载的名称 - key: PreStop.sh path: PreStop.sh.bak mode: 0755 # 指定文件权限,优先级高 name: app-config name: test
查看
$ kubectl exec -it dp-cm-bc6f948f-qd8jr -- bash root@dp-cm-bc6f948f-qd8jr:/# ls /opt/ PreStop.sh a conf.d root@dp-cm-bc6f948f-qd8jr:/# ls /opt/a/ PreStop.sh root@dp-cm-bc6f948f-qd8jr:/# ls /opt/conf.d/ PreStop.sh PreStop.sh.bak
注意:
- 默认挂载目录,目录本身的文件因挂载而消失,同时修改 ConfigMap 可自动更新 Pod 中文件内容
- subPath 无法动态更新文件内容。
自定义挂载权限及名称
名称用法见上文
权限与 linux 中权限一致,推荐写 8 进制方式
- configMap: defaultMode: 420 # 即 8 进制的 0644 items: # 只使用 ConfigMap 部分的 key - key: PreStop.sh path: PreStop.sh.bak mode: 0755 # 指定文件权限,优先级高
defaultMode 是可选的:默认情况下,模式位用于为已创建的文件设置权限。 必须是 0000 到 0777 之间的八进制值或 0 到 511 之间的十进制值。 YAML 既接受八进制值也接受十进制值,JSON 针对模式位需要十进制值。此字段默认为 0644。
8进制与10进制互转换
root@dp-cm-5f766576cb-kjmrv:/opt/conf.d/..data# ls -l total 8 -rw-r--r-- 1 root root 251 Sep 6 17:18 PreStop.sh -rwxr-xr-x 1 root root 251 Sep 6 17:18 PreStop.sh.bak printf '%d\n' 0644 # 8 转 10 420 # printf '%o\n' 511 # 10 转 8 777
Secret
什么是secret
文档:https://kubernetes.io/docs/concepts/configuration/secret/
用于存储和管理一些敏感数据,比如密码,token,密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了。
Secret 常用类型
- Opaque:通用型Secret,默认类型;
- kubernetes.io/service-account-token:作用于ServiceAccount,包含一个令牌,用于标识API服务账户;
- kubernetes.io/dockerconfigjson:下载私有仓库镜像使用的Secret,和宿主机的/root/.docker/config.json一致,宿主机登录后即可产生该文件;
- kubernetes.io/basic-auth:用于使用基本认证(账号密码)的Secret,可以使用Opaque取代;
- kubernetes.io/ssh-auth:用于存储ssh密钥的Secret
- kubernetes.io/tls:用于存储HTTPS域名证书文件的Secret,可以被Ingress使用;
- bootstrap.kubernetes.io/token:一种简单的 bearer token,用于创建新集群或将新节点添加到现有集群,在集群安装时可用于自动颁发集群的证书。
创建 Secret 的几种形式
- 使用 kubectl 命令来创建 Secret
- 基于配置文件来创建 Secret
- 使用 kustomize 来创建 Secret
使用 kubectl 命令创建 Secret
命令行创建时可指定的类型
- docker-registry Create a secret for use with a Docker registry
- generic Create a secret from a local file, directory or literal value
- tls Create a TLS secret
- 基于文件的方式创建
类似于 ConfigMap 创建
echo -n 'admin' > ./username.txt echo -n '1f2d1e2e67df' > ./password.txt kubectl create secret generic db-user-pass \ --from-file=./username.txt \ --from-file=./password.txt # 默认密钥名称是文件名。 你可以选择使用 --from-file=[key=]source 来设置密钥名称。
- 基于 key-value 标签提供 Secret 数据
kubectl create secret generic db-user-pass \ --from-literal=username=devuser \ --from-literal=password='S!B\*d$zDsb=' #注意,包含特殊字符(例如:$,\,*,= 和 !)由你的 shell 解释执行,而且需要转义。推荐使用单引号,而不使用双引号 ### 解码 $ kubectl get secrets db-user-pass -oyaml apiVersion: v1 data: password: UyFCXCpkJHpEc2I9 username: ZGV2dXNlcg== # echo 'UyFCXCpkJHpEc2I9' | openssl base64 -d S!B\*d$zDsb= # kubectl get secret db-user-pass -o jsonpath='{.data.password}' |base64 -d S!B\*d$zDsb=
- 镜像拉取凭证
kubectl create secret docker-registry myregistrykey \ --docker-server=DOCKER_REGISTRY_SERVER \ --docker-username=DOCKER_USER \ --docker-password=DOCKER_PASSWORD \ --docker-email=DOCKER_EMAIL
- docker-registry:指定Secret的类型
- myregistrykey: Secret名称
- DOCKER_REGISTRY_SERVER:镜像仓库地址
- DOCKER_USER:镜像仓库用户名,需要有拉取镜像的权限
- DOCKER_PASSWORD:镜像仓库密码
- DOCKER_EMAIL:邮箱信息,可以为空
- docker-registry:指定Secret的类型
- 证书
kubectl create secret tls tomcat-ingress-secret --cert=tls.crt --key=tls.key
使用 Secret
挂载和生成环境变量与 ConfigMap 使用方式一样,这里不重启赘述了。
使用Secret拉取私有仓库镜像
创建镜像 Secret
#kubectl create secret docker-registry docker-pull --docker-username=pfgc-k8s-pull --docker-password=QpJUfm9LBv --docker-server=https://pg-ops-harbor.xxx.com -n default --- apiVersion: v1 data: .dockerconfigjson: eyIjoiY0dabll5MXJPSE10Y0hWc2JEcFJjRXBWWm0wNVRFSjIifX19 kind: Secret metadata: name: docker-pull namespace: default type: kubernetes.io/dockerconfigjson
挂载镜像凭证
- `pod.spec.imagePullSecrets[].name` pod 中指定镜像凭证,可多个,依次请求
- 基于用户访问认证
Secret管理HTTPS证书
做成自签证书
openssl genrsa -out tls.key 2048 openssl req -new -x509 -days 365 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=test.com
创建证书 Secrets
#kubectl -n default create secret tls nginx-test-tls --key=tls.key --cert=tls.crt --- apiVersion: v1 data: tls.crt: *** tls.key: *** kind: Secret metadata: name: nginx-test-tls namespace: default type: kubernetes.io/tls
使用 ingress
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: "nginx" name: example spec: ingressClassName: nginx tls: - hosts: - nginx.test.com # SSL 证书对应的域名 (必填)。 secretName: nginx-test-tls rules: - host: "nginx.test.com" http: paths: - backend: service: name: nginx port: number: 80 path: / pathType: ImplementationSpecific
ConfigMap&Secret 热更新
Kubernetes中提供configmap,用来管理应用的配置,configmap具备热更新的能力,但只有通过目录挂载的configmap才具备热更新能力,其余通过环境变量,通过subPath挂载的文件都不能动态更新。
使用volume的方式挂载configmap之后,当configmap更新之后,变量的值会发生变化
但是中间会存在一定的时间间隔,大约是10左右,这主要是因为kubelet 对pod的同步间隔是10秒,另外需要注意的是当使用subpath将configmap中的某个文件单独挂载到目录下,那这个文件是无法热更新的,这是configmap本身的逻辑决定的。
热更新ConfigMap 和 Secert
# 基于文件的形式 kubectl create cm nginx-conf --from-file=nginx.conf --dry-run -oyaml | kubectl replace -f -
手动为 ServiceAccount 创建长期有效的 API 令牌
https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
从 1.29 版本开始,如果传统 ServiceAccount 令牌在一定时间段(默认设置为一年)内未被使用,则会被标记为无效。
说明:Kubernetes 在 v1.22 版本之前自动创建用来访问 Kubernetes API 的长
期凭据。 这一较老的机制是基于创建令牌 Secret 对象来实现的,Secret 对象
可被挂载到运行中的 Pod 内。 在最近的版本中,包括 Kubernetes v1.31,API
凭据可以直接使用 TokenRequest API 来获得,并使用一个投射卷挂载到 Pod
中。使用此方法获得的令牌具有受限的生命期长度,并且能够在挂载它们的 Pod
被删除时自动被废弃。
你仍然可以通过手动方式来创建服务账号令牌 Secret 对象,例如你需要一个永
远不过期的令牌时。 不过,使用 TokenRequest 子资源来获得访问 API 的令牌
的做法仍然是推荐的方式
kubectl create sa build-robot #生成临时token kubectl create token build-robot #--duration 参数来请求特定的令牌有效期 #手动为 ServiceAccount 创建长期有效的 API 令牌 kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: build-robot annotations: kubernetes.io/service-account.name: build-robot type: kubernetes.io/service-account-token EOF kubectl get secret build-robot -o jsonpath={".data.token"} | base64 -d
老方法
kubectl apply -f - <<EOF apiVersion: v1 kind: ServiceAccount metadata: name: cluster-readonly namespace: kube-system secrets: - name: cluster-readonly EOF kubectl -n kube-system get secret cluster-readonly -o jsonpath={".data.token"} | base64 -d
ConfigMap&Secret使用限制
- 提前场景ConfigMap和Secret
- 引用Key必须存在
- envFrom、valueFrom无法热更新环境变量
- envFrom配置环境变量,如果key是无效的,它会忽略掉无效的key
- ConfigMap和Secret必须要和Pod或者是引用它资源在同一个命名空间
- subPath也是无法热更新的
- ConfigMap和Secret最好不要太大
k8s1.19的不可变Secret和ConfigMap
比如秒杀系统中,设置了某个值是100,只能100个人秒杀成功,这个值不可变
kubectl create cm test-immutable --from-file=/etc/kubernetes/admin.kubeconfig # 最后加上 kubectl edit cm test-immutable immutable: true # 再去修改 kubectl edit cm test-immutable, 发现无法修改
说明:
一旦一个 Secret 或 ConfigMap 被标记为不可更改,撤销此操作或者更改 data 字段的内容都是 不 可能的。 只能删除并重新创建这个 Secret。现有的 Pod 将维持对已删除 Secret 的挂载点 – 建议重新创建这些 Pod。
解决 configmap 乱码
制表符TAB、尾随空格
Kubernetes ConfigMap在load过程中发现文件中包含tab缩进,直接转化为\n\t。
# 使用以下方法删除尾随空格,空格替换制表符 sed -i -E 's/[[:space:]]+$//g' file.txt sed -i 's/\t/ /g' file.txt
kubectl get cm my-cm -o json | jq '.data."nginx.conf"' -r # 将my-cm这个configmap资源以json格式化输出,格式化范围是/data/nginx.conf内容