07-持久化存储和StorageClass
在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
1、持久化存储
我们可以通过下面的命令查询Kubernetes的Pod支持哪些持久化存储方案:
kubectl explain pods.spec.volumes
awsElasticBlockStore
:AWSElasticBlockStore 表示一个 AWS 磁盘资源,它附加到 kubelet 的主机,然后暴露给 pod。azureDisk
:AzureDisk 表示主机上的 Azure 数据磁盘挂载,并将挂载绑定到 pod。azureFile
:AzureFile 表示主机上的 Azure 文件服务挂载,并将挂载绑定到 pod。cephfs
:CephFS 表示主机上的 Ceph FS 挂载,共享 pod 的生命周期。cinder
:Cinder 表示附加并安装在 kubelets 主机上的 cinder 卷。configMap
:ConfigMap 表示应该填充此卷的 configMap。csi
:CSI(容器存储接口)表示由某些外部 CSI 驱动程序(Beta 功能)处理的临时存储。downwardAPI
:DownwardAPI 表示关于应该填充此卷的 pod 的向下 API。emptyDir
:EmptyDir 表示共享 pod 生命周期的临时目录。ephemeral
:Ephemeral 表示由集群存储驱动程序(Alpha 功能)处理的卷。 卷的生命周期与定义它的 pod 相关联——它将在 pod 启动之前创建,并在 pod 被删除时删除。fc
:FC 表示连接到 kubelet 主机然后暴露给 pod 的光纤通道资源。flexVolume
:FlexVolume 表示使用基于 exec 的插件配置/附加的通用卷资源。flocker
:Flocker 表示附加到 kubelet 主机的 Flocker 卷。 这取决于正在运行的 Flocker 控制服务。gcePersistentDisk
:GCEPersistentDisk 表示一个 GCE 磁盘资源,它附加到 kubelet 的主机上,然后暴露给 pod。gitRepo
:GitRepo 代表特定版本的 git 存储库。GitRepo 已弃用
。 要使用 git repo 配置容器,请将 EmptyDir 挂载到使用 git 克隆 repo 的 InitContainer 中,然后将 EmptyDir 挂载到 Pod 的容器中。glusterfs
:Glusterfs 表示主机上的 Glusterfs 挂载,共享一个 pod 的生命周期。hostPath
:HostPath 表示主机上直接暴露给容器的预先存在的文件或目录。 这通常用于系统代理或其他允许查看主机的特权事物。 大多数容器不需要这个。iscsi
:ISCSI 代表一个 ISCSI 磁盘资源,它连接到 kubelet 的主机,然后暴露给 pod。name
:卷的名称。 必须是 DNS_LABEL 并且在 pod 中是唯一的。nfs
:NFS 表示共享 pod 生命周期的主机上的 NFS 挂载。persistentVolumeClaim
:PersistentVolumeClaimVolumeSource 表示对同一命名空间中的 PersistentVolumeClaim 的引用。photonPersistentDisk
:PhotonPersistentDisk 表示附加并挂载在 kubelets 主机上的 PhotonController 永久磁盘。portworxVolume
:PortworxVolume 表示连接并挂载在 kubelets 主机上的 portworx 卷。projected
:一站式资源秘密、配置映射和向下 API 的项目。quobyte
:Quobyte 表示共享 pod 生命周期的主机上的 Quobyte 挂载。rbd
:RBD 表示在共享 pod 生命周期的主机上挂载的 Rados 块设备。scaleIO
:ScaleIO 表示附加并安装在 Kubernetes 节点上的 ScaleIO 持久卷。secret
:Secret 表示应填充此卷的密钥。storageos
:StorageOS 表示附加并安装在 Kubernetes 节点上的 StorageOS 卷。vsphereVolume
:VsphereVolume 表示附加并安装在 kubelets 主机上的 vSphere 卷。
其中比较常用的挂在卷类型有:
- emptyDir
- hostPath
- nfs
- persistentVolumeClaim
- glusterfs
- cephfs
- configMap
- secret
想要使用存储卷,需要经历下面两个步骤:
- 定义pod的volume,这个volume指明它要关联到哪个存储上的
- 在容器中要使用volumemounts挂载对应的存储
1.1 emptyDir
emptyDir类型的Volume是在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。
官方文档:https://kubernetes.io/docs/concepts/storage/volumes#emptydir
示例:创建一个Pod挂载emptyDir:
emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-emptydir
namespace: default
labels:
app: pod-emptydir
spec:
containers:
- name: pod-emptydir
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /cache
name: cache-volume
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
restartPolicy: Always
volumes:
- name: cache-volume
emptyDir:
{}
更新资源清单:
kubectl apply -f emptydir.yaml
查看Pod描述信息,可以看到对应的挂载卷为cache-volume
,类型为EmptyDir
:
然后去Pod所在的节点查看对应的Volume挂在卷:
kubectl get pods -l app=pod-emptydir -o wide
kubectl get pods -l app=pod-emptydir -o yaml | grep uid
获得对应Pod所在的节点以及uid信息:
在对应的节点上查看Pod相关信息:
tree /var/lib/kubelet/pods/18168545-1292-4086-aaba-0bf71edadc0e
右下图可知,临时目录在对应工作节点的/var/lib/kubelet/pods/18168545-1292-4086-aaba-0bf71edadc0e/volumes/
目录下:
1.2 hostPath
hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。
示例:创建一个Pod挂载hostPath类型的存储卷:
hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
namespace: default
labels:
app: pod-hostpath
spec:
containers:
- name: pod-hostpath-nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /test-nginx
name: hostpath-volume
- name: pod-hostpath-tomcat
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /test-tomcat
name: hostpath-volume
restartPolicy: Always
volumes:
- name: hostpath-volume
hostPath:
path: /data1
type: DirectoryOrCreate
更新资源清单:
kubectl apply -f hostpath.yaml
查看Pod被调度到了哪个节点:
get pods -l app=pod-hostpath -o wide
结果如下:
前往对应的工作节点,查看是否创建了/data1
目录:
ll /data1/
结果如下:
进入/data1
目录,创建一个文件夹,并且进入对应的Pod中检查:
# 在对应的工作节点上执行
cd /data1 && mkdir -p testDir
结果如下:
# 到控制节点上执行进入Pod并检查的操作
kubectl exec -it pod-hostpath -c pod-hostpath-nginx -- /bin/bash
ls -l /test-nginx
kubectl exec -it pod-hostpath -c pod-hostpath-tomcat -- /bin/bash
ls -l /test-tomcat
分别进入两个Pod中检查,都存在testDir
这个目录:
由此可知,pod-hostpath-nginx
和pod-hostpath-tomcat
两个Pod这两个容器是共享存储卷的。
hostPath类型存储卷的缺点:
- 单节点:pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失
要解决单节点问题,可以使用分布式存储来解决,例如:nfs、cephfs、glusterfs等。
1.3 nfs
1.3.1 搭建nfs服务
NFS服务端搭建:
# 1. nfs服务端安装nfs-utils
yum install -y nfs-utils
# 2. 创建NFS共享的目录
mkdir -pv /data/volumes
# 3. 配置nfs共享目录
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
EOF
# 4. 启动nfs服务
systemctl start nfs.service
systemctl status nfs.service
systemctl enable nfs.service
NFS客户端搭建:
在Kubernetes的各个工作节点上操作:
yum install -y nfs-utils
systemctl enable nfs.service
# 手动挂载nfs
mkdir -p /test
mount 192.168.126.40:/data/volumes /test/
df -Th
挂载成功显示如下:
# 手动卸载
umount /test
df -Th
卸载成功:
1.3.2 创建Pod挂载nfs作为存储卷
Kubernetes挂载NFS的官方文档:https://kubernetes.io/zh/docs/concepts/storage/volumes/#nfs
nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-nfs-volume
namespace: default
labels:
app: test-nfs-volume
spec:
containers:
- name: nfs-volume-nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-volume
restartPolicy: Always
volumes:
- name: nfs-volume
nfs:
path: /data/volumes
server: 192.168.126.40
更新资源清单:
kubectl apply -f nfs.yaml
检查Pod是否创建成功:
kubectl get pods -l app=test-nfs-volume -o wide
结果如下:
进入到NFS共享目录下,创建一个index.html文件:
# 在nfs-server服务器上操作
cd /data/volumes/
echo 'NFS Volume Successed!' > index.html
效果如下:
通过curl Pod IP查看结果:
curl 10.244.85.208
结果如下:
NFS挂载成功,nfs支持多个客户端挂载,可以创建多个pod,挂载同一个nfs服务器共享出来的目录;但是nfs如果宕机了,数据也就丢失了,所以需要使用分布式存储,常见的分布式存储有glusterfs和cephfs。
2、PVC
官方文档:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
2.1 什么是PV?
PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像pod是k8s集群资源一样。 PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。
2.2 什么是PVC?
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。
2.3 PVC和PV工作原理
PV是群集中的资源。 PVC是对这些资源的请求。
PV和PVC之间的相互作用遵循以下生命周期。
2.3.1 pv的供应方式
- 静态的:集群管理员创建了许多PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用。
- 动态的:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,群集可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses,PVC必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。
2.3.2 绑定
用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。
2.3.3 使用
- 需要找一个存储服务器,把它划分成多个存储空间;
- k8s管理员可以把这些存储空间定义成多个pv;
- 在pod中使用pvc类型的存储卷之前需要先创建pvc,通过定义需要使用的pv的大小和对应的访问模式,找到合适的pv;
- pvc被创建之后,就可以当成存储卷来使用了,我们在定义pod时就可以使用这个pvc的存储卷;
- pvc和pv它们是一一对应的关系,pv如果被pvc绑定了,就不能被其他pvc使用了;
- 我们在创建pvc的时候,应该确保和底下的pv能绑定,如果没有合适的pv,那么pvc就会处于pending状态。
2.3.4 回收策略
当我们创建pod时如果使用pvc做为存储卷,那么它会和pv绑定,当删除pod,pvc和pv绑定就会解除,解除之后和pvc绑定的pv卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:
- Retain
- Recycle (不推荐使用,1.15可能被废弃了)
- Delete
从其声明中释放持久卷时会发生什么。 有效选项是 Retain(手动创建的 PersistentVolumes 的默认值)、Delete(动态配置的 PersistentVolumes 的默认值)和 Recycle(已弃用)。 此 PersistentVolume 下的卷插件必须支持回收。
2.3.4.1 Retain
当删除pvc的时候,pv仍然存在,处于released状态,但是它不能被其他pvc绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略。
2.3.4.2 Delete
删除pvc时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产。
2.4 通过PVC给Pod提供持久化存储卷
2.4.1 创建NFS共享目录
# 在nfs-server服务器上操作
mkdir -p /data/volume_test/v{1,2,3,4,5,6,7,8,9,10}
# 配置nfs
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v1 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v2 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v3 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v4 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v5 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v6 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v7 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v8 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v9 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v10 192.168.126.40/24(rw,no_root_squash)
EOF
# 重新加载配置
exportfs -arv
2.4.2 PV资源清单
通过kubectl explain pv
命令可以查看pv资源对象的字段和配置信息。
apiVersion
:pv对象的API组和版本信息。当前版本信息为v1
,可以通过kubectl explain pv.apiVersion
查看当前Kubernetes版本的API版本信息。kind
:资源对象类型,PersistentVolume
,可以通过kubectl explain pv.kind
查询。metadata
:PV资源对象的元数据信息name
:PV对象的名称namespace
:PV资源对象所属的名称空间
spec
:PV资源对象的详细配置accessModes
:AccessModes 包含可以挂载卷的访问方式。参考:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#access-modesReadWriteOnce
:卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。ReadOnlyMany
:卷可以被多个节点以只读方式挂载。ReadWriteMany
:卷可以被多个节点以读写方式挂载。ReadWriteOncePod
:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
awsElasticBlockStore
:AWSElasticBlockStore 表示一个 AWS 磁盘资源,它附加到 kubelet 的主机,然后暴露给 pod。azureDisk
:AzureDisk 表示主机上的 Azure 数据磁盘挂载,并将挂载绑定到 pod。azureFile
:AzureFile 表示主机上的 Azure 文件服务挂载,并将挂载绑定到 pod。capacity
:持久卷的资源和容量的描述。参考:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#capacity 和 https://kubernetes.io/zh/docs/reference/glossary/?all=true#term-quantitystorage
:PV存储卷的大小。例如:1Gi
。
cephfs
:CephFS 表示主机上的 Ceph FS 挂载,共享 pod 的生命周期。cinder
:Cinder 表示附加并安装在 kubelets 主机上的 cinder 卷。claimRef
:ClaimRef 是 PersistentVolume 和 PersistentVolumeClaim 之间双向绑定的一部分。 绑定时预计为非零。 claim.VolumeName 是 PV 和 PVC 之间的权威绑定。csi
:CSI(容器存储接口)表示由某些外部 CSI 驱动程序(Beta 功能)处理的临时存储。fc
:FC 表示连接到 kubelet 主机然后暴露给 pod 的光纤通道资源。flexVolume
:FlexVolume 表示使用基于 exec 的插件配置/附加的通用卷资源。flocker
:Flocker 表示附加到 kubelet 主机的 Flocker 卷。 这取决于正在运行的 Flocker 控制服务。gcePersistentDisk
:GCEPersistentDisk 表示一个 GCE 磁盘资源,它附加到 kubelet 的主机上,然后暴露给 pod。glusterfs
:Glusterfs 表示主机上的 Glusterfs 挂载,共享一个 pod 的生命周期。hostPath
:HostPath 表示主机上直接暴露给容器的预先存在的文件或目录。 这通常用于系统代理或其他允许查看主机的特权事物。 大多数容器不需要这个。iscsi
:ISCSI 代表一个 ISCSI 磁盘资源,它连接到 kubelet 的主机,然后暴露给 pod。local
:Local 表示具有节点亲和性的直连存储。mountOptions
:挂载选项列表,例如ro
、soft
。nfs
:NFS 表示主机上的 NFS 挂载。 由管理员提供。nodeAffinity
:NodeAffinity 定义了限制可以从哪些节点访问此卷的约束。 此字段影响使用此卷的 pod 的调度。persistentVolumeReclaimPolicy
:PV回收策略,从其声明中释放持久卷时会发生什么。 有效选项是 Retain(手动创建的 PersistentVolumes 的默认值)、Delete(动态配置的 PersistentVolumes 的默认值)和 Recycle(已弃用)。 此 PersistentVolume 下的卷插件必须支持回收。photonPersistentDisk
:PhotonPersistentDisk 表示附加并挂载在 kubelets 主机上的 PhotonController 永久磁盘。portworxVolume
:PortworxVolume 表示连接并挂载在 kubelets 主机上的 portworx 卷。quobyte
:Quobyte 表示共享 pod 生命周期的主机上的 Quobyte 挂载。rbd
:RBD 表示在共享 pod 生命周期的主机上挂载的 Rados 块设备。scaleIO
:ScaleIO 表示附加并安装在 Kubernetes 节点上的 ScaleIO 持久卷。storageClassName
:此持久卷所属的 StorageClass 的名称。 空值表示该卷不属于任何 StorageClass。storageos
:StorageOS 表示附加并安装在 Kubernetes 节点上的 StorageOS 卷。volumeMode
:volumeMode 定义卷是打算与格式化的文件系统一起使用还是保持在原始块状态。 当规范中不包含文件系统的值时,它是隐含的。vsphereVolume
:VsphereVolume 表示附加并安装在 kubelets 主机上的 vSphere 卷。
status
:Kubernetes创建完PV之后自动生成的状态信息,无法手动修改。
2.4.3 创建PV
pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
namespace: default
spec:
capacity:
storage: 1Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce"]
nfs:
path: /data/volume_test/v1 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
namespace: default
spec:
capacity:
storage: 2Gi # pv的存储空间容量
accessModes: ["ReadWriteMany"]
nfs:
path: /data/volume_test/v2 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v3
namespace: default
spec:
capacity:
storage: 3Gi # pv的存储空间容量
accessModes: ["ReadOnlyMany"]
nfs:
path: /data/volume_test/v3 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v4
namespace: default
spec:
capacity:
storage: 4Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v4 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v5
namespace: default
spec:
capacity:
storage: 5Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v5 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v6
namespace: default
spec:
capacity:
storage: 6Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v6 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v7
namespace: default
spec:
capacity:
storage: 7Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v7 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v8
namespace: default
spec:
capacity:
storage: 8Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v8 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v9
namespace: default
spec:
capacity:
storage: 9Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v9 # 把nfs的存储空间创建成pv
server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v10
namespace: default
spec:
capacity:
storage: 10Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce", "ReadWriteMany"]
nfs:
path: /data/volume_test/v10 # 把nfs的存储空间创建成pv
server: 192.168.126.40
更新资源清单:
kubectl apply -f pv.yaml
Kubernetes会自动创建pv:
STATUS
为Available
表示PV是可用的。
2.4.4 创建PVC绑定PV
pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc1
namespace: default
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 2Gi
更新资源清单:
kubectl apply -f pvc.yaml
创建完成之后,查看PVC和哪个PV绑定成功了:
STATUS
为Bound
表示已经被绑定了,CLAIN
表示绑定了哪一个PVC。
2.4.5 创建Pod挂载PVC
pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pvc
namespace: default
labels:
app: pod-pvc
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nginx-html
restartPolicy: Always
volumes:
- name: nginx-html
persistentVolumeClaim:
claimName: my-pvc1
更新资源清单:
kubectl apply -f pod-pvc.yaml
查看Pod是否成功创建:
到nfs-serve节点上,在对应的共享路径下创建index.html
:
cd /data/volume_test/v2/
echo 'PVC my-pvc1 Mounted Successed!' > index.html
效果如下:
然后返回控制节点,访问Pod IP:
PV
和PVC
使用注意事项:
- 我们每次创建pvc的时候,需要事先有划分好的pv,这样可能不方便,那么可以在创建pvc的时候直接动态创建一个pv这个存储类,pv事先是不存在的。
- pvc和pv绑定,如果使用默认的回收策略retain,那么删除pvc之后,pv会处于released状态,我们想要继续使用这个pv,需要手动删除pv,kubectl delete pv pv_name,删除pv,不会删除pv里的数据,当我们重新创建pvc时还会和这个最匹配的pv绑定,数据还是原来数据,不会丢失。
3、Kubernetes存储类StorageClass
上面提到的PV和PVC绑定的模式都需要提前创建好PV,然后再由PVC和PV进行绑定,但是如果成千上万个PVC需要绑定PV,那么我们需要手动创建深千上万个PV,对于运维人员来说维护成本很高。所以Kubernetes提供了一种自动创建PV的机制,叫做StorageClass,它的作用就是创建PV模板。Kubernetes管理员提供创建StorageClass可以动态生成一个存储卷PV供PVC使用。
每个StorageClass都包含字段provisioner,parameters和reclaimPolicy。
具体来说,StorageClass会定义以下两个部分:
- PV的属性,比如PV存储的大小、类型、名称等
- 创建这种PV需要使用的存储插件,比如Ceph、NFS、Glusterfs等等
有了这两部分信息,Kubernetes就能够根据用户提交的PVC,找到对应的StorageClass,然后Kubernetes就会调用 StorageClass声明的存储插件,创建出需要的PV。
3.1 StorageClass资源清单
通过命令kubectl explain storageclass
查看StorageClass定义所需的字段和配置信息。
-
allowVolumeExpansion
:AllowVolumeExpansion 显示存储类是否允许卷扩展。布尔类型,是否许卷扩展,PersistentVolume 可以配置成可扩展。将此功能设置为true时,允许用户通过编辑相应的 PVC 对象来调整卷大小。当基础存储类的allowVolumeExpansion字段设置为 true 时,以下类型的卷支持卷扩展。此功能仅用于扩容卷,不能用于缩小卷。
-
allowedTopologies
:限制可以动态供应卷的节点拓扑。 每个卷插件都定义了自己支持的拓扑规范。 空的 TopologySelectorTerm 列表意味着没有拓扑限制。 此字段仅适用于启用 VolumeScheduling 功能的服务器。 -
apiVersion
:StorageClass所属的API组和版本信息,storage.k8s.io/v1
,可以根据kubectl explain storageclass.apiVersion
查询当前Kubernetes版本中StorageClass的API组和版本信息。 -
kind
:资源类型,StorageClass
,可以根据kubectl explain storageclass.kind
命令查询资源类型 -
metadata
:StorageClass资源的元数据 -
mountOptions
:此存储类的动态配置 PersistentVolume 是使用这些 mountOptions 创建的,例如 [ro
,soft
]。 未验证 - 如果 PV 无效,则安装 PV 将失败。 -
parameters
:创建此存储类的卷的配置程序的参数。 -
provisioner
:供应商,Provisioner 表示 Provisioner 的类型。StorageClass需要有一个供应者,用来确定我们使用什么样的存储来创建pv。- 常见的供应商有:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/#provisioner
- provisioner既可以由内部供应商提供,也可以由外部供应商提供,如果是外部供应商可以参考https://github.com/kubernetes-incubator/external-storage/下提供的方法创建。
- https://github.com/kubernetes-sigs/sig-storage-lib-external-provisioner
-
reclaimPolicy
:回收策略,默认的回收策略是Delete
。 -
volumeBindingMode
:VolumeBindingMode 指示应如何配置和绑定 PersistentVolumeClaims。 未设置时,使用VolumeBindingImmediate。 此字段仅适用于启用 VolumeScheduling 功能的服务器。
这里通过NFS演示StorageClass的使用。
3.2 安装nfs provisioner
3.2.1 创建运行nfs-provisioner需要的sa账号
nfs-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: default
更新资源清单:
kubectl apply -f nfs-serviceaccount.yaml
结果如下:
什么是sa?
sa全称是ServiceAccount。ServiceAccount是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。
指定了serviceaccount之后,我们把pod创建出来了,我们在使用这个pod时,这个pod就有了我们指定的账户的权限了。
3.2.2 对sa授权
kubectl create clusterrolebinding nfs-provisioner-clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner
效果如下:
3.2.3 安装nfs-provisioner程序
3.2.3.1 配置NFS共享目录
# 1. 创建nfs-provisioner共享目录(在nfs-server服务器上操作)
mkdir -p /data/nfs_pro
# 2. 配置nfs
# 配置nfs
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v1 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v2 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v3 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v4 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v5 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v6 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v7 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v8 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v9 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v10 192.168.126.40/24(rw,no_root_squash)
/data/nfs_pro 192.168.126.40/24(rw,no_root_squash)
EOF
# 重新加载配置
exportfs -arv
3.2.3.2 创建Deployment运行nfs-provisioner
nfs-provisioner-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-provisioner
labels:
app: nfs-provisioner
spec:
selector:
matchLabels:
app: nfs-provisioner
strategy:
type: Recreate
replicas: 1
template:
metadata:
name: nfs-provisioner
labels:
app: nfs-provisioner
spec:
serviceAccountName: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /persistentvolumes
name: nfs-client-root
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: 192.168.126.40
- name: NFS_PATH
value: /data/nfs_pro
restartPolicy: Always
volumes:
- name: nfs-client-root
nfs:
path: /data/nfs_pro
server: 192.168.126.40
更新资源清单:
kubectl apply -f nfs-provisioner-deployment.yaml
检查是否成功创建资源:
3.3 创建StorageClass动态供给PV
nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storageclass
namespace: default
provisioner: example.com/nfs # 此处的provisioner必须要和安装的nfs-prosivioner的PROVISIONER_NAME保持一致
provisioner处写的
example.com/nfs
应该跟安装nfs provisioner时候的env下的PROVISIONER_NAME的value值保持一致。
更新资源清单:
kubectl apply -f nfs-storageclass.yaml
检车是否成功创建StorageClass:
3.4 创建PVC通过StorageClass动态生成PV
nfs-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
namespace: default
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 2Gi
storageClassName: nfs-storageclass # 与定义的StorageClass中的name保持一致
更新资源清单:
kubectl apply -f nfs-persistentvolumeclaim.yaml
检查PVC是否创建成功并且与动态生成的PV绑定:
PVC动态生成PV并绑定的步骤如下:
- 供应商:创建一个nfs provisioner
- 创建StorageClass,指向上一步中的供应商
- 创建PersistentVolumeClaim,指定StorageClass后动态生成PV并绑定
3.5 创建Pod挂载StorageClass动态生成的PVC
podMountPVC.yaml
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
namespace: default
labels:
app: pvc-pod
spec:
containers:
- name: pvc-pod-container
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-pvc-volume
restartPolicy: Always
volumes:
- name: nfs-pvc-volume
persistentVolumeClaim:
claimName: nfs-pvc # PVC的名称
更新资源清单:
kubectl apply -f podMountPVC.yaml
检查Pod是否创建成功:
然后去nfs-server服务器上进入到该PV对应的目录下创建index.html
:
cd /data/nfs_pro/default-nfs-pvc-pvc-672785ee-acc5-482e-811a-71f8fdf76886
echo 'Create By nfs provisioner PV Successed!' > index.html
效果如下:
然后回到控制节点,访问该Pod: