Kubernetes 配置之Secret

这里介绍Kubernetes配置的Secret

Kubernetes 配置之Secret

abstract.png

创建Secret

对于敏感性、机密性的配置数据,Kubernetes提供了Secret进行存储。具体地,其以键值对的形式进行存储。下面即是一个简单的利用Yaml文件定义配置数据的Secret示例。其中,对于data字段下添加的配置而言,其值必须填写原文进行Base 64编码后的结果;为此我们还可以使用stringData字段添加配置,此时值可以使用原文。但需要注意的是stringData是「只写」的。当通过配置文件创建完Secret后,所有键值对将只会在data字段下存在,包括stringData字段下的配置也会被放置到data字段下

apiVersion: v1
kind: Secret
# 用户自定义类型的Secret, 该字段未显式设置则默认为Opaque
type: Opaque
metadata:
  name: my-secret-1
# 值使用原文
stringData:
  url: localhost:3306/db1  # echo -n 'bG9jYWxob3N0OjMzMDYvZGIx' | base64 -d ---> localhost:3306/db1
# 值需使用Base 64进行编码
data:
  # admin 的Base 64编码值为 YWRtaW4=
  # echo -n 'admin' | base64  --->  YWRtaW4=
  username: YWRtaW4=
  # 123456 的Base 64编码值为 MTIzNDU2
  # echo -n '123456' | base64 --->  MTIzNDU2
  password: MTIzNDU2

效果如下所示

Kubernetes 配置之Secret

figure 1.jpeg

类似地,Secret同样支持通过命令行进行创建。具体地:

  • 「–from-literal=[key]=[value]」 :从字面量定义配置
  • 「–from-file=[fileName]」 :从指定文件创建配置,Key默认为文件名,Value为该文件的内容
  • 「–from-file=[key]=[fileName]」 :从指定文件创建配置,Key为指定名称,Value为该文件的内容
  • 「–from-file=[folderName]」 :从指定文件夹创建配置, Key为该文件夹下各文件名, Value为相应文件的内容

假设目前应用的所有配置文件按如下方式存储

Kubernetes 配置之Secret

figure 2.jpeg

则我们可通过下述命令创建Secret

# 创建一个名为my-secret-2的secret
# generic 子命令标明创建的是Opaque类型的Secret
kubectl create secret generic my-secret-2 
    `# 从指定文件夹创建配置, Key为文件名`       
    --from-file=PersonInfo/                 
    `# 从指定文件创建配置, Key名为文件名`        
    --from-file=MongoDB.properties          
    `# 从指定文件夹创建配置, 指定Key名为pgdb`    
    --from-file=pgdb=PostgreSQL.properties   
    `# 从字面量创建配置`                        
    --from-literal=firstName="Tom"

效果如下所示,符合预期

Kubernetes 配置之Secret

figure 3.jpeg

使用Secret

基于环境变量

对于容器而言,其使用Secret最简单的方式即是通过环境变量实现。具体地,利用Secret的条目来初始化容器运行过程中所需的环境变量值。需要注意的是此时给环境变量赋的值,是将Secret中相应配置的值进行Base 64解码后的结果。但由于应用程序通常会在报告错误时转储环境变量,或在应用程序的日志中打印了Secret信息,故基于环境变量的方式并不是非常安全。这里我们构建一个名为demo:1.0的容器镜像,Dockerfile如下所示

# 镜像:demo:1.0

FROM ubuntu:22.10
COPY demo.sh /home/aaron/
ENTRYPOINT ["sh""/home/aaron/demo.sh" ]

该镜像很简单,就是执行一个Shell脚本,具体地如下所示,获取环境变量的值、重复打印即可

#!/bin/bash
while true
do
    echo "${greet}, I'm ${name}. I want to say: ${msg}"
    sleep 5s
done

文件结构如下所示。Docker镜像构建完毕后推送至中央仓库Docker Hub当中,以便后续Kubernetes可以从中央仓库中拉取、使用该镜像

Kubernetes 配置之Secret

figure 4.jpeg

我们可以先通过Docker创建容器来验证下镜像是否正常

docker run --name demo1 --rm -it  
    --env greet="Hello" 
    --env name="Tom" 
    --env msg="Good Luck" demo:1.0

效果如下所示,符合预期

Kubernetes 配置之Secret

figure 5.jpeg

现在我们创建一个Secret,并将Secret中的条目通过环境变量的方式暴露、传递给容器

# 创建 Secret

apiVersion: v1
kind: Secret
metadata:
  name: my-app-secret-1
stringData:
  app.greet: Hi
  app.name: Tom
  app.msg: Thank you

---

# 创建RS

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-1
spec:  
  replicas: 1
  selector:
    matchLabels:
      app: my-app-1
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-1
    spec:
      # 容器信息
      containers:
        - name: my-app-1
          image: aaron1995/demo:1.0
          env:
          # 将名为my-app-secret-1的Secret中Key为app.greet的配置 赋值给 容器中名为greet的环境变量
          - name: greet
            valueFrom:
              secretKeyRef:                
                name: my-app-secret-1
                key: app.greet
                # 对Secret的引用是可选的, 这样即使Secret不存在, 容器也能正常启动
                optional: true
          # 将名为my-app-secret-1的Secret中Key为app.name的配置 赋值给 容器中名为name的环境变量
          - name: name
            valueFrom:
              secretKeyRef:
                name: my-app-secret-1
                key: app.name
                optional: true
          # 将名为my-app-secret-1的Secret中Key为app.msg的配置 赋值给 容器中名为msg的环境变量
          - name: msg
            valueFrom:
              secretKeyRef:
                name: my-app-secret-1
                key: app.msg
                optional: true

---

效果如下所示,通过查看容器日志说明其成功获取到了配置信息

Kubernetes 配置之Secret

figure 6.jpeg

如果Secret的条目非常多,一个一个定义环境变量显然非常繁琐、麻烦。故我们还可以一次性将Secret中的所有条目全部设置容器当中的环境变量。其中Secret中的键名将作为环境变量的名称

# 创建 Secret

apiVersion: v1
kind: Secret
metadata:
  name: my-app-secret-2
stringData:
  greet: Hello
  name: Aaron
  msg: I Love U

---

# 创建RS

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-2
spec:  
  replicas: 1
  selector:
    matchLabels:
      app: my-app-2
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-2
    spec:
      # 容器信息
      containers:
        - name: my-app-2
          image: aaron1995/demo:1.0          
          envFrom:
          # 将名为my-app-secret-2的Secret中的配置 全部作为容器中的环境变量
          - secretRef:
              name: my-app-secret-2

---

效果如下所示,通过查看容器日志说明其成功获取到了配置信息

Kubernetes 配置之Secret

figure 7.jpeg

基于Secret类型卷

同理针对Secret资源而言,Kubernetes同样提供了一种卷——Secret卷。故可以通过Secret类型卷的方式将Secret中的条目暴露、传递到容器当中。当Secret发生更新后,Secret类型卷中的文件也会自动更新。而无需像基于环境变量的方式时,通过重启容器获取Secret中的新配置。在此之前,我们先创建一个Secret。其从文件、字面量当中创建配置。命令如下所示

# 创建一个名为my-app-secret-4的Secret
kubectl create secret generic my-app-secret-4     
    `# 从指定文件夹创建配置, Key为文件名`    
    --from-file=PostgreSQL.properties       
    `# 从字面量创建配置`                        
    --from-literal=sex="man"                
    --from-literal=msg="This is a Test"

效果如下所示

Kubernetes 配置之Secret

figure 8.jpeg

然后我们利用Secret类型卷将Secret中的全部条目暴露为容器内的文件,这样容器就可以直接读取该配置文件了。示例配置如下所示

# Secret通过Secret类型卷传递给容器: 方式1: 暴露Secret中全部条目

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-5
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app-5
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-5
    spec:
      # 容器信息
      containers:
      - name: my-app-5
        image: luksa/kubia
        volumeMounts:
        # 将名为my-volume-app-conf的卷挂载到容器内的指定路径
        - name: my-volume-app-conf
          mountPath: /AppConf
          # 容器对该卷只读
          readOnly: true
      volumes:
      # 名为my-volume-app-conf的Secret类型卷
      - name: my-volume-app-conf
        secret:
          # Secret类型卷引用的Secret名称
          secretName: my-app-secret-4

效果如下所示,符合预期。不难看出,一方面通过Secret卷中文件读取到依然是Base 64解码后的值;另一方面,Secret卷使用的是基于内存的tmpfs文件系统,而非基于硬盘。进一步地避免了Secret中数据被窃取

Kubernetes 配置之Secret

figure 9.jpeg

此外还可以将Secret中指定条目暴露为容器当中的文件,示例配置如下所示

# Secret通过卷传递给容器: 方式2: 暴露Secret中指定条目

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-6
spec:  
  replicas: 1
  selector:
    matchLabels:
      app: my-app-6
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-6
    spec:
      # 容器信息
      containers:
      - name: my-app-6
        image: luksa/kubia
        volumeMounts:
        # 将名为my-volume-app-conf的卷挂载到容器内的指定路径
        - name: my-volume-app-conf
          mountPath: /AppConf
          # 容器对该卷只读
          readOnly: true
      volumes:
      # 名为my-volume-app-conf的Secret类型卷
      - name: my-volume-app-conf
        secret:
          # Secret类型卷引用的Secret名称
          secretName: my-app-secret-4          
          items:
          # 将my-app-secret-4中Key为sex的配置 暴露作为 Secret类型卷中的文件
          - key: sex
            path: sex
          # 将my-app-secret-4中Key为PostgreSQL.properties的配置 暴露作为 Secret类型卷中的文件
          # 并将文件重命名为 pg.properties
          - key: PostgreSQL.properties
            path: pg.properties         

效果如下所示,符合预期

Kubernetes 配置之Secret

figure 10.jpeg

众所周知,在Linux系统当中将文件系统挂载到非空文件夹时,会导致该非空文件夹下原有的文件全部被隐藏。此时原有文件自然也无法正常访问使用。为避免该问题,我们可以将Secret中的指定条目挂载到容器的指定文件中。具体地,通过subPath选项实现。但需要注意的是,此时会导致配置无法进行热更新。即Secret中值更新后, 容器中的文件不会被更新

# Secret通过卷传递给容器: 方式3: 将指定条目挂载到指定文件中
# Note: 当Secret中值更新后, 容器中的文件不会更新

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-7
spec:  
  replicas: 1
  selector:
    matchLabels:
      app: my-app-7
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-7
    spec:
      # 容器信息
      containers:
      - name: my-app-7
        image: luksa/kubia
        volumeMounts:
        # 将名为my-volume-app-conf卷中的PostgreSQL.properties文件挂载到容器内的/usr/db.conf文件上
        - name: my-volume-app-conf
          mountPath: /usr/db.conf
          subPath: PostgreSQL.properties
        # 将名为my-volume-app-conf卷中的sex文件挂载到容器内的/usr/sex.conf文件上
        - name: my-volume-app-conf
          mountPath: /usr/sex.conf
          subPath: sex
      volumes:
      # 名为my-volume-app-conf的Secret类型卷
      - name: my-volume-app-conf
        secret:
          # Secret类型卷引用的Secret名称
          secretName: my-app-secret-4
          # 设置Secret类型卷中所有文件权限为742,配置时不足四位长度时需要使用前导零
          defaultMode: 0742

效果如下所示,符合预期

Kubernetes 配置之Secret

figure 11.jpeg

镜像拉取

当拉取私有仓库的容器镜像,你需要一种方式让每个节点上的kubelet能够完成与镜像仓库的身份认证。可以通过Docker配置类型的Secret来实现。具体地,我们可以通过docker-registry子命令标明创建kubernetes.io/dockerconfigjson类型的Secret

# docker-registry 子命令标明创建的是kubernetes.io/dockerconfigjson类型的Secret
kubectl create secret docker-registry <Secret的名称> 
  --docker-username=<Docker仓库的用户名> 
  --docker-password=<Docker仓库的密码> 
  --docker-email=<电子邮箱> 
  --docker-server=<Docker仓库的地址>

这里为了演示我们在Docker Hub中央仓库(docker.io)创建了一个账号,并推送了一个私密的容器镜像用于演示。下面创建Docker配置类型的Secret,效果如下所示

Kubernetes 配置之Secret

figure 12.jpeg

我们只需在Pod模板定义时使用imagePullSecrets字段配置拉取容器镜像所需的Secret即可,如下所示

# 使用拉取 Secret  拉取私服容器镜像

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-app-rs-8
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app-8
  # Pod 模板
  template:
    metadata:
      labels:
        app: my-app-8
    spec:
      # 配置拉取容器镜像所需 Secret
      imagePullSecrets:
        - name: my-secret-8
      # 容器信息
      containers:
      - name: my-app-8
        image: aaron1995/custom-ubuntu:1.0
        command:  # 该命令可以保持容器一直运行, 而不会结束退出
        - sleep
        - infinity

效果如下所示,Kubernetes成功拉取镜像并运行Pod

Kubernetes 配置之Secret

figure 13.jpeg

参考文献

  1. Kubernetes in Action中文版 Marko Luksa著
  2. 深入剖析Kubernetes 张磊著

原文始发于微信公众号(青灯抽丝):Kubernetes 配置之Secret

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/41907.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!