# 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 中通过命令将日志内容读到控制台。
# 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: {}
创建 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
# 1.2 访问 Pod 中的 nginx
访问几次 Pod 中的 nginx,使其有日志输出
curl 10.244.3.20
# 2. hostPath
很明显 emptyDir 中的数据不会被持久化,它会随着 Pod 的结束而销毁,如果想要简单的将数据持久化到主机中,可以选择 hostPath。
hostPath 就是将 Node 主机中的一个实际目录挂载到 Pod 中,以供容器使用,这样的设计就可以保证 Pod 销毁了,但是数据依旧可以保存在 Node 主机上。
# 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 节点上,又会出现问题,此时需要准备单独的网络存储系统,比较常用的是 NFS 和 CIFS。
NFS(network file system) 是一个网络文件存储系统,可以搭建一台 NFS 服务器,然后将 Pod 中的存储直接连接到 NFS 系统上,这样,无论 Pod 在节点上怎么转移,只要 Node 和 NFS 的对接没有问题,数据就可以成功访问。
# 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 引入了 PV 和 PVC 两种资源对象。
- PV(Persistent Volume) 是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 k8s 管理员进行创建和配置,它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接
- PVC(Persistent Volume Claim) 是持久化卷声明的意思,是用户对于存储需求的一种声明。换言之,PVC 其实就是用户向 k8s 系统发出的一种资源需求申请
使用了 PV 和 PVC 之后,工作可以得到进一步的提升:
- 存储:存储工程师维护
- PV:k8s 管理员维护
- PVC:k8s 用户维护
# 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. 生命周期
PVC 和 PV 是一一对应的,PV 和 PVC 之间的相互作用遵循如下的生命周期:
资源供应
管理员手动创建底层存储和 PV。
资源绑定
- 用户创建 PVC,k8s 负责根据 PVC 声明去寻找 PV,并绑定在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在以存在的 PV 中选择一个满足条件的。
- 一旦找到,就将该 PV 和用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了,PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再和其他的 PVC 进行绑定了
- 如果找不到,PVC 就会无限期的处于 Pending 状态,直到系统管理员创建一个符合其要求的PV
- 用户创建 PVC,k8s 负责根据 PVC 声明去寻找 PV,并绑定在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在以存在的 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.key
和tls.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