# Kubernetes 数据存储

在前面已经提到,容器的生命周期可能很短,会被频繁的创建和销毁。那么容器在销毁的时候,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器中的数据,k8s 引入了 Volume 的概念。

Volume 是 Pod 中能够被多个容器访问的共享目录,它被定义在 Pod 上,然后被一个 Pod 里面的多个容器挂载到具体的文件目录下,k8s 通过 Volume 实现同一个 Pod 中不同容器之间的数据共享以及数据的持久化存储。Volume 的生命周期不与 Pod 中的单个容器的生命周期有关,当容器终止或者重启的时候,Volume 中的数据也不会丢失。

k8s 的 Volume 支持多种类型,比较场景的有下面几个:

  • 简单存储:EmptyDir、HostPath、NFS
  • 高级存储:PV,PVC
  • 配置存储:ConfigMap、Secret

# 一、基本存储

# 1. emptyDir

emptyDir 是最基础的 Volume 类型,一个 emptyDir 就是Host上的一个空目录。

emptyDir 是在 Pod 被分配到 Node 时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为 k8s 会自动分配一个目录,当 Pod 销毁时,emptyDir 中的数据也会被永久删除。

emptyDir 的用途如下:

  • 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
  • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)

案例:

在一个 Pod 中准备两个容器 nginx 和 busybox,然后声明一个 volume 分别挂载到两个容器的目录中,然后 nginx 容器负责向volume 中写日志,busybox 中通过命令将日志内容读到控制台。

image-20210712102913718

# 1.1 创建 Pod

  • 创建 volume-emptydir.yaml 文件:

    apiVersion: v1
    kind: Pod
    metadata:
      name: volume-emptydir
      namespace: dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          volumeMounts:
            - name: logs-volume #将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
              mountPath: /var/log/nginx
        - name: busybox
          image: busybox:1.30
          imagePullPolicy: IfNotPresent
          command: ["/bin/bash", "-c", "tail -f /logs/access.log"] #初始命令,动态读取指定文件的内容变化
          volumeMounts: #将logs-volume挂载到busybox容器中的对应父母,该目录为/logs
            - name: logs-volume
              mountPath: /logs
      volumes:  #声明volume,name为logs-volume,类型为emptyDir
        - name: logs-volume
          emptyDir: {}
    
    image-20210712104838415
  • 创建 Pod

    [root@k8s-master volume]# kubectl create -f volume-emptydir.yaml 
    pod/volume-emptydir created
    
  • 查看 Pod

    [root@k8s-master volume]# kubectl get pod volume-emptydir -n dev -o wide
    NAME              READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
    volume-emptydir   2/2     Running   0          63s   10.244.3.20   k8s-node2   <none>           <none>
    

# 1.2 监听日志

kubectl logs -f volume-emptydir -n dev -c busybox

img

# 1.2 访问 Pod 中的 nginx

  • 访问几次 Pod 中的 nginx,使其有日志输出

    curl 10.244.3.20
    

# 2. hostPath

很明显 emptyDir 中的数据不会被持久化,它会随着 Pod 的结束而销毁,如果想要简单的将数据持久化到主机中,可以选择 hostPath

hostPath 就是将 Node 主机中的一个实际目录挂载到 Pod 中,以供容器使用,这样的设计就可以保证 Pod 销毁了,但是数据依旧可以保存在 Node 主机上。

image-20210712105059079

# 2.1 创建 Pod

  • 创建 volume-hostpath.yaml 文件:

    apiVersion: v1
    kind: Pod
    metadata:
      name: volume-hostpath
      namespace: dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
            - name: logs-volume
              mountPath: /var/log/nginx
        - name: busybox
          image: busybox:1.30
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
          volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
            - name: logs-volume
              mountPath: /logs
      volumes: # 声明volume,name为logs-volume,类型为hostPath
        - name: logs-volume
          hostPath:
            path: /root/logs
            type: DirectoryOrCreate # 目录存在就使用,不存在就先创建再使用
    

    type 的值的说明:

    • DirectoryOrCreate:目录存在就使用,不存在就先创建后使用

    • Directory:目录必须存在

    • FileOrCreate:文件存在就使用,不存在就先创建后使用

    • File:文件必须存在

    • Socket:unix 套接字必须存在

    • CharDevice:字符设备必须存在

    • BlockDevice:块设备必须存在

  • 创建 Pod

    kubectl create -f volume-hostpath.yaml
    
  • 查看 Pod

    kubectl get pod volume-hostpath -n dev -o wide
    

# 2.2 访问 Pod 中的 nginx

curl PodIP地址:80

# 2.3 删除 Pod

kubectl delete pod volume-hostpath -n dev

# 2.4 去 node 节点找到 hostPath 映射的目录中的文件

到Pod所在的节点(k8s-node1 或 k8s-node2)查看 hostPath 映射的目录中的文件:

ls /root/logs

同样的道理,如果在此目录中创建文件,到容器中也是可以看到的。

# 3. NFS

hostPath 虽然可以解决数据持久化的问题,但是一旦 Node 节点故障了,Pod 如果转移到别的 Node 节点上,又会出现问题,此时需要准备单独的网络存储系统,比较常用的是 NFSCIFS

NFS(network file system) 是一个网络文件存储系统,可以搭建一台 NFS 服务器,然后将 Pod 中的存储直接连接到 NFS 系统上,这样,无论 Pod 在节点上怎么转移,只要 Node 和 NFS 的对接没有问题,数据就可以成功访问。

image-20210712105956016

# 3.1 搭建 NFS 服务器

  • 首先需要准备 NFS 服务器,这里为了简单,直接在 k8s-master 节点做 NFS 服务器

    yum install -y nfs-utils rpcbind
    
  • 准备一个共享目录

    mkdir -pv /root/data/nfs
    
  • 将共享目录以读写权限暴露给172.16.58.0/24网段中的所有主机

    [root@k8s-master ~]# vim /etc/exports
    [root@k8s-master ~]# more /etc/exports
    /root/data/nfs 172.16.58.0/24(rw,no_root_squash)
    
  • 修改权限

    chmod 777 -R /root/data/nfs
    
  • 加载配置

    exportfs -r
    
  • 启动 NFS 服务

    systemctl start rpcbind
    
    systemctl enable rpcbind
    
    systemctl start nfs
    
    systemctl enable nfs
    
  • 在 k8s-master 节点上测试是否挂载成功

    [root@k8s-master ~]# showmount -e 172.16.58.200
    Export list for 172.16.58.200:
    /root/data/nfs 172.16.58.0/24
    
  • 在 k8s-node1 和 k8s-node2 都安装 NFS 服务器,目前是为了 Node 节点可以驱动 NFS 服务器

    # 在Node节点上安装NFS服务,不需要启动
    yum -y install nfs-utils
    
  • 在 k8s-node1 和 k8s-node2 测试是否挂载成功

    showmount -e 172.16.58.200
    
  • 高可用备份方式,在所有节点执行如下的命令

    mount -t  nfs 172.16.58.200:/root/data/nfs /mnt
    

# 3.2 创建 Pod

  • 创建 volume-nfs.yaml 文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: volume-nfs
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          volumeMounts:
            - name: logs-volume #将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
              mountPath: /var/log/nginx
        - name: busybox
          image: busybox:1.30
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "tail -f /logs/error.log"] #初始命令,动态读取指定文件的内容变化
          volumeMounts: #将logs-volume挂载到busybox容器中的对应父母,该目录为/logs
            - name: logs-volume
              mountPath: /logs
      volumes:  #声明volume,name为logs-volume,类型为emptyDir
        - name: logs-volume
          nfs:
            server: 172.16.58.200 #NFS服务器地址
            path: /root/data/nfs #共享文件路径
    
  • 创建 Pod

    [root@k8s-master volume]# kubectl apply -f volume-nfs.yaml 
    pod/volume-nfs created
    
  • 查看 Pod

    [root@k8s-master volume]# kubectl get pod volume-nfs -o wide
    NAME         READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
    volume-nfs   2/2     Running   0          6s    10.244.1.45   k8s-node1   <none>           <none>
    

# 3.3 访问 pod 中的 nginx

curl 10.244.1.45

# 3.4 查看 nfs 服务器上的共享目录

[root@k8s-master volume]# ls /root/data/nfs/
access.log  error.log

# 二、高级存储

前面我们已经学习了使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 NFS。

由于 k8s 支持的存储系统有很多,要求客户全部掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,k8s 引入了 PVPVC 两种资源对象。

  • PV(Persistent Volume) 是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 k8s 管理员进行创建和配置,它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接
  • PVC(Persistent Volume Claim) 是持久化卷声明的意思,是用户对于存储需求的一种声明。换言之,PVC 其实就是用户向 k8s 系统发出的一种资源需求申请

使用了 PV 和 PVC 之后,工作可以得到进一步的提升:

  • 存储:存储工程师维护
  • PV:k8s 管理员维护
  • PVC:k8s 用户维护

image-20210712112423723

# 1. PV

# 1.1 PV 资源清单

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型,和底层正则的存储对应,NFS/CIFS/GlusterFS
    path:
    server:
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes: # 访问模式
    -
  storageClassName: # 存储类别
  persistentVolumeReclaimPolicy: # 回收策略
  • 存储类型

    底层实际存储的类型,k8s 支持多种存储类型,每种存储类型的配置有所不同:

    • NFS
    • CIFS
    • ClusterFS
    • ...
  • 存储能力(capacity)

    目前只支持存储空间的设置(storage=1Gi),不过未来可能会加入 IOPS、吞吐量等指标的配置

  • 访问模式(accessModes)

    用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

    • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
    • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
    • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

    需要注意的是,底层不同的存储类型可能支持的访问模式不同。

  • 回收策略( persistentVolumeReclaimPolicy)

    当 PV 不再被使用之后,对其的处理方式,目前支持三种策略:

    • Retain(保留):保留数据,需要管理员手动清理数据
    • Recycle(回收):清除 PV 中的数据,效果相当于rm -rf /volume/*
    • Delete(删除):和 PV 相连的后端存储完成 volume 的删除操作,常见于云服务器厂商的存储服务

    需要注意的是,底层不同的存储类型可能支持的回收策略不同。

  • 存储类别(storageClassName)

    PV 可以通过 storageClassName 参数指定一个存储类别:

    • 具有特定类型的 PV 只能和请求了该类别的 PVC 进行绑定
    • 未设定类别的 PV 只能和不请求任何类别的 PVC 进行绑定
  • 状态(status)

    一个 PV 的生命周期,可能会处于 4 种不同的阶段:

    • Available(可用):表示可用状态,还未被任何 PVC 绑定
    • Bound(已绑定):表示PV已经被 PVC 绑定
    • Released(已释放):表示 PVC 被删除,但是资源还没有被集群重新释放
    • Failed(失败):表示该 PV 的自动回收失败

# 1.2 准备 NFS 环境

为了方便,就直接在 k8s-master 上搭建了。

  • 创建共享目录

    mkdir -pv /root/data/{pv1,pv2,pv3}
    
  • 授权共享目录的读写权限

    chmod 777 -R /root/data
    
  • 修改 /etc/exports,将共享目录以读写权限暴露给172.16.58.0/24网段中的所有主机

    [root@k8s-master volume]# vim /etc/exports
    [root@k8s-master volume]# cat /etc/exports
    /root/data/nfs 172.16.58.0/24(rw,no_root_squash)
    #添加以下内容
    /root/data/pv1 172.16.58.0/24(rw,no_root_squash)
    /root/data/pv2 172.16.58.0/24(rw,no_root_squash)
    /root/data/pv3 172.16.58.0/24(rw,no_root_squash)
    
  • 重启 NFS 服务

    systemctl restart nfs
    

# 1.3 创建 PV

  • 创建 pv.yaml 文件

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv1
    spec:
      nfs: #存储类型,和底层支持的存储对应
        path: /root/data/pv1
        server: 172.16.58.200
      capacity:
        storage: 1Gi
      accessModes: #访问模式
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain #回收策略
    
    --- 
    
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv2
    spec:
      nfs: #存储类型,和底层支持的存储对应
        path: /root/data/pv2
        server: 172.16.58.200
      capacity:
        storage: 2Gi
      accessModes: #访问模式
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain #回收策略
    
    --- 
    
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv3
    spec:
      nfs: #存储类型,和底层支持的存储对应
        path: /root/data/pv3
        server: 172.16.58.200
      capacity:
        storage: 3Gi
      accessModes: #访问模式
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain #回收策略
    
  • 创建 PV

    [root@k8s-master volume]# kubectl apply -f pv.yaml 
    persistentvolume/pv1 created
    persistentvolume/pv2 created
    persistentvolume/pv3 created
    

# 1.4 查看 PV

[root@k8s-master volume]# kubectl get pv -o wide
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE   VOLUMEMODE
pv1    1Gi        RWX            Retain           Available                                   31s   Filesystem
pv2    2Gi        RWX            Retain           Available                                   31s   Filesystem
pv3    3Gi        RWX            Retain           Available                                   31s   Filesystem

# 2. PVC

# 2.1 PVC 资源清单

PVC 是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,下面是 PVC 的资源清单文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: dev
spec:
  accessModes: # 访客模式
    - 
  selector: #采用标签对PV选择
  storageClassName: #存储类别
  resources: # 请求空间
    requests:
      storage: 5Gi
  • 访客模式(accessModes):用于描述用户应用对存储资源的访问权限

  • 用于描述用户应用对存储资源的访问权限:

    • 选择条件(selector):通过 Label Selector 的设置,可使 PVC 对于系统中已存在的 PV 进行筛选
    • 存储类别(storageClassName):PVC 在定义时可以设定需要的后端存储的类别,只有设置了该 class 的 PV 才能被系统选出
    • 资源请求(resources):描述对存储资源的请求

# 2.2 创建 PVC

  • 编写 pvc.yaml 文件

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc1
    spec:
      accessModes: #访客模式
        - ReadWriteMany
      resources: #资源要求
        requests: 
          storage: 1Gi
    
    ---
    
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc2
    spec:
      accessModes: #访客模式
        - ReadWriteMany
      resources: #资源要求
        requests: 
          storage: 1Gi
    
    ---
    
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc3
    spec:
      accessModes: #访客模式
        - ReadWriteMany
      resources: #资源要求
        requests: 
          storage: 5Gi
    
  • 创建 PVC

    [root@k8s-master volume]# kubectl apply -f pvc.yaml 
    persistentvolumeclaim/pvc1 created
    persistentvolumeclaim/pvc2 created
    persistentvolumeclaim/pvc3 created
    

# 2.3 查看 PVC

[root@k8s-master volume]# kubectl get pvc
NAME   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound     pv1      1Gi        RWX                           41s
pvc2   Bound     pv2      2Gi        RWX                           41s
pvc3   Pending                                                     41s

会发现 pvc3 由于请求的资源(5Gi)大于目前所有提供资源的 PV,所以没法为 pvc3 分配 PV。

# 2.4 查看 PV

[root@k8s-master volume]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
pv1    1Gi        RWX            Retain           Bound       default/pvc1                           7m29s
pv2    2Gi        RWX            Retain           Bound       default/pvc2                           7m29s
pv3    3Gi        RWX            Retain           Available                                          7m29s

可以看到 pv1 和 pv2 都已经被分配了,pv3 还没有被分配。

# 2.5 创建 Pod

  • 创建 pvc-pod.yaml 文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pvc-pod1
    spec:
      containers:
        - name: busybox
          image: busybox:1.30
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "while true; do echo pvc-pod1 >> /root/out.txt; sleep 10; done;"]
          volumeMounts:
            - name: volume
              mountPath: /root/
      volumes:
        - name: volume
          persistentVolumeClaim:
            claimName: pvc1
            readOnly: false
    
    ---
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: pvc-pod2
    spec:
      containers:
        - name: busybox
          image: busybox:1.30
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "while true; do echo pvc-pod2 >> /root/out.txt; sleep 10; done;"]
          volumeMounts:
            - name: volume
              mountPath: /root/
      volumes:
        - name: volume
          persistentVolumeClaim:
            claimName: pvc2
            readOnly: false
    
  • 创建 Pod

    [root@k8s-master volume]# kubectl apply -f pvc-pod.yaml 
    pod/pvc-pod1 created
    pod/pvc-pod2 created
    
  • 查看 Pod

    [root@k8s-master volume]# kubectl get pod pvc-pod1 -o wide
    NAME       READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
    pvc-pod1   1/1     Running   0          7s    10.244.3.21   k8s-node2   <none>           <none>
    [root@k8s-master volume]# kubectl get pod pvc-pod2 -o wide
    NAME       READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
    pvc-pod2   1/1     Running   0          17s   10.244.1.46   k8s-node1   <none>           <none>
    

# 2.6 查看 PVC

[root@k8s-master volume]# kubectl get pvc
NAME   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound     pv1      1Gi        RWX                           9m25s
pvc2   Bound     pv2      2Gi        RWX                           9m25s
pvc3   Pending                                                     9m25s

# 2.7 查看 PV

[root@k8s-master volume]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
pv1    1Gi        RWX            Retain           Bound       default/pvc1                           15m
pv2    2Gi        RWX            Retain           Bound       default/pvc2                           15m
pv3    3Gi        RWX            Retain           Available                                          15m

# 2.8 查看 NFS 中的文件存储

[root@k8s-master volume]# cat /root/data/pv1/out.txt 
pvc-pod1
pvc-pod1
pvc-pod1
...
[root@k8s-master volume]# cat /root/data/pv2/out.txt 
pvc-pod2
pvc-pod2
pvc-pod2
...

# 3. 生命周期

image-20210712120351532

PVC 和 PV 是一一对应的,PV 和 PVC 之间的相互作用遵循如下的生命周期:

  • 资源供应

    管理员手动创建底层存储和 PV。

  • 资源绑定

    • 用户创建 PVC,k8s 负责根据 PVC 声明去寻找 PV,并绑定在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在以存在的 PV 中选择一个满足条件的。
      • 一旦找到,就将该 PV 和用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了,PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再和其他的 PVC 进行绑定了
      • 如果找不到,PVC 就会无限期的处于 Pending 状态,直到系统管理员创建一个符合其要求的PV
  • 资源使用

    用户可以在 Pod 中像 volume 一样使用 PVC,Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用。

  • 资源释放

    用户删除 PVC 来释放 PV。

    当存储资源使用完毕后,用户可以删除 PVC,和该 PVC 绑定的 PV 将会标记为“已释放”,但是还不能立刻和其他的 PVC 进行绑定。通过之前 PVC 写入的数据可能还留在存储设备上,只有在清除之后该 PV 才能再次使用。

  • 资源回收

    k8s 根据 PV 设置的回收策略进行资源的回收。

    对于 PV,管理员可以设定回收策略,用于设置与之绑定的 PVC 释放资源之后如何处理遗留数据的问题。只有 PV 的存储空间完成回收,才能供新的 PVC 绑定和使用。

# 三、配置存储

# 1. ConfigMap

ConfigMap 是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。

# 1.1 ConfigMap 资源清单

apiVersion: v1
kind: ConfigMap
metadata:
  name: configMap
  namespace: dev
data: # <map[string]string>
  info:
  	username: admin
  	password: 123456

# 1.2 创建 ConfigMap

  • 编写 configmap.yaml 文件

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: configmap
      namespace: dev
    data:
      info:
        username:admin
        password:123456
    
  • 创建 ConfigMap

    kubectl create -f configmap.yaml
    

# 1.3 创建 Pod

  • 编写 pod-configmap.yaml 文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-configmap
      namespace: dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          volumeMounts:
            - mountPath: /configmap/config
              name: config
      volumes:
        - name: config
          configMap:
            name: configmap
    
  • 创建 Pod

    kubectl create -f pod-configmap.yaml
    
  • 查看 Pod

    kubectl get pod pod-configmap -n dev
    

# 1.4 查看配置

  • 进入容器

    kubectl exec -it pod-configmap -n dev /bin/sh
    
  • 查看配置

    cd /configmap/config
    
    ls
    
    more info
    

ConfigMap 中的 key 映射为一个文件,value 映射为文件中的内容。如果更新了 ConfigMap 中的内容,容器中的值也会动态更新。

# 2. Secret

在 k8s 中,还存在一种和 ConfigMap 非常类似的对象,称为 Secret 对象,它主要用来存储敏感信息,例如密码、密钥、证书等等。

# 2.1 Secret 资源清单

apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: dev
type: Opaque # Secret 类型
data: #加密数据
  username: YWRtaW4= 
  password: MTIzNDU2
stringData: #明文数据,stringData会覆盖data
	username: admin #会被自动加密,默认 base64
	password: 123456
  • type

    内置类型 用法
    Opaque 用户定义的任意数据
    kubernetes.io/service-account-token 服务账号令牌
    kubernetes.io/dockercfg ~/.dockercfg 文件的序列化形式
    kubernetes.io/dockerconfigjson ~/.docker/config.json 文件的序列化形式
    kubernetes.io/basic-auth 用于基本身份认证的凭据
    kubernetes.io/ssh-auth 用于 SSH 身份认证的凭据
    kubernetes.io/tls 用于 TLS 客户端或者服务器端的数据
    bootstrap.kubernetes.io/token 启动引导令牌数据
    • Opaque Secret

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

      [root@k8s-master secret]# kubectl create secret generic empty-secret
      secret/empty-secret created
      [root@k8s-master secret]# kubectl get secret empty-secret
      NAME           TYPE     DATA   AGE
      empty-secret   Opaque   0      8s
      

      DATA 列显示 Secret 中保存的数据条目个数。 在这个例子种,0 意味着我们刚刚创建了一个空的 Secret。

    • 服务账号令牌 Secret

      类型为 kubernetes.io/service-account-token 的 Secret 用来存放标识某 服务账号的令牌。使用这种 Secret 类型时,你需要确保对象的注解 kubernetes.io/service-account-name 被设置为某个已有的服务账号名称。 某个 Kubernetes 控制器会填写 Secret 的其它字段,例如 kubernetes.io/service-account.uid 注解以及 data 字段中的 token 键值,使之包含实际的令牌内容。

      apiVersion: v1
      kind: Secret
      metadata:
        name: secret-sa-sample
        annotations:
          kubernetes.io/service-account.name: "sa-name"
      type: kubernetes.io/service-account-token
      data:
        # 你可以像 Opaque Secret 一样在这里添加额外的键/值偶对
        extra: YmFyCg==
      

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

    • Docker 配置 Secret

      你可以使用下面两种 type 值之一来创建 Secret,用以存放访问 Docker 仓库 来下载镜像的凭据。

      • kubernetes.io/dockercfg
      • kubernetes.io/dockerconfigjson

      kubernetes.io/dockercfg 是一种保留类型,用来存放 ~/.dockercfg 文件的 序列化形式。该文件是配置 Docker 命令行的一种老旧形式。 使用此 Secret 类型时,你需要确保 Secret 的 data 字段中包含名为 .dockercfg 的主键,其对应键值是用 base64 编码的某 ~/.dockercfg 文件的内容。

      类型 kubernetes.io/dockerconfigjson 被设计用来保存 JSON 数据的序列化形式, 该 JSON 也遵从 ~/.docker/config.json 文件的格式规则,而后者是 ~/.dockercfg 的新版本格式。 使用此 Secret 类型时,Secret 对象的 data 字段必须包含 .dockerconfigjson 键,其键值为 base64 编码的字符串包含 ~/.docker/config.json 文件的内容。

      下面是一个 kubernetes.io/dockercfg 类型 Secret 的示例:

      apiVersion: v1
      kind: Secret
      metadata:
        name: secret-dockercfg
      type: kubernetes.io/dockercfg
      data:
        .dockercfg: |
              "<base64 encoded ~/.dockercfg file>"
      
    • 基本身份认证 Secret

      kubernetes.io/basic-auth 类型用来存放用于基本身份认证所需的凭据信息。 使用这种 Secret 类型时,Secret 的 data 字段必须包含以下两个键:

      • username: 用于身份认证的用户名;
      • password: 用于身份认证的密码或令牌。

      以上两个键的键值都是 base64 编码的字符串。 当然你也可以在创建 Secret 时使用 stringData 字段来提供明文形式的内容。 下面的 YAML 是基本身份认证 Secret 的一个示例清单:

      apiVersion: v1
      kind: Secret
      metadata:
        name: secret-basic-auth
      type: kubernetes.io/basic-auth
      stringData:
        username: admin
        password: t0p-Secret
      

      提供基本身份认证类型的 Secret 仅仅是出于用户方便性考虑。 你也可以使用 Opaque 类型来保存用于基本身份认证的凭据。 不过,使用内置的 Secret 类型的有助于对凭据格式进行归一化处理,并且 API 服务器确实会检查 Secret 配置中是否提供了所需要的主键。

    • SSH 身份认证 Secret

      Kubernetes 所提供的内置类型 kubernetes.io/ssh-auth 用来存放 SSH 身份认证中 所需要的凭据。使用这种 Secret 类型时,你就必须在其 data (或 stringData) 字段中提供一个 ssh-privatekey 键值对,作为要使用的 SSH 凭据。

      下面的 YAML 是一个 SSH 身份认证 Secret 的配置示例:

      apiVersion: v1
      kind: Secret
      metadata:
        name: secret-ssh-auth
      type: kubernetes.io/ssh-auth
      data:
        # 此例中的实际数据被截断
        ssh-privatekey: |
                MIIEpQIBAAKCAQEAulqb/Y ..
      

      提供 SSH 身份认证类型的 Secret 仅仅是出于用户方便性考虑。 你也可以使用 Opaque 类型来保存用于 SSH 身份认证的凭据。 不过,使用内置的 Secret 类型的有助于对凭据格式进行归一化处理,并且 API 服务器确实会检查 Secret 配置中是否提供了所需要的主键。

    • TLS Secret

      Kubernetes 提供一种内置的 kubernetes.io/tls Secret 类型,用来存放证书 及其相关密钥(通常用在 TLS 场合)。 此类数据主要提供给 Ingress 资源,用以终结 TLS 链接,不过也可以用于其他 资源或者负载。当使用此类型的 Secret 时,Secret 配置中的 data (或 stringData)字段必须包含 tls.keytls.crt 主键,尽管 API 服务器 实际上并不会对每个键的取值作进一步的合法性检查。

      下面的 YAML 包含一个 TLS Secret 的配置示例:

      apiVersion: v1
      kind: Secret
      metadata:
        name: secret-tls
      type: kubernetes.io/tls
      data:
        # 此例中的数据被截断
        tls.crt: |
              MIIC2DCCAcCgAwIBAgIBATANBgkqh ...
        tls.key: |
              MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...
      
    • 启动引导令牌 Secret

      通过将 Secret 的 type 设置为 bootstrap.kubernetes.io/token 可以创建 启动引导令牌类型的 Secret。这种类型的 Secret 被设计用来支持节点的启动引导过程。 其中包含用来为周知的 ConfigMap 签名的令牌。

      启动引导令牌 Secret 通常创建于 kube-system 名字空间内,并以 bootstrap-token-<令牌 ID> 的形式命名;其中 <令牌 ID> 是一个由 6 个字符组成 的字符串,用作令牌的标识。

      以 Kubernetes 清单文件的形式,某启动引导令牌 Secret 可能看起来像下面这样:

      apiVersion: v1
      kind: Secret
      metadata:
        name: bootstrap-token-5emitj
        namespace: kube-system
      type: bootstrap.kubernetes.io/token
      data:
        auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
        expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
        token-id: NWVtaXRq
        token-secret: a3E0Z2lodnN6emduMXAwcg==
        usage-bootstrap-authentication: dHJ1ZQ==
        usage-bootstrap-signing: dHJ1ZQ==
      

      启动引导令牌类型的 Secret 会在 data 字段中包含如下主键:

      • token-id:由 6 个随机字符组成的字符串,作为令牌的标识符。必需。
      • token-secret:由 16 个随机字符组成的字符串,包含实际的令牌机密。必需。
      • description:供用户阅读的字符串,描述令牌的用途。可选。
      • expiration:一个使用 RFC3339 来编码的 UTC 绝对时间,给出令牌要过期的时间。可选。
      • usage-bootstrap-<usage>:布尔类型的标志,用来标明启动引导令牌的其他用途。
      • auth-extra-groups:用逗号分隔的组名列表,身份认证时除被认证为 system:bootstrappers 组之外,还会被添加到所列的用户组中。

      上面的 YAML 文件可能看起来令人费解,因为其中的数值均为 base64 编码的字符串。 实际上,你完全可以使用下面的 YAML 来创建一个一模一样的 Secret:

      apiVersion: v1
      kind: Secret
      metadata:
        # 注意 Secret 的命名方式
        name: bootstrap-token-5emitj
        # 启动引导令牌 Secret 通常位于 kube-system 名字空间
        namespace: kube-system
      type: bootstrap.kubernetes.io/token
      stringData:
        auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
        expiration: "2020-09-13T04:39:10Z"
        # 此令牌 ID 被用于生成 Secret 名称
        token-id: "5emitj"
        token-secret: "kq4gihvszzgn1p0r"
        # 此令牌还可用于 authentication (身份认证)
        usage-bootstrap-authentication: "true"
        # 且可用于 signing (证书签名)
        usage-bootstrap-signing: "true"
      

# 2.2 使用 base64 对数据进行编码

  • 加密 username

    [root@k8s-master ~]# echo -n "admin" | base64
    YWRtaW4=
    
  • 加密 password

    [root@k8s-master ~]# echo -n "123456" | base64
    MTIzNDU2
    

# 2.3 创建 Secret

  • 编写 secret.yaml 文件

    apiVersion: v1
    kind: Secret
    metadata:
      name: secret
      namespace: dev
    type: Opaque
    data:
      username: YWRtaW4=
      password: MTIzNDU2
    
  • 创建 Secret

    kubectl create -f secret.yaml
    

# 2.4 自动编码

  • 上面的方式是先手动将数据进行编码,其实也可以使用直接编写数据,将数据编码交给 k8s。
apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: dev
type: Opaque
stringData:
  username: "admin"
  password: "123456"

如果同时使用 data 和 stringData,那么 data 会被忽略。

# 2.5 查看 Secret

[root@k8s-master secret]# kubectl get secret secret -n dev
NAME     TYPE     DATA   AGE
secret   Opaque   2      73s

[root@k8s-master secret]# kubectl describe secret secret -n dev
Name:         secret
Namespace:    dev
Labels:       <none>
Annotations:  
Type:         Opaque

Data
====
username:  5 bytes
password:  6 bytes

# 2.6 创建 Pod

  • 编写 pod-secret.yaml 文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret
      namespace: dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          volumeMounts:
            - mountPath: /secret/config
              name: config
      volumes:
        - name: config
          secret:
            secretName: secret
    
  • 创建 Pod

    kubectl apply -f pod-secret.yaml 
    
  • 查看 Pod

    kubectl get pod pod-secret -n dev
    

# 2.7 进入 Pod 查看配置信息

  • 进入容器,查看 Secret 信息,发现已经自动解码了
[root@k8s-master secret]# kubectl exec -it pod-secret -n dev /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
# ls /secret/config
password  username
# more /secret/config/username
admin
# more /secret/config/password
123456
# 

# 2.8 Secret 用途

imagePullSecret:Pod 拉取私有镜像仓库的时使用的账户密码,会传递给 kubelet,然后 kubelet 就可以拉取有密码的仓库里面的镜像。

kubectl create secret docker-registry docker-harbor-registrykey --docker-server=IP:PORT \
          --docker-username=username --docker-password=password \
          --docker-email=xxxxx@qq.com
上次更新: 7/13/2021, 11:54:08 AM