12、Kubernetes学习-Kubernetes ConfigMap&Secret详解

一、ConfigMap

官方文档 ConfigMap

概念

ConfigMap的功能在k8s1.2版本中引入的,许多应用程序会从配置文件,命令行参数或环境变量中读取配置信息。ConfigMap API会给我们提供了向容器中注入配置信息的机制,ConigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制的对象

1. 为什么需要configmap

我们经常都需要为我们的应用程序配置一些特殊的数据,比如密钥、Token 、数据库连接地址或者其他私密的信息。你的应用可能会使用一些特定的配置文件进行配置,比如settings.py文件,或者我们可以在应用的业务逻辑中读取环境变量或者某些标志来处理配置信息。我们要做到这个,有好多种方案,比如:

  • 我们可以直接在打包镜像的时候写在应用配置文件里面,但是这种方式的坏处显而易见而且非常明显。
  • 我们可以在配置文件里面通过 env 环境变量传入,但是这样的话我们要修改 env 就必须去修改 yaml 文件,而且需要重启所有的 container 才行。

通过env设置不同的环境,使用不同的yaml配置文件

  • 我们可以在应用启动的时候去数据库或者某个特定的地方拿,没问题!但是第一,实现起来麻烦;第二,如果配置的地方变了怎么办?

当然还有别的方案,但是各种方案都有各自的问题。

而且,还有一个问题就是,如果说我的一个配置,是要多个应用一起使用的,以上除了第三种方案,都没办法进行配置的共享,就是说我如果要改配置的话,那得一个一个手动改。假如我们有 100 个应用,就得改 100 份配置,以此类推……

kubernetes 对这个问题提供了一个很好的解决方案,就是用 ConfigMap 和 Secret。

有很多种方案可以将项目 和 项目的配置解耦:

 

configMap就是让项目镜像 和 配置、环境变量、配置文件等配置数据 解耦,保证镜像的可移植性

  • ConfigMap功能在kubernetes1.2版本中引入,许多应用程序会从配置文件,命令行参数或环境变量中读取配置信息,ConfigAPI给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。
  • ConfigMap对像是一系列配置的集合,k8s会将这一集合注入到对应的Pod对像中,并为容器成功启动使用。注入的方式一般有两种,一种是挂载存储卷,一种是传递变量。ConfigMap被引用之前必须存在,属于名称空间级别,不能跨名称空间使用,内容明文显示。ConfigMap内容修改后,对应的pod必须重启或者重新加载配置(支持热更新的应用,不需要重启)。
  • Secret类似于ConfigMap,是用Base64加密,密文显示,一般存放敏感数据。一般有两种创建方式,一种是使用kubectl create创建,一种是用Secret配置文件。

2. 应用场景

应用场景:镜像往往是一个应用的基础,还有很多需要自定义的参数或配置,例如资源的消耗、日志的位置级别等等,这些配置可能会有很多,因此不能放入镜像中,Kubernetes中提供了Configmap来实现向容器中提供配置文件或环境变量来实现不同配置,从而实现了镜像配置与镜像本身解耦,使容器应用做到不依赖于环境配置。

向容器传递参数:

Docker Kubernetes 描述
ENTRYPOINT command 容器中的可执行文件
CMD args 需要传递给可执行文件的参数

Kubernetes如果需要向容器传递参数,可以在Yaml文件中通过command和args或者环境变量的方式实现。(不使用configMap的场景)

示例:demo-mpconft.yaml

apiVersion: v1
kind: Pod
metadata:
  name: print-mapenv
spec:
  containers:
  - name: env-print-demo
    image: centos:7.9.2009
    imagePullPolicy: IfNotPresent
    env: #添加了一个环境变量
    - name: GREETING
      value: "hello"
    - name: HONORIFIC
      value: "welcome"
    - name: NAME
      value: "Kubernetes is a good product"
    command: 
    - "echo" #执行echo命令
    args: 
    - "$(GREETING) $(HONORIFIC) $(NAME)"  #命令中传入了这些参数,引用的就是上面env中定义的

