Kubernetes: 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/

yaml 文件中多行字符串

参考:

yaml 文件中多行字符串可以使用|保留换行符,或者使用>将换行符替换为空格。其中,这两个换行符有以下几种用法:

  • |:文中自动换行,默认仅保留一行空行
  • |+:文中自动换行,保留字符串后面所有的空行
  • |-:文中自动换行,删除字符串后面所有的空行
  • >:文中不自动换行,默认仅保留一行空行
  • >+:文中不自动换行,保留字符串后面所有的空行
  • >-:文中不自动换行,删除字符串后面所有的空行

多行字符串可以使用引号括起来:" "会进行特殊字符转义,' '保留原始字符串。

测试

import yaml

with open('string.yaml','r', encoding='utf-8') as f:
    data = yaml.load(f, Loader=yaml.FullLoader)

for key in data:
    print(f'{key}:{data[key]}')
string_1: |
  I am a string_1.
  11111.
string_2: |+
  I am a string_2.
  22222.
string_3: |-
  I am a string_3.
  33333.
string_4: >
  I am a string_4.
  44444.
string_5: >+
  I am a string_5.
  55555.
string_6: >-
  I am a string_6.
  66666.
[root@localhost ~]# python3 read_yaml.py
string_1:I am a string_1.
11111.

string_2:I am a string_2.
22222.

string_3:I am a string_3.
33333.
string_4:I am a string_4. 44444.

string_5:I am a string_5. 55555.

string_6:I am a string_6. 66666.
[root@localhost ~]#

范例

spec:
  containers:                                                                                                                                                                                                                 
  - name: mq-nameserver-1
    image: gehuiwin/rocketmq:4.9.2
    imagePullPolicy: IfNotPresent
    env:
    - name: JAVA_OPT_EXT
      value: |-
        -XX:+PerfDisableSharedMem
        -Xloggc:/home/rocketmq/logs/rocketmq_gc.log
        -XX:+PrintGCDetails 
        -XX:+PrintGCDateStamps 
        -XX:+PrintGCTimeStamps
        -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=4 -XX:GCLogFileSize=128M
        -Duser.timezone=Asia/Calcutta -Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn512m

创建 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

示例

挂载文件同时挂载nfs

配置configmap

apiVersion: v1
data:
  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/
  APP_NAMESPACE: default
  TZ: Asia/ShangHai
  postStart.sh: |-
    #!/bin/bash
    #set -ex

    service_name=$1
    [ -z ${service_name} ] && { echo "未定义service_name...退出" ; exit; }

    #service_port=""
    #if [ ${service_name}  = "x11" ]; then
    #  service_port='1028'
    #elif [ ${service_name}  = "x12" ]; then
    #  service_port='1034'
    #else
    #  service_port=""
    #fi
    #[ -z ${service_port} ] && { echo "未定义service_port...退出" ; exit; }

    declare -A name_port
    name_port=()
    name_port+=([x11]='1028') 
    name_port+=([x12]='1029') 
    service_port=${name_port[${service_name}]}

    APP_NAME="ROOT.jar" 
    CAPTURE_DIR="/tmp/debug/${HOSTNAME}-`hostname -i`/arthas_captures"

    REPORT_FILE="$CAPTURE_DIR/report.txt"

    monitor_health() {
      while : 
      do
          command -v curl &> /dev/null || { echo "没有curl命令,退出循环" ; break; }
          val=$(curl -o /dev/null -s  -w "%{http_code}" http://127.0.0.1:${service_port}/actuator/health/liveness)
          echo "val-> ${val}"
          if [[ $val != "200" ]];then
              # 创建目录
              mkdir -p $CAPTURE_DIR
              echo "-----" > $REPORT_FILE
              echo "----------http://127.0.0.1:${service_port}/actuator/health/liveness 非200,开始分析: $(TZ='CST-8' date +%Y%m%d_%H%M%S.%N)" |tee  $REPORT_FILE
              take_thread_dump |tee -a $REPORT_FILE
          fi
          sleep 5
      done
    }

    take_thread_dump() {
        # 获取PID
        PID=$(jps -l | grep "$APP_NAME" | awk '{print $1}')

        if [ -z "$PID" ]; then
            echo "错误: 找不到 $APP_NAME 的进程"
            echo "当前Java进程:"
            jps -l
            exit 1
        fi

        echo "=== 应用信息 ==="
        echo "进程名称: $APP_NAME"
        echo "进程PID: $PID"
        echo "监听端口: $PORT"

        # 生成唯一时间戳(纳秒级)
        TIMESTAMP=$(TZ='CST-8' date +%Y%m%d_%H%M%S.%N)
        echo "=== 开始抓取现场数据: $TIMESTAMP ==="

        # 1. 抓取完整线程栈(最关键的)
        echo "1. 抓取jstack线程栈..."
        jstack -l $PID > $CAPTURE_DIR/${TIMESTAMP}_jstack_full.log 2>&1

        # 2. 重点抓取XNIO/Undertow线程(根据你的线程名)
        echo "2. 抓取XNIO相关线程..."
        jstack -l $PID | grep -B 5 -A 25 "XNIO" > $CAPTURE_DIR/${TIMESTAMP}_jstack_xnio.log 2>&1

        # 3. 抓取GC信息
        echo "3. 抓取GC信息..."
        jstat -gcutil $PID 1000 3 > $CAPTURE_DIR/${TIMESTAMP}_jstat_gc.log 2>&1

        # 4. 抓取堆内存信息
        echo "4. 抓取堆内存信息..."
        jmap -heap $PID > $CAPTURE_DIR/${TIMESTAMP}_jmap_heap.log 2>&1

        # 5. 网络连接信息
        echo "5. 抓取网络连接..."
        netstat -ant | grep ":$PORT" > $CAPTURE_DIR/${TIMESTAMP}_netstat.log 2>&1
        netstat -ant | grep ESTABLISHED | wc -l > $CAPTURE_DIR/${TIMESTAMP}_established_count.log 2>&1

        # 6. 系统负载
        echo "6. 抓取系统信息..."
        uptime > $CAPTURE_DIR/${TIMESTAMP}_system.log 2>&1
        free -m >> $CAPTURE_DIR/${TIMESTAMP}_system.log 2>&1

        # 7. 尝试通过Arthas抓取(如果已连接)
        echo "7. 尝试通过Arthas抓取..."
        if command -v arthas &> /dev/null; then
            # 这里需要根据你的Arthas连接方式来调整
            echo "注意: Arthas命令需要根据实际情况调整" > $CAPTURE_DIR/${TIMESTAMP}_arthas_note.txt
        else
            echo "Arthas未安装或不在PATH中" > $CAPTURE_DIR/${TIMESTAMP}_arthas_note.txt
        fi

        # 8. 生成摘要报告
        echo "8. 生成摘要报告..."
        {
            echo "=== 现场抓取摘要 ==="
            echo "时间: $(TZ='CST-8' date)"
            echo "PID: $PID"
            echo "应用: $APP_NAME"
            echo ""
            echo "=== 线程状态统计 ==="
            grep "java.lang.Thread.State:" $CAPTURE_DIR/${TIMESTAMP}_jstack_full.log | sort | uniq -c
            echo ""
            echo "=== 活跃连接数 ==="
            cat $CAPTURE_DIR/${TIMESTAMP}_established_count.log
            echo ""
            echo "=== 抓取的文件 ==="
            ls -la $CAPTURE_DIR/${TIMESTAMP}_*
        } > $CAPTURE_DIR/${TIMESTAMP}_summary.txt

        echo ""
        echo "=== 抓取完成 ==="
        echo "数据已保存到 $CAPTURE_DIR/"
        echo ""
        echo "=== 关键信息摘要 ==="
        tail -20 $CAPTURE_DIR/${TIMESTAMP}_summary.txt

        # 提供分析建议
        echo ""
        echo "=== 分析建议 ==="
        echo "1. 查看慢线程: grep -A 30 'XNIO-2 task' $CAPTURE_DIR/${TIMESTAMP}_jstack_full.log"
        echo "2. 查看线程状态: grep 'java.lang.Thread.State:' $CAPTURE_DIR/${TIMESTAMP}_jstack_full.log | sort | uniq -c"
        echo "3. 查看GC情况: cat $CAPTURE_DIR/${TIMESTAMP}_jstat_gc.log"
        echo "4. 查看连接数: cat $CAPTURE_DIR/${TIMESTAMP}_established_count.log"
    }

    wtime=$2
    [ -z $wtime ] && wtime=300 || :
    echo "启动监听,预处理等待 $wtime s"
    sleep $wtime
    monitor_health

kind: ConfigMap
metadata:
  name: app-config
  namespace: default

测试POD

apiVersion: v1
kind: Pod
metadata:
  name: app1
  namespace: default
spec:
  containers:
  - name: app1
    image: nginx
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]

    lifecycle:
      postStart:  #  只有 postStart 处理函数执行完毕,容器的状态才会变成 RUNNING。 不能是死循环脚本
        exec:
          command:
          - /bin/bash
          - -c 
          - '/opt/postStart.sh'
    volumeMounts:
    - mountPath: "/tmp/debug"
      name: "volume-0"
      readOnly: false
    - mountPath: /opt/postStart.sh
      name: app-config
      subPath: postStart.sh
  volumes:
  - name: "volume-0"
    nfs:
      path: "/mnt/nfs/debug"
      readOnly: false
      server: "10.2.0.1"
  - configMap:
      defaultMode: 420
      items:
      - key: postStart.sh
        path: postStart.sh
        mode: 0755 
      name: app-config
    name: app-config

deployment测试

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: x11
  name: x11
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: x11
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: x11
    spec:
      containers:
        - command:
            - sh
            - '-c'
            - /opt/bin/postStart.sh x11 & /start.sh
          env:
            - name: LANG
              value: C.UTF-8
            - name: JAVA_HOME
              value: /usr/local/openjdk-8
            - name: JAVA_BASE_URL
              value: >-
                https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u222-b10/OpenJDK8U-jre_
            - name: TZ
              value: Asia/Shanghai
            - name: JAVA_OPTS
              value: '-Xmx1g -Xms1g'
            - name: aliyun_logs_x11-stdout
              value: stdout
          image: >-
            x.cr.xx.com/x/x11:273
          imagePullPolicy: Always
          lifecycle:
            preStop:
              exec:
                command:
                  - sh
                  - '-c'
                  - >-
                    curl -X POST http://127.0.0.1:1028/actuator/eureka-registry
                    &&  sleep 90
          livenessProbe:
            failureThreshold: 8
            httpGet:
              path: /actuator/health/liveness
              port: 1028
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 3
          name: x11
          readinessProbe:
            failureThreshold: 5
            httpGet:
              path: /actuator/health/readiness
              port: 1028
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 3
          resources:
            limits:
              cpu: '1'
              memory: 2Gi
            requests:
              cpu: 100m
              memory: 1Gi
          volumeMounts:
            - mountPath: /tmp/debug
              name: volume-0
            - mountPath: /opt/bin/
              name: app-config
      dnsPolicy: ClusterFirst
      imagePullSecrets:
        - name: x1-docker-hub
        - name: x2-docker-hub
      restartPolicy: Always
      terminationGracePeriodSeconds: 100
      volumes:
        - name: volume-0
          nfs:
            path: /mnt/nfs/debug
            server: 10.2.0.1
        - configMap:
            defaultMode: 420
            items:
              - key: postStart.sh
                mode: 493
                path: postStart.sh
            name: app-config
          name: app-config

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 文件

基于明文方式创建

官方文档:https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-config-file/#specify-unencoded-data-when-creating-a-secret

创建完后会自动加密

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: <user>
    password: <password>

其它详情请查看官方文档。

使用 Secret

挂载和生成环境变量与 ConfigMap 使用方式一样,这里不重启赘述了。

使用Secret拉取私有仓库镜像

创建镜像 Secret

#kubectl create secret docker-registry docker-pull --docker-username=k8s-pull --docker-password=QpJUfm9LBv --docker-server=https://pg-ops-harbor.xxx.com -n default
---
apiVersion: v1
data:
  .dockerconfigjson: eyIjoiY0NVRFSjIifX19
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内容
emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike