Drollery Medieval drollery of a knight on a horse

🏆 欢迎来到本站: https://xuchangwei.com/希望这里有你感兴趣的内容

flowery border with man falling
flowery border with man falling

Kubernetes: k8s 基础篇-配置管理

k8s-基础篇-配置管理

ConfigMap

云原生要素-配置分离

  • Java Out of Code
    • SpringCloud ConfigServer
    • Apollo
    • ConfigMap&Secret

什么是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` 变量比较少时使用

  • 范例 env 文件创建 ConfigMap

    `–from-env-file` 创建

    $ kubectl  create cm envcm --from-env-file=conf/game1.conf
    
    $ kubectl  get cm envcm -oyaml
    apiVersion: v1
    data:
      lives: "3"
      secret.code: "true"
    kind: ConfigMap
    metadata:
      name: envcm
      namespace: default
    
通过 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 中变量名不能包含点`.`的

  • 给每个环境变量名称加前缀

    `deploy.spec.template.spec.containers[].envFrom[].prefix`

    $ cat dp-cm.yaml
    ...
        spec:
          containers:
          - env:
            - name: SERVICE_NAME
              value: "ludo-user"
            envFrom:
            - configMapRef:
                name: app-config
              prefix: "fromCm_"
            image: nginx
            name: nginx
    
以文件的形式挂载 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:邮箱信息,可以为空
  • 证书
    kubectl create secret tls tomcat-ingress-secret --cert=tls.crt  --key=tls.key
    
yaml 文件

使用 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/

https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#auto-generated-legacy-serviceaccount-token-clean-up

从 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内容