等待一会儿时间后,看到STATUS是Completed,Ready是0/1,pod中的容器已经结束了 因为我们yaml文件里就要求容器运行以后只执行一条指令,执行完成后就结束了

# 创建后,命令 echo hello welcome Kubernetes is a good product 将在容器中运行,也就是环境变量中的值被传递到了容器中。
# 查看pod就可以看出
# [root@k8s-master yaml]# kubectl logs print-mapenv
hello welcome Kubernetes is a good product

现在测试的这种情况是将配置数据和yaml资源文件耦合在一起了,接下来我们使用configMap分离

3. 创建ConfigMap

3.1 help文档

通过hepl命令可以查看configmap的用法:

# [root@k8s-master-155-221 configmap]# kubectl create  configmap --help
......
Aliases:
configmap, cm  #可以使用cm替代

Examples:
  # Create a new configmap named my-config based on folder bar
  # 从目录创建  文件名称为键  文件内容为值
  kubectl create configmap my-config --from-file=path/to/bar  

  # Create a new configmap named my-config with specified keys instead of file basenames on disk
  # 从文件创建 key1为键 文件内容为值
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt 

  # Create a new configmap named my-config with key1=config1 and key2=config2
  # 直接命令行给定,键为key1 值为config1
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2   

  # Create a new configmap named my-config from the key=value pairs in the file
  # 从文件创建 文件名为键 文件内容为值
  kubectl create configmap my-config --from-file=path/to/bar   

  # Create a new configmap named my-config from an env file
  kubectl create configmap my-config --from-env-file=path/to/bar.env

  Usage:
  kubectl create configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none]
[options]

Use "kubectl options" for a list of global command-line options (applies to all commands).

3.2 使用目录创建

# 指定目录
# 创建configmap目录
# ls /opt/yaml/configmap

# 创建game.properties文件
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

# ui.propertes文件
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
# 创建configmap ,指令
# game-config-test :configmap的名称
# --from-file:指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容
# [root@k8s-master configmap]# kubectl create configmap game-config-test --from-file=/opt/yaml/configmap
configmap/game-config-test created

# 查看configmap文件
# [root@k8s-master configmap]# kubectl get cm 
NAME               DATA   AGE
game-config-test   2      24s
# 查看详细信息 -o 指定输出格式为yaml
# [root@k8s-master configmap]# kubectl get cm game-config-test -o yaml
apiVersion: v1
data:
  game.properties: |   # | 前是文件名; | 后是文件内容
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.propertes: |     # | 前是文件名; | 后是文件内容
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap      # 类型
metadata:
  creationTimestamp: "2021-06-04T06:45:42Z"
  name: game-config-test   # 名称
  namespace: default       # 命名空间
  resourceVersion: "625034"
  uid: 8a7f0064-f816-4e70-a367-bd37371dcaec

data就是数据区,存放的就是配置文件,key就是文件名,后面"|"后跟的就是配置文件内容,这个是yaml语法

# 描述configmap
# [root@k8s-master configmap]# kubectl describe cm game-config-test -n default
Name:         game-config-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

ui.propertes:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

Events:  <none>

清空环境 [root@k8s-master configmap]# kubectl delete cm/game-config-test -n default

3.3 根据文件创建

只需要指定为一个文件就可以从单个文件中创建ConfigMap

# 指定创建的文件即可
kubectl create configmap game-config-test2 --from-file=/opt/yaml/configmap/game.properties

#查看
kubectl get cm game-config-test2 -o yaml

--from-file这个参数可以使用多次,可以分别指定game.properties,ui.propertes,效果和指定整个目录是一样的。

# [root@k8s-master configmap]# kubectl create configmap game-config-test2  --from-file=/opt/yaml/configmap/game.properties --from-file=/opt/yaml/configmap/ui.propertes  
configmap/game-config-test2 created

# [root@k8s-master configmap]# kubectl get cm -n default
NAME                DATA   AGE
game-config-test2   2      8s

# [root@k8s-master configmap]# kubectl get cm  game-config-test2 -n default -o yaml

# [root@k8s-master configmap]# kubectl describe cm game-config-test2 -n default

清理环境 kubectl delete cm/game-config-test2 -n default

3.4 文字创建

# 使用--from-literal 方式直接创建configmap
# Create the ConfigMap
# [root@k8s-master configmap]# kubectl create configmap test-config-1 --from-literal=key1=value1 --from-literal=key2=value2
configmap/test-config-1 created

# 获取cm
# [root@k8s-master configmap]# kubectl get cm -n default
NAME               DATA   AGE
test-config-1      2      17s

# 以yaml格式查看cm
# Get the ConfigMap Details for my-config
# [root@k8s-master configmap]# kubectl describe cm/test-config-1 -n default
Name:         test-config-1
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
key1:
----
value1
key2:
----
value2
Events:  <none>
[root@k8s-master configmap]# kubectl get cm test-config-1 -n default -o yaml
apiVersion: v1
data:
  key1: value1
  key2: value2
kind: ConfigMap
metadata:
  creationTimestamp: "2021-06-04T07:07:50Z"
  name: test-config-1
  namespace: default
  resourceVersion: "627044"
  uid: 8f904ca7-94f7-4262-98d9-69385130df28
# 描述cm
# [root@k8s-master configmap]# kubectl describe cm/test-config-1 -n default
Name:         test-config-1
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
key1:
----
value1
key2:
----
value2
Events:  <none>

使用文字方式创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次。

3.5 直接方法(yaml文件方式创建)

ConfigMap 是一个 API 对象, 让你可以存储其他对象所需要使用的配置。 和其他 Kubernetes 对象都有一个 spec 不同的是,ConfigMap 使用 data 和 binaryData 字段。这些字段能够接收 键-值对作为其取值。data 和 binaryData 字段都是可选的。data 字段设计用来保存 UTF-8 字节序列,而 binaryData 则 被设计用来保存二进制数据。
ConfigMap 的名字必须是一个合法的 DNS 子域名。
data 或 binaryData 字段下面的每个键的名称都必须由字母数字字符或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下 出现的键名有重叠。
从v1.19 开始,你可以添加一个 immutable[长期不变的 / 不可改变的] 字段到 ConfigMap 定义中,创建 不可变更的 ConfigMap。

&nbsp;

示例:

# 直接通过配置文件的方式创建
# vim configmap-test1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: game-config
  namespace: default
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
# [root@k8s-master configmap]# kubectl apply -f configmap-test1.yaml 
configmap/configmap-test-one created

# [root@k8s-master configmap]# kubectl  get cm/configmap-test-one -n default
NAME                 DATA   AGE
configmap-test-one   2      23s

# [root@k8s-master configmap]# kubectl  get cm/configmap-test-one -n default -o yaml
apiVersion: v1
data:
  game.properties: |
    firstkey=firstvaule
    secondkey=secondvalue
    thirdkey=thirdvalue
    forthkey=forthvalue
  ui.properties: |
    key11=value11
    key21=value21
    key31=value31
    key41=value41
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"game.properties":"firstkey=firstvaule\nsecondkey=secondvalue\nthirdkey=thirdvalue\nforthkey=forthvalue\n","ui.properties":"key11=value11\nkey21=value21\nkey31=value31\nkey41=value41\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"configmap-test-one","namespace":"default"}}
  creationTimestamp: "2021-06-04T07:22:57Z"
  name: configmap-test-one
  namespace: default
  resourceVersion: "628411"
  uid: 5f6bc92c-ac17-401d-8948-44c1fdcab7ea

# [root@k8s-master configmap]# kubectl describe cm/configmap-test-one -n default
Name:         configmap-test-one
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
ui.properties:
----
key11=value11
key21=value21
key31=value31
key41=value41

game.properties:
----
firstkey=firstvaule
secondkey=secondvalue
thirdkey=thirdvalue
forthkey=forthvalue

Events:  <none>

4. pod中应用

你可以使用四种方式来使用 ConfigMap 配置Pod 中的容器:

1、 容器entrypoint的命令行参数;
2、 容器的环境变量;
3、 在只读卷里面添加一个文件,让应用来读取;
4、 编写代码在Pod中运行,使用KubernetesAPI来读取ConfigMap;

这些不同的方法适用于不同的数据使用方式。 对前三个方法,kubelet 使用 ConfigMap 中的数据在 Pod 中启动容器。

第四种方法意味着你必须编写代码才能读取 ConfigMap 和它的数据。然而, 由于你是直接使用 Kubernetes API,因此只要 ConfigMap 发生更改,你的应用就能够通过订阅来获取更新,并且在这样的情况发生的时候做出反应。 通过直接进入 Kubernetes API,这个技术也可以让你能够获取到不同的名字空间里的 ConfigMap。

先创建两个configMap

# 创建第一个configMap,   special.how: very   键名:键值
# vim configmap-for-pod.yaml
---
apiVersion: v1
kind: ConfigMap
metadata: 
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

# 创建第二个configMap
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO

清空环境,删除之前操作创建的configmap:

# 创建configMap:
# [root@k8s-master configmap]# kubectl apply -f config-for-pod.yaml
configmap/special-config created
configmap/env-config created

# [root@k8s-master configmap]# kubectl get cm -n default
NAME               DATA   AGE
env-config         1      12m
special-config     2      12m

4.1 第一种方式: 在pod中使用ConfigMap来替代环境变量

  • valueFrom -> configMapKeyRef 通过valueFrom 导入
  • envFrom -> configMapRef 通过envFrom 导入
# vim configmap-instead-podenv.yaml
# 第一种方式: 在pod中使用configmap配置,使用ConfigMap来替代环境变量
apiVersion: v1
kind: Pod
metadata:
  name: configmap-instead-podenv
spec:
  containers:
    - name: cm-instead-podenv
      image: centos:7.9.2009
      imagePullPolicy: IfNotPresent
      command: 
      - "/bin/sh"
      -  "-c"
      - "env"   # 容器一但运行会执行命令/bin/sh -c env,会在控制台打印一下

      env: # 第一种导入方式:在env中导入
        - name: SPECIAL_LEVEL_KEY   # name是当前容器环境变量的key
          valueFrom:                # 通过valueFrom导入
            configMapKeyRef: 
              name: special-config  # name是要引入哪个configMap的name
              key: special.how      # key是要引入该configMap中的哪个key
        - name: SPECIAL_TYPE_KEY
          valueFrom: 
            configMapKeyRef: 
              name: special-config 
              key: special.type 
      envFrom:                      # 第二种导入方式,直接使用envFrom导入
        - configMapRef: 
            name: env-config 
  restartPolicy: Never
# 查看日志可以发现,环境变量注入到了容器中了,打印env就结束了
# [root@k8s-master configmap]# kubectl apply -f configmap-instead-podenv.yaml
pod/configmap-instead-podenv created
# [root@k8s-master configmap]# kubectl get po 
NAME                       READY   STATUS      RESTARTS   AGE
configmap-instead-podenv   0/1     Completed   0          15s
# [root@k8s-master configmap]# kubectl logs pod/configmap-instead-podenv -n default
MYNGINX_PORT_80_TCP=tcp://10.96.28.181:80
HOSTNAME=configmap-instead-podenv
MYNGINX_PORT=tcp://10.96.28.181:80
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
MYSERVICE_SERVICE_PORT=80
MYNGINX_PORT_80_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_HOST=10.96.0.1
MYNGINX_PORT_80_TCP_PORT=80
MYSERVICE_PORT_80_TCP_ADDR=10.96.17.78
SPECIAL_TYPE_KEY=charm   # 这里的env是导入的
MYSERVICE_PORT_80_TCP=tcp://10.96.17.78:80
SPECIAL_LEVEL_KEY=very   # 这里的env是导入的
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
MYSERVICE_PORT=tcp://10.96.17.78:80
SHLVL=1
HOME=/root
log_level=INFO     # 这里的env是导入的
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
MYSERVICE_PORT_80_TCP_PORT=80
MYSERVICE_PORT_80_TCP_PROTO=tcp
MYNGINX_SERVICE_HOST=10.96.28.181
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
MYNGINX_PORT_80_TCP_ADDR=10.96.28.181
MYSERVICE_SERVICE_HOST=10.96.17.78
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
MYNGINX_SERVICE_PORT=80
_=/usr/bin/env

清除环境:kubectl delete -f configmap-instead-podenv.yaml

4.2 第二种方式:用ConfigMap设置命令行参数

用作命令行参数,将 ConfigMap 用作命令行参数时,需要先把 ConfigMap 的数据保存在环境变量中,然后通过 $(VAR_NAME) 的方式引用环境变量.

#第二种方式:用ConfigMap设置命令行参数
# vim configmap-for-cmdargs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-for-cmdargs
spec:
  containers:
    - name: configmap-for-cmdargs
      image: centos:7.9.2009
      imagePullPolicy: IfNotPresent
      command: 
      - "/bin/sh"
      - "-c"
      - "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)"    # 命令行中通过${}获取环境变量参数
      env:
        - name: SPECIAL_LEVEL_KEY  # name是当前容器环境变量的key
          valueFrom:  # 通过valueFrom引入
            configMapKeyRef: 
              name: special-config  # name是要引入哪个configMap的name
              key: special.how      # key是要引入该configMap中的哪个key
        - name: SPECIAL_TYPE_KEY 
          valueFrom: 
            configMapKeyRef: 
              name: special-config 
              key: special.type 
  restartPolicy: Never
# [root@k8s-master configmap]# kubectl apply -f configmap-for-cmdargs.yaml
pod/configmap-for-cmdargs created
# [root@k8s-master configmap]# kubectl get pods -n default
NAME                    READY   STATUS      RESTARTS   AGE
configmap-for-cmdargs   0/1     Completed   0          21s
# [root@k8s-master configmap]# kubectl logs pod/configmap-for-cmdargs -n default
very charm

4.3 第三种方式:通过数据卷挂载ConfigMap

在数据卷里面使用这个ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容,即在 Pod 中将 ConfigMap 当做文件使用。

# 第三种方式:通过数据卷挂载ConfigMap
# vim configmap-pass-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-pass-volume
spec:
  containers:
    - name: configmap-pass-volume
      image: centos:7.9.2009
      imagePullPolicy: IfNotPresent
      command: 
      - "/sbin/init"
      volumeMounts: # 挂载数据卷
        - name: configmap-volume # 指定数据卷名
          mountPath: /etc/config # 表示把conifg-volume数据卷挂载到容器的/etc/config目录下
  volumes:    # 定义数据卷
    - name: configmap-volume #给数据卷起名
      configMap:          #数据卷挂载configmap
        name: special-config #挂载的configmap名字
  restartPolicy: Never
# [root@k8s-master configmap]# kubectl apply -f configmap-pass-volume.yaml
pod/configmap-pass-volume created

# [root@k8s-master configmap]# kubectl get pods -n default
NAME                    READY   STATUS    RESTARTS   AGE
configmap-pass-volume   1/1     Running   0          8s

登录容器查看/etc/config目录下是否挂载成功

# 进入容器中
# [root@k8s-master configmap]# kubectl exec -it pod/configmap-pass-volume -n default -- bash

# 查看挂载
# [root@configmap-pass-volume /]# ls /etc/config/
special.how  special.type
# [root@configmap-pass-volume /]# cd /etc/config/
# [root@configmap-pass-volume config]# cat special.how special.type 
verycharm

清除环境: kubectl delete -f configmap-pass-volume.yaml

4.4 被挂载的 ConfigMap 内容会被自动更新-热更新:

# ConfigMap的热更新
# vim configmap-hot-update.yaml
---
# 创建一个configmap
apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
#  创建pod
apiVersion: apps/v1
kind: Deployment 
metadata: 
  name: my-nginx 
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      restartPolicy: Always
      containers:
        - name: my-nginx
          image: nginx:1.20.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80 
              name: http-port
              protocol: TCP
          volumeMounts: # 挂载数据卷
            - name: configmap-volume-nginx
              mountPath: /etc/config  # 挂载到容器的/etc/config目录下
      volumes:
        - name: configmap-volume-nginx
          configMap: # 数据卷挂载了上面的ConfigMap
            name: log-config
# [root@k8s-master configmap]# kubectl apply -f configmap-hot-update.yaml
configmap/log-config created
deployment.apps/my-nginx created

# [root@k8s-master configmap]# kubectl get pods -n default
NAME                       READY   STATUS    RESTARTS   AGE
my-nginx-6fd588dfc-f5dzc   1/1     Running   0          45s

# 进入容器查看挂载情况
# [root@k8s-master configmap]# kubectl exec -it pod/my-nginx-6fd588dfc-f5dzc -n default -- bash
# root@my-nginx-6fd588dfc-f5dzc:/# ls /etc/config/
log_level  # key是文件名
# root@my-nginx-6fd588dfc-f5dzc:/# cat /etc/config/log_level 
INFO   # value是文件内容

修改ConfigMap

# kubectl edit configmap log-config -n default
# 修改log-level的值为DEBUG等待大概10秒钟时间,再次查看环境变量的值

# [root@k8s-master configmap]# kubectl edit configmap log-config -n default
configmap/log-config edited

# 再次查看环境变量的值发生了变化
# [root@k8s-master configmap]# kubectl exec pod/my-nginx-6fd588dfc-f5dzc -n default -- cat /etc/config/log_level
DEBUG

二、Secret

官方文档Secret

1. 理解Secret

Secret对象存储数据的方式是以键值方式存储数据,在Pod资源进行调用Secret的方式是通过环境变量或者存储卷的方式进行访问数据,解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。

另外,Secret对象的数据存储和打印格式为Base64编码的字符串,因此用户在创建Secret对象时,也需要提供该类型的编码格式的数据。在容器中以环境变量或存储卷的方式访问时,会自动解码为明文格式。需要注意的是,如果是在Master节点上,Secret对象以非加密的格式存储在etcd中,所以需要对etcd的管理和权限进行严格控制。

要使用Secret,Pod 需要引用 Secret。 Pod 可以用三种方式之一来使用 Secret:

  • 作为挂载到一个或多个容器上的 卷 中的文件。
  • 作为容器的环境变量
  • 由 kubelet 在为 Pod 拉取镜像时使用

Secret 对象的名称必须是合法的 DNS 子域名。 在为创建 Secret 编写配置文件时,你可以设置 data 与/或 stringData 字段。 data 和 stringData 字段都是可选的。data 字段中所有键值都必须是 base64 编码的字符串。如果不希望执行这种 base64 字符串的转换操作,你可以选择设置 stringData 字段,其中可以使用任何字符串作为其取值。

2. Secret 的类型

在创建Secret 对象时,你可以使用 Secret 资源的 type 字段,或者与其等价的 kubectl 命令行参数(如果有的话)为其设置类型。 Secret 的类型用来帮助编写程序处理 Secret 数据。

Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。

内置类型 用法
Opaque 用户定义的任意数据 base64编码格式的Secret,用来存储密码、密钥、信息、证书等,类型标识符为generic;
http://kubernetes.io/service-account-token 服务账号令牌 用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/http://kubernetes.io/serviceaccount目录中;
http://kubernetes.io/dockercfg ~/.dockercfg 文件的序列化形式
http://kubernetes.io/dockerconfigjson ~/.docker/config.json 文件的序列化形式 用来存储私有docker registry的认证信息,类型标识为docker-registry
http://kubernetes.io/basic-auth 用于基本身份认证的凭据
http://kubernetes.io/ssh-auth 用于 SSH 身份认证的凭据
http://kubernetes.io/tls 用于 TLS 客户端或者服务器端的数据 用于为SSL通信模式存储证书和私钥文件,命令式创建类型标识为tls。
http://bootstrap.kubernetes.io/token 启动引导令牌数据

通过为Secret 对象的 type 字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型。如果 type 值为空字符串,则被视为 Opaque 类型。 Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。

3. Service Account

Kubernetes 在创建 Pod 时会自动创建一个服务账号 Secret 并自动修改你的 Pod 以使用该 Secret。该服务账号令牌 Secret 中包含了访问 Kubernetes API 所需要的凭据。

Service Account 用来访问kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/http://kubernetes.io/serviceaccount目录中。

Service Account 不需要我们自己去管理的,此证书是由kubernetes自己来进行维护管理的。

# 创建pod
kubectl run my-nginx --image=nginx:1.20.0

# 查看证书
kubctl exec -it podName -- bash

# 进入证书目录/run/secrets/kubernetes.io/serviceaccount查看即可
ca.crt
namespace
token

# 查看证书
# root@my-nginx:/run/secrets/kubernetes.io/serviceaccount# cat ca.crt 
-----BEGIN CERTIFICATE-----
MIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIxMDUxNzA3MTgyMloXDTMxMDUxNTA3MTgyMlowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIB
eVgGvrAomjLyj4fgasSoJQoZnhpZj75wKDOg7gYFKmGb9oLX5qIj33jqmrK4bUI5
symIPTJTrNZ7FsVmeC66JiE50niMpxuD01pDJARTfc0cMgkFWDq7JsVptA7MNILT
81keKB7eOZY0Cx7c/O+9w6f6UrvpHTMgNvE6wG3StFw2YJFPhDC6ZqwduISzBIsK
wbTrwp6jBHPTxsQEmEuHuoe0Hz+sjov5wXFpw7QB5V+P980tVvgK1GGX9wPxMytJ
ofv3vwbvHD/DrotA6HNwdYELIEgFULXPGuO/HL4z7C2MJxqVW7UdirCdnBDtI6HB
m55toVZJtmHj7kCpkzMCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFIaXFp8Y2aneT8Wk59gysMNJxmOhMA0GCSqGSIb3
DQEBCwUAA4IBAQCuPXfqD25NJagNOoEPjXyTfWGCHHBJSqSknnz3B/KaJu7hzIwD
G5c5zLQwDc/chHNjaRRZWcvOpfQfmqdhRg0EdDIa/B4cVmGa9eUs7f2XwlZuu6aw
5VOoTRZ6h/a9L4RQLxSSWTl2/AR4YeiBiU1tjfrc+gmZTObptmNLyuDfU7A4BG7U
1N8AddG4dojH2a7xbAnIKbTjTXRtLsI5aC0BPWHazwwG5NOreCauD+yVnQlw/dcw
C74QaamuFeWr/K2W0pq0qcjH471xuKhOUnY02HkN7P1zOL2uIZQ613yBYNksmPZB
9AQJ9VZlD6szo0XoniWcSD0Z2J90pbFCJCUd
-----END CERTIFICATE-----

# 查看命名空间
root@my-nginx:/run/secrets/kubernetes.io/serviceaccount# cat namespace 
default

# 查看token
# root@my-nginx:/run/secrets/kubernetes.io/serviceaccount# cat token 
eyJhbGciOiJSUzI1NiIsImtpZCI6ImIyMVg2OTBtZm1jUVNWM2ZGMUI0QkQ2MzJVS0EyOER2N3NuZFpha3pRMDgifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjU0MzM0NzE4LCJpYXQiOjE2MjI3OTg3MTgsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJteS1uZ2lueCIsInVpZCI6IjEyNzI5NWFjLWIzN2EtNDc5Zi1hMzYwLWEzNDg4ODA0NDgxZCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInVpZCI6IjE5Y2ZkZmM2LThlZGMtNDNjYy05YjUxLTJlNDUzZjMwNTRhZSJ9LCJ3YXJuYWZ0ZXIiOjE2MjI4MDIzMjV9LCJuYmYiOjE2MjI3OTg3MTgsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.i-VcnUBSBGm6FlukqjgcWe50nBQNGgERoKjWhkPod5rTAIswJ9EGyUcADt-vYewT4CYb8R4SVRY53kfk2mPnaH54_b9BbHD5jixA99YxYcDc7N99UE_ySyJj13zHlPNRFgoagBKmwr2b2nXNtw5PEAKqI_lHbDI2oQfUHY_yTqUM9si6HMqGJJ7W2non44OzGnB33RAV--ZzJkr6oZBkgMesPYAwsJa4FMoCOIB8OtczQ3Yvtr7IxvMGoeSRbjcjN5A_v4p8-GpjlkbbXfMME9B04iFeZmhERQpkf6CwtnXgEtNwktYJATQ9jOE9lXLwYd4WpBr7zspmU67yuVtuqQ

4. Opaque Secret

4.1 创建示例

当Secret 配置文件中未作显式设定时,默认的 Secret 类型是 Opaque。 当你使用 kubectl 来创建一个 Secret 时,你会使用 generic 子命令来标明 要创建的是一个 Opaque 类型 Secret。

Opaque类型的数据一个map类型,要求value是base64编码格式

# base64对用户名,密码加密效果演示
# [root@k8s-master configmap]# echo "superadmin" | base64
c3VwZXJhZG1pbgo=

# [root@k8s-master configmap]# echo "passpppp" | base64
cGFzc3BwcHAK

多次加密结果都是一样的,破解很容易

# secre-test.yaml配置文件方式
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
 username: c3VwZXJhZG1pbgo=   # 上面演示用户名加密结果
 password: cGFzc3BwcHAK       # 密码加密结果
# [root@k8s-master configmap]# kubectl apply -f secret-test.yaml
secret/mysecret created

# [root@k8s-master configmap]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-cgxwv   kubernetes.io/service-account-token   3      18d
mysecret              Opaque                                2      13s  # 自定义的secret类型为Opaque
tls-secret            kubernetes.io/tls                     2      8d

4.2 使用方式一:作为挂载到一个或多个容器上的 卷 中的文件

# 将secret挂载到volume中
# secret-in-volume.yaml
apiVersion: v1
kind: Pod
metadata:
 name: secret-test
 labels:
   name: secret-test
spec:
  containers:
  - name: nginx-secret-volume
    image: nginx:1.20.0
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: myvolsecrets
      mountPath: "/etc/secrets" # 容器挂载数据卷,挂载到/etc/secrets目录下
      readOnly: true
  volumes: # 引入一个数据卷
  - name: myvolsecrets
    secret: # 挂载指定的secret
      secretName: mysecret
# [root@k8s-master configmap]# kubectl apply -f secret-in-volume.yaml
pod/secret-test created

# [root@k8s-master configmap]# kubectl get pods 
NAME          READY   STATUS    RESTARTS   AGE
secret-test   1/1     Running   0          3m1s

# 进入容器
[root@k8s-master configmap]# kubectl exec -it secret-test -n default -- bash

# 切换路径
# root@secret-test:/# cd /etc/secrets/

# 查看挂载文件
# root@secret-test:/etc/secrets# ls
password  username

# 查看文件内容--文件内容已自动解析为明文
# root@secret-test:/etc/secrets# cat password username
passpppp
superadmin

清理环境 kubectl delete -f secret-in-volume.yaml

4.3 使用方式二:作为容器的环境变量

# 将secret导出到环境变量中
# vim secret-to-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: secret-to-envdeployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pod-secret-to-env
  template:
    metadata:
      labels:
        app: pod-secret-to-env
    spec:
      containers:
      - name: secret-to-envdeployment
        image: nginx:1.20.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        env: # 设置环境变量
        - name: TEST_USER
          valueFrom:
            secretKeyRef: # 核心在这里 secretKeyRef
              name: mysecret   # secret名称
              key: username    # key的名称
        - name: TEST_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
# [root@k8s-master configmap]# kubectl apply -f secret-to-env.yaml
deployment.apps/secret-to-envdeployment created

# [root@k8s-master configmap]# kubectl get pods 
NAME                                       READY   STATUS    RESTARTS   AGE
secret-to-envdeployment-67b9584f69-tmlzf   1/1     Running   0          9s

# 进入容器
# [root@k8s-master configmap]# kubectl exec -it  pod/secret-to-envdeployment-67b9584f69-tmlzf -n default -- bash

# 打印环境变量
# root@secret-to-envdeployment-67b9584f69-tmlzf:/# echo ${TEST_USER}
superadmin
# root@secret-to-envdeployment-67b9584f69-tmlzf:/# echo ${TEST_PASSWORD}
passpppp