持久存储卷

官方文档:https://kubernetes.io/docs/concepts/storage/persistent-volumes/ 通过前面使用持久存储卷(Persistent Volume)的示例可知,K8S用户必须要清晰了解到所用的网络存储系统的访问细节才能完成存储卷相关的配置任务,例如,NFS存储卷的server和path字段的配置就依赖于服务器地址和共享目录路径。这与K8S的向用户和开发隐藏底层架构的目标有所背离,对于存储资源的使用最好也能像使用计算资源一样,用户和开发人员无需了解Pod资源究竟运行于哪个节点,也无需了解存储系统是什么设备及位于何处。为此,K8S的PersistentVolume子系统在用户与管理员之间添加了一个抽象层,从而使得存储系统的使用和管理职能互相解耦。如下图所示 存储卷与数据持久化(四)之持久存储卷 - 图1

PV详解

PersistentVolume(PV)是指由集群管理员配置提供的某系统上的一段存储空间,它是对底层共享存储的抽象,将共享存储作为一种可由用户申请使用的资源,实现了”存储消费”机制。通过存储插件机制,PV支持使用多种网络存储系统或云端存储等多种后端存储系统,例如,前面使用的NFS,RBD和cinder等,它将这些共享存储定义为一种” 资源”,比如Node也是一种容器应用可以”消费”的资源。PV是集群级别的资源,不属于任何名称空间,用户对PV资源的使用需要通PersistentVolumeClaim(PVC)提出的使用申请(或称为声明)来完成绑定,是PV资源的消费者,就像Pod消费Node的资源一样,它向PV申请特定大小的空间及访问模式(如rw或ro),从而创建出PVC存储卷,而后再由Pod资源通过persistentVolumeClaim存储卷关联使用

尽管PVC使得用户可以以抽象的方式访问存储资源,但很多时候还是会设计PV的不少属性,通常应用程序会对存储设备的特性和性能有不同的要求,包括读写速度,并发性能,数据冗余等更高的要求,为此,集群管理员不得不通过多种方式提供不同的PV以满足应用程序的要求。K8S从1.4版本开始引入了一个新的资源对象StorageClass,用于标记存储资源的特性和性能而不是具体的PV,例如”fast”,”slow”或”glod”,”silver”,”bronze”等。到1.6版本时,StoragetClass和动态资源供应的机制得到了完善,实现了存储卷的按需创建,在共享存储的自动化管理进程中实现了重要的一步。用户通过PVC直接向意向的类别发出申请,匹配由管理员事先创建的PV,或者由其按需为用户动态创建PV,这样做甚至免去了需要事先创建PV的过程。

PV(Persistent Volume)是集群之中的一块网络存储。跟Node一样,也是集群级别的资源。其独立于Pod的生命周期。PV作为存储资源,主要包括存储能力,访问模式,存储类型,回收策略,后端存储类型等关键信息的设置。PV对存储系统的支持可以通过插件来实现,目前,K8S支持如下类型插件

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC(Fibre Channel) **
  • FlexVolume
  • Flocker
  • NFS
  • iSCSI
  • RBD(Ceph Block Device)
  • CephFS
  • Cinder(OpenStack block storage)
  • GlusterFS
  • VsphereVolume
  • Quobyte Volumes
  • HostPath
  • VMware Photon
  • Portworx Volumes
  • ScaleIO Volumes
  • StorageOS
  • Local

创建PV

Persistent Volume主要支持以下几个通用字段,用于定义PV的容量、存储卷类型、访问模式和回收策略

容量设置

  • persistentvolume.spec.capacity <map[string]string>:用于定义PV的存储容量,目前仅支持存储空间的设置,未来可能加入IOPS、吞吐率等指标的设置

存储卷类型

  • persistentvolume.spec.volumeMode <string>:用于定义存储卷的类型,从1.13版本开始引入的。可选项有Filesystem和Block,默认值为Filesystem。目前PV支持如下的块设备类型,AWSElasticBlockStore、AzureDisk、FC、GCEPersistentDisk、iSCSI、Local Volume、RBD(Ceph Block Device)、VsphereVolume

访问模式

  • 尽管在PV层看起来并无差别,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类型的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能。具体的可用查看官方文档[https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes)
  • persistentvolume.spec.accessModes <[]string>:对PV进行访问模式的设置,用于描述用户的应用对存储资源的访问权限。具体可使用的模式如下:
  • ReadWriteOnce:仅可以被单个节点读写挂载;命令行中简写为RWO
  • ReadOnlyMany:可被多个节点同时只读挂载; 命令行中简写为ROX
  • ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。
    某些PV可能支持多种访问模式,但PV在挂载时只能使用一种访问模式,多种访问模式不能同时生效。在 PVC 绑定 PV 时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。

回收策略

  • persistentvolume.spec.persistentVolumeReclaimPolicy <string>:PV存储空间被释放时的处理机制。可用类型为Retain(默认)、Recycle或Delete。具体说明如下:
  • Retain:不清理,保持不动,由管理员随后手动回收
  • Recycle:空间回收,即删除存储卷目录下的所有数据文件(包括子目录和隐藏文件,即 rm -rf /thevolume/*),目前仅NFS和hostPath支持此操作
  • Delete:删除存储资源(包括存储卷),仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure

存储类别(StorageClass)

  • persistentvolume.spec.storageClassName <string>:当前PV所属的storageClassName的名称;默认值为空,即不属于任何storageClass

挂载参数(mountOptions)

  • persistentvolume.spec.mountOptions <[]string>:挂载选项组成的列表,如ro、soft和hard等

下面的资源清单配置示例中定义了一个使用NFS存储后端的PV,空间大小为2GB,支持多路的读写操作。待后端存储系统满足需求时,即可进行如下PV资源的创建。我们将如下的配置文件保存至pv-nfs.yaml文件中

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv-nfs-0001
  5. labels:
  6. release: stable
  7. spec:
  8. capacity:
  9. storage: 2Gi
  10. volumeMode: Filesystem
  11. accessModes:
  12. - ReadWriteMany
  13. persistentVolumeReclaimPolicy: Recycle
  14. storageClassName: slow
  15. mountOptions:
  16. - hard
  17. - nfserver=4.1
  18. nfs:
  19. path: "/webdata/htdocs"
  20. server: nfs.ilinux.io

资源清单配置文成之后可以使用kubectl apply进行创建pv资源

  1. [root@k8s-master01 pv]# kubectl apply -f pv-nfs.yaml
  2. persistentvolume/pv-nfs-0001 created
  3. [root@k8s-master01 pv]#

创建完成后,可以看到其状态为”Available”,即”可用”状态,表示目前尚未被PVC资源所”绑定”

  1. [root@k8s-master01 pv]# kubectl get pv pv-nfs-0001
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv-nfs-0001 2Gi RWX Recycle Available slow 75s
  4. [root@k8s-master01 pv]#
  5. [root@k8s-master01 pv]# kubectl get pv pv-nfs-0001 -o custom-columns=NAME:metadata.name,STARUS:status.phase
  6. NAME STARUS
  7. pv-nfs-0001 Available
  8. [root@k8s-master01 pv]#

下面是另外一个PV资源的配置清单,它使用RBD后端存储,空间大小为1GB,仅支持单个客户端的读写访问(注意:每种存储卷的类型的不同所支持的访问模式也不尽相同,具体的请查看前面给出的网站地址),将RBD相关属性设定为匹配实际的环境需求,例如在Ceph集群中创建映象pv-rbd-0001,大小为1GB,并在映射后进行映象文件格式化,随后即可创建如下的PV资源:pv-rbd.yaml

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv-rbd-0001
  5. labels:
  6. release: stable
  7. spec:
  8. capacity:
  9. storage: 1Gi
  10. accessModes:
  11. - ReadWriteOnce
  12. persistentVolumeReclaimPolicy: Retain
  13. storageClassName: fast
  14. rbd:
  15. monitors:
  16. - ceph-mon01.ilinux.io:6789
  17. - ceph-mon02.ilinux.io:6789
  18. - ceph-mon03.ilinux.io:6789
  19. pool: kube
  20. image: pv-rbd-0001
  21. user: admin
  22. secretRef:
  23. name: ceph-secret
  24. fsType: ext4
  25. readOnly: false

资源清单配置完成之后,使用kubectl apply进行创建

  1. [root@k8s-master01 pv]# kubectl apply -f pv-rbd.yaml
  2. persistentvolume/pv-rbd-0001 created
  3. [root@k8s-master01 pv]#

使用资源的查看命令可列出PV资源的相关信息

  1. [root@k8s-master01 pv]# kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv-nfs-0001 2Gi RWX Recycle Available slow 15m
  4. pv-rbd-0001 1Gi RWO Retain Available fast 2m7s
  5. [root@k8s-master01 pv]#

使用资源的查看命令可列出PV资源的相关信息,创建完成的PV资源可能处于下列四种状态中的某一种,它们代表着PV资源生命周期中的各个阶段

  • Available: 可用状态的自由资源,尚未被PVC绑定
  • Bound: 已经绑定至某个PVC
  • Released: 绑定的PVC已经被删除,资源已释放,但资源尚未被集群回收
  • Failed: 因自动回收资源失败而处于的故障状态

创建PVC

PersistentVolumeClaim是存储卷类型的资源,它通过申请占用某个PersistentVolume而创建,它与PV是一对一的关系,用户无需关心其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、pv标签选择器和StorageClass等相关信息即可。PVC的spec字段的可嵌套字段具体如下 persistentvolumeclaim.spec.accessModes <[]string>:当前PVC的访问模式,用于描述用于应用对存储资源的访问权限,其三种访问的设置与PV设置相同 persistentvolumeclaim.spec.resources <object>:当前PVC存储卷需要占用的资源量最小值,目前仅支持resuest.storage的设置,即仅限定其存储空间大小 persistentvolumeclaim.spec.dataSource <object>: persistentvolumeclaim.spec.selector <object>:绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-perssions),用于挑选要绑定的PV,系统将根据标签选择出合适的PV与该PVC进行绑定;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出 persistentvolumeclaim.spec.storageClassName <string>:所依赖的存储类的名称。PVC在定义时可以设置需要的后端存储类的类别,以减少对后端存储特性的详细信息的依赖。只有设置了该class的PV才能被系统选出,并与该PVC进行绑定 persistentvolumeclaim.spec.volumeMode <string>:存储卷模型,用于指定此pv存储卷可被用作文件系统还是裸格式的块设备,默认为filesystem persistentvolumeclaim.spec.volumeName <string>:用于指定要绑定的PV的卷名

注意:PVC和PV都受限于Namespace,PVC在选择PV时受到Namespace的限制,只有相同namespace中的PV才可能与PVC绑定,Pod在引用PVC时同样受Namespace的限制,只有相同namespace中的pvc才能挂载到Pod内 另外,如果资源供应使用的是动态模式,即管理员没有预先定义PV,仅通过storageClass交给系统自动完成PV的动态创建,那么PVC再设定selector时,系统将无法为其供应任何存储资源 在启用动态供应模式的情况下,一旦用户删除了PVC,与之绑定的PV也将根据其默认的回收策略”Delete”被删除。如果需要保留PV(用户数据),则在动态绑定成功后,用户需要将系统自动生成的回收策略从Delete改为Retain

下面是一个pvd资源配置清单示例,(pvc-nfs-0001.yaml)定义了一个pvc资源示例,其选择pv的挑选机制是使用了标签选择器,适配的标签是release:stable,存储类为slow,这会关联到前面创建的pv资源pv-nfs-0001

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: pvc-nfs-0001
  5. lables:
  6. release: stable
  7. spec:
  8. accessModes:
  9. - ReadWriteMany
  10. volumeMode: Filesystem
  11. resources:
  12. requests:
  13. storage: 1Gi
  14. storageClassName: slow
  15. selector:
  16. matchLabels:
  17. release: stable

资源清单文件配置完成之后,使用资源创建命令完成创建即可

  1. [root@k8s-master01 pv]# kubectl apply -f pvc-nfs-0001.yaml
  2. persistentvolumeclaim/pvc-nfs-0001 created
  3. [root@k8s-master01 pv]#

创建完成之后,即可查看绑定PV资源的相关信息

  1. [root@k8s-master01 pv]# kubectl get pvc pvc-nfs-0001
  2. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  3. pvc-nfs-0001 Bound pv-nfs-0001 1Gi RWX slow 2m25s
  4. [root@k8s-master01 pv]#
  5. [root@k8s-master01 ~]# kubectl get pv
  6. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  7. pv-nfs-0001 1Gi RWX Recycle Bound default/pvc-nfs-0001 slow 26m
  8. pv-rbd-0001 1Gi RWO Retain Available 2d20h
  9. [root@k8s-master01 ~]#

如果需要绑定此前创建的PV资源pv-rbd-0001.yaml,那么创建类似如下的资源配置即可

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: pvc-nfs-0001
  5. lables:
  6. release: stable
  7. spec:
  8. accessModes:
  9. - ReadWriteOnce
  10. volumeMode: Filesystem
  11. resources:
  12. requests:
  13. storage: 1Gi
  14. storageNameClassName: fast
  15. selector:
  16. release: stable

创建好PVC资源以后,即可在Pod资源中通过persistentVolumeClain存储卷引用它,而后挂在于容器中进行数据持久化。需要注意的是,PV是集群级别的资源,而PVC则隶属于名称空间,因此,PVC在绑定目标PV时受名称空间的限制,Pod引用PVC时,也只能属于同一名称空间中的资源

在Pod中使用PVC

在Pod资源中调用PVC资源,只需要定义volumes时使用persistentVolumeClaims字段嵌套指定两个字段即可,具体如下

  • claimName: 需要调用的PVC存储卷的名称,PVC卷与Pod资源必须在同一个名称空间中
  • readOnly: 是否将存储卷强制挂载为只读模式,默认为false

为Redis创建PV

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv-nfs-redis-0001
  5. labels:
  6. release: redis-cache
  7. spec:
  8. capacity:
  9. storage: 1Gi
  10. volumeMode: Filesystem
  11. accessModes:
  12. - ReadWriteMany
  13. persistentVolumeReclaimPolicy: Recycle
  14. storageClassName: cache
  15. nfs:
  16. path: "/data/redis"
  17. server: nfs.ilinux.io

查看redis的pv是否创建成功

  1. [root@k8s-master01 pv]# kubectl apply -f pv-nfs-redis.yaml
  2. persistentvolume/pv-nfs-redis-0001 created
  3. [root@k8s-master01 pv]#
  4. [root@k8s-master01 pv]# kubectl get pv pv-nfs-redis-0001
  5. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  6. pv-nfs-redis-0001 1Gi RWX Recycle Available cache 21s
  7. [root@k8s-master01 pv]#

为redis创建PVC

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: pvc-nfs-redis-0001
  5. labels:
  6. release: redis-cache
  7. spec:
  8. accessModes:
  9. - ReadWriteMany
  10. volumeMode: Filesystem
  11. resources:
  12. requests:
  13. storage: 1Gi
  14. storageClassName: cache
  15. selector:
  16. matchLabels:
  17. release: redis-cache

查看PVC是否创建成功

  1. [root@k8s-master01 pv]# kubectl apply -f pvc-nfs-0001.yaml
  2. persistentvolumeclaim/pvc-nfs-redis-0001 created
  3. [root@k8s-master01 pv]#
  4. [root@k8s-master01 pv]# kubectl get pvc pvc-nfs-redis-0001
  5. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  6. pvc-nfs-redis-0001 Bound pv-nfs-redis-0001 1Gi RWX cache 15s
  7. [root@k8s-master01 pv]#
  8. [root@k8s-master01 pv]# kubectl get pv pv-nfs-redis-0001
  9. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  10. pv-nfs-redis-0001 1Gi RWX Recycle Bound default/pvc-nfs-redis-0001 cache 2m22s
  11. [root@k8s-master01 pv]#

创建Pod资源

创建redis的Pod资源,下面的清单定义了一个Pod资源,它是我们之前使用的redis资源,具体的实现步骤如下 先删除之前创建的Pod资源,不删除也可以

  1. [root@k8s-master01 emptydir]# kubectl delete -f redis-nfs.yaml
  2. deployment.apps "redis" deleted
  3. [root@k8s-master01 emptydir]#

编辑Pod资源清单配置文件

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: pvc-redis-nfs
  5. spec:
  6. replicas: 1
  7. selector:
  8. matchLabels:
  9. app: pvc-redis-nfs
  10. template:
  11. metadata:
  12. labels:
  13. app: pvc-redis-nfs
  14. spec:
  15. dnsPolicy: ClusterFirstWithHostNet
  16. containers:
  17. - name: redis-pvc
  18. image: docker.io/redis:6.0.5-alpine
  19. ports:
  20. - containerPort: 6379
  21. name: redisport
  22. volumeMounts:
  23. - name: pvc-nfs-redis-0001
  24. mountPath: /data
  25. volumes:
  26. - name: pvc-nfs-redis-0001
  27. persistentVolumeClaim:
  28. claimName: pvc-nfs-redis-0001

资源清单配置文成之后,即可创建Pod资源

  1. [root@k8s-master01 pv]# kubectl apply -f redis-nfs.yaml
  2. deployment.apps/pvc-redis-nfs created
  3. [root@k8s-master01 pv]#
  4. [root@k8s-master01 pv]# kubectl get pods -l "app=pvc-redis-nfs"
  5. NAME READY STATUS RESTARTS AGE
  6. pvc-redis-nfs-5845944687-wksvt 1/1 Running 0 2m7s
  7. [root@k8s-master01 pv]#

测试数据是否可以持久化,使用redis-cli的方式进入Pod,插入一行数据

  1. [root@k8s-master01 pv]# kubectl exec -ti pvc-redis-nfs-5845944687-wksvt -- redis-cli
  2. 127.0.0.1:6379> set mykey "hello nfs.ilinux.io"
  3. OK
  4. 127.0.0.1:6379> get mykey
  5. "hello nfs.ilinux.io"
  6. 127.0.0.1:6379>
  7. 127.0.0.1:6379>
  8. 127.0.0.1:6379> BGSAVE
  9. Background saving started
  10. 127.0.0.1:6379>
  11. 127.0.0.1:6379>
  12. 127.0.0.1:6379>
  13. 127.0.0.1:6379>
  14. 127.0.0.1:6379> exit
  15. [root@k8s-master01 pv]#

为了测试数据是否可以持久化,删除Pod资源

  1. [root@k8s-master01 pv]# kubectl delete -f redis-nfs.yaml
  2. deployment.apps "pvc-redis-nfs" deleted
  3. [root@k8s-master01 pv]# kubectl get pods
  4. NAME READY STATUS RESTARTS AGE
  5. myapp-57bd958885-2mv6z 1/1 Running 0 13d
  6. myapp-57bd958885-6sl2c 1/1 Running 0 13d
  7. myapp-57bd958885-8522q 1/1 Running 0 13d
  8. myapp-57bd958885-gsfkf 1/1 Running 0 13d
  9. myapp-57bd958885-hpdgf 1/1 Running 0 13d
  10. pvc-redis-nfs-5845944687-c9lh4 0/1 Terminating 0 47s
  11. [root@k8s-master01 pv]#
  12. #然后使用`kubectl apply`命令进行重建Pod
  13. [root@k8s-master01 pv]# kubectl apply -f redis-nfs.yaml
  14. deployment.apps/pvc-redis-nfs created
  15. [root@k8s-master01 pv]#
  16. [root@k8s-master01 pv]# kubectl get pods -l "app=pvc-redis-nfs"
  17. NAME READY STATUS RESTARTS AGE
  18. pvc-redis-nfs-68c997dd-xvzm6 1/1 Running 0 29s
  19. [root@k8s-master01 pv]#
  20. [root@k8s-master01 pv]# kubectl exec -ti pvc-redis-nfs-68c997dd-xvzm6 -- redis-cli
  21. 127.0.0.1:6379> get key
  22. (nil)
  23. 127.0.0.1:6379> get mykey
  24. "hello nfs.ilinux.io"
  25. 127.0.0.1:6379>

完整的资源清单配置文件

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv-nfs-redis-0001
  5. labels:
  6. release: redis-cache
  7. spec:
  8. capacity:
  9. storage: 1Gi
  10. volumeMode: Filesystem
  11. accessModes:
  12. - ReadWriteMany
  13. persistentVolumeReclaimPolicy: Recycle
  14. storageClassName: cache
  15. # mountOptions:
  16. # - hard
  17. # - nfserver=4.1
  18. nfs:
  19. path: "/data/redis"
  20. server: 172.18.15.113
  21. ---
  22. apiVersion: v1
  23. kind: PersistentVolumeClaim
  24. metadata:
  25. name: pvc-nfs-redis-0001
  26. labels:
  27. release: redis-cache
  28. spec:
  29. accessModes:
  30. - ReadWriteMany
  31. volumeMode: Filesystem
  32. resources:
  33. requests:
  34. storage: 1Gi
  35. storageClassName: cache
  36. selector:
  37. matchLabels:
  38. release: redis-cache
  39. ---
  40. apiVersion: apps/v1
  41. kind: Deployment
  42. metadata:
  43. name: pvc-redis-nfs
  44. spec:
  45. replicas: 1
  46. selector:
  47. matchLabels:
  48. app: pvc-redis-nfs
  49. template:
  50. metadata:
  51. labels:
  52. app: pvc-redis-nfs
  53. spec:
  54. containers:
  55. - name: redis-pvc
  56. image: docker.io/redis:6.0.5-alpine
  57. ports:
  58. - containerPort: 6379
  59. name: redisport
  60. volumeMounts:
  61. - name: pvc-nfs-redis-0001
  62. mountPath: /data
  63. volumes:
  64. - name: pvc-nfs-redis-0001
  65. persistentVolumeClaim:
  66. claimName: pvc-nfs-redis-0001

存储类

官方文档:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/ 存储类(storage class)是K8S资源类型的一种,它是由管理员为管理PV之便而按需创建的类别(逻辑组),例如,可按存储系统的性能高低分类,或者根据其综合服务质量级别进行分类(如下图)、依照备份策略分类,甚至直接按管理员自定义的标准进行分类等。不过,K8S自身无法理解”类别”到底意味着什么,它仅仅是将这些当做PV的特性描述 存储卷与数据持久化(四)之持久存储卷 - 图2

存储类的好处之一便是支持PV的动态创建。用户用到持久存储时,需要通过创建PVC来绑定匹配的PV,此类操作需求量较大,或者当管理员手动创建的PV无法满足PVC的所有需求时,系统按PVC的需求标准动态创建适配的PV会为存储管理带来极大的灵活性 存储类对象的名称至关重要,它是用户调用的标识。创建存储类对象时,除了名称之外,还需要为其定义三个关键字段:

  • storageclass.parameters <map[string]string>:存储类使用参数描述需要关联到的存储卷,不过,不同的Provisioner可用的参数各不相同
  • storageclass.provisioner <string> -required:描述存储资源的提供者,即提供了存储资源的存储系统,存储类要依赖Provisioner来判定要使用的存储插件以便适配到目标存储系统。K8S内建有多种供给方(Provisioner),这些供给方的名字都以”kubernetes.io”为前缀。另外,它还支持用户自定义供给方(Provisioner),但需要遵循存储卷的开发规则。
  • storageclass.reclaimPolicy <string>:为当前存储类动态创建的PV指定回收策略,可用值为Delete(默认)和Retain,不过,那些由管理员手工创建的PV的回收策略则取决于他们自身的定义
  • storageclass.volumeBindingMode <string>:定义如何为PVC完成供给和绑定,默认为”VolumeBindingImmediate”;此选项仅在启用了存储卷调度功能时才生效
  • storageclass.mountOptions <[]string>:由当前类动态创建的PV的挂载选项列表

下面是一个定义在glusterfs-storageclass.yaml配置文件中的资源清单,它定义了一个使用glusterFS存储系统的存储类,并通过annotations字段将其定义为默认的存储类

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: glusterfs
  5. provisioner: kubernetes.io/glusterfs
  6. parameters:
  7. resturl: "http://heketi.ilinux.io:8080"
  8. restauthenabled: "false"
  9. restuser: "ik8s"
  10. restuserkey: "ik8s.io"

清单文件完成之后直接创建即可

  1. [root@k8s-master01 pv]# kubectl apply -f glusterfs-storageclass.yaml
  2. storageclass.storage.k8s.io/glusterfs created
  3. [root@k8s-master01 pv]#
  4. [root@k8s-master01 pv]#
  5. [root@k8s-master01 pv]# kubectl get pv
  6. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  7. pv-nfs-redis-0001 1Gi RWX Recycle Bound default/pvc-nfs-redis-0001 cache 28h
  8. pv-rbd-0001 1Gi RWO Retain Available fast 4d20h
  9. [root@k8s-master01 pv]# kubectl get sc
  10. NAME PROVISIONER AGE
  11. glusterfs kubernetes.io/glusterfs 15s
  12. [root@k8s-master01 pv]#

需要特别提醒的是,parameters.resturl字段用于指定gluster存储系统的RESTful风格的访问接口,本示例中的”http://heketi.ilinux.io:8080"应替换为自己实际环境当中的可用地址。Gluster存储系统本身并不支持这种访问方式,管理员需要额外部署heketi配合gluster以提供此类服务接口。Heketi支持认证访问,不过只有在restauthenabled设置为true时,restuser和restuserkey字段才会启用

动态PV供给

动态PV供给的启用,需要事先由管理员创建至少一个存储类,不同的Provisioner的创建方法各有不同。另外,并非所有的存储卷插件都由K8S内建支持PV动态供给功能 上面定义glusterfs存储类资源创建完成后,便可以根据此使用动态PV供给功能。下面的资源清单文件定义在pvc-gluster-dynamic-0001.yaml配置文件中,它将从glusterfs存储类中申请使用5GB的存储空间

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: pvc-glusterfs-dynamic-0001
  5. spec:
  6. storageClassName: glusterfs
  7. accessModes:
  8. - ReadWriteOnce
  9. resources:
  10. requests:
  11. storage: 1Gi

目前,在PVC的定义中指定使用的存储类资源的方式共有两种:一种是使用.spec.storageClassName字段,另外一种是使用”volume.beta.kubernetes.io/storage”注解信息,如上面所示。不过建议仅使用一种方式,以免两者设置为不同的值时会出现配置错误。上述的清单文件完成之后,即可创建pvc资源,然后检查其绑定状态

  1. [root@k8s-master01 pv]# kubectl apply -f pvc-gluster-dynamic-0001.yaml
  2. persistentvolumeclaim/pvc-glusterfs-dynamic-0001 created
  3. [root@k8s-master01 pv]#

通过如下命令输出的PVC资源的描述信息可以看到,PVC存储卷已经创建完成且已经完成了pv绑定,绑定的PV资源由persistentvolume-controller控制器动态提供

  1. [root@k8s-master01 pv]# kubectl describe pvc pvc-glusterfs-dynamic-0001
  2. Name: pvc-glusterfs-dynamic-0001
  3. Namespace: default
  4. StorageClass:
  5. Status: Pending
  6. Volume:
  7. Labels: <none>
  8. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  9. {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{"volume.beta.kubernetes.io/storage.class":"glusterfs"},"name"...
  10. volume.beta.kubernetes.io/storage.class: glusterfs
  11. Finalizers: [kubernetes.io/pvc-protection]
  12. Capacity:
  13. Access Modes:
  14. VolumeMode: Filesystem
  15. Mounted By: <none>
  16. Events:
  17. Type Reason Age From Message
  18. ---- ------ ---- ---- -------
  19. Normal FailedBinding 4m4s (x182 over 49m) persistentvolume-controller no persistent volumes available for this claim and no storage class is set
  20. [root@k8s-master01 pv]#

因为我们并没有Glusterfs集群,所以状态是pending。因为PVC的定义一旦生成,系统便将触发heketi进行相应的操作。关于Gluster集群的部署后续会写

任何支持PV动态供给的存储系统都可以定义为存储类后由PVC动态申请使用,这对于难以实现预估使用到的存储空间大小及存储卷数量的使用场景尤为有用,例如由statefulSet控制器管理Pod对象时,存储卷是必备资源,且随着规模的变动,存储卷的数量也会随之变动 另外,用户也可以使用云端存储提供的PV动态供给机制,如AWS EBS , AzureDisk,Cinder等,将K8S部署在云端时,此种存储方式使用的较多。 关于动态存储的资料后续会详细写到

PV和PVC的生命周期

PV是K8S集群的存储资源,而PVC则代表着资源需求。创建PVC时对PV发起的使用申请,即为”绑定”。PV和PVC是一一对应的关系,可用于响应PVC申请的PV必须要能够容纳PVC的请求条件,他们二者的交互遵循如下生命周期

存储供给

存储供给(Provisioning)是指为PVC准备可用的PV机制。K8S支持两种PV供给方式:静态和供给和动态供给

  • 静态供给:是指由集群管理员手动创建一定数量的PV的资源供给方式,这些PV负责处理存储系统的细节,并将其抽象成易用的存储资源供用户使用。静态提供的PV可能属于某存储类(storageClass),也可能没有存储类,这一点取决于管理员的设定
  • 动态供给:不存在某静态的PV匹配到用户的PVC申请时,K8S集群会尝试为PVC动态创建符合需求的PV,此为动态供给。这种方式依赖于存储类的辅助,PVC必须要向一个事先存在的存储类发起动态分配PV的请求,没有指定存储类的PVC请求会被禁止使用动态创建PV的方式
    另外,为了支持使用动态供给机制,集群管理员需要为准入控制器(admission controller)启用”DefaultStorageClass”选项,这一点通过”—admission-control”命令选项为API Server进行设定即可

存储绑定

用户基于一系列存储需求和访问模式定义好PVC后,K8S系统的控制器即会为其查找匹配的PV,并于找到之后在此二者之间建立起关联关系,而后它们二者之间的状态即转为”绑定(Binding)”。若PV是为PVC而动态创建的,则该PV专用于其PVC 若是无法为PVC找到可匹配的PV,则PVC将一直处于未绑定状态,直到有符合条件的PV出现并完成绑定才可以使用

  • 存储使用(Using):Pod资源基于persistentVolumeClaim卷类型的定义,将选定的PVC关联为存储卷,而后即可为内部的容器所使用。对于支持多种访问模式的存储卷来说,用户需要额外指定需要使用的模式。一旦完成将存储卷挂载至Pod对象内的容器中,其应用即可使用关联的PV提供的存储空间
  • PVC保护(Protection):为了避免使用中的存储卷移除而导致数据丢失,K8S从1.9版本起引入了”PVC保护机制”。启用了此特性后,万一有用户删除了仍处于某Pod资源使用中的PVC时,K8S不会立即移除,而是推迟到不再被任何Pod资源使用后才执行删除操作。处于此种阶段的PVC资源的status字段为”Termination”,并且其Finalizers字段包含”kubernetes.io/pvc-protection”

存储回收

完成存储卷的使用目标之后,即可删除PVC对象以便进行资源回收。不过,至于如何操作则取决于PV的回收策略。目前,可用的回收策略有三种:Retained、Recycled和Deleted

  • 留存(Retained):留存策略意味着在删除PVC之后,K8S系统不会自动删除PV,而仅仅是将它置于”释放(released)”状态。不过,此种状态的PV不能被其他PVC申请所绑定,因为此前的申请生成的数据仍然存在,需要由管理员手动决定其后续处理方案。这就意味着,如果想要再次使用此类的PV资源,则需要由管理员按下面的步骤手动执行删除操作
  • 1.删除PV,这之后,此PV的数据依然留存于外部的存储之上
  • 2.手工清理存储系统上依然留存的数据
  • 3.手工删除存储系统级的存储卷以释放空间,以便再次创建,或者直接将其重建为PV
  • 回收(Recycle):如果可被底层存储插件支持,资源回收策略会在存储卷上执行数据删除操作并让PV资源再次变为可被Claim。另外,管理员也可以配置一个自定义的回收器Pod模板,以便执行自定义的回收操作。不过,此种回收策略基本已经被废弃
  • 删除(Delete):对于支持Delete回收策略的存储插件来说,在PVC被删除后悔直接移除PV对象,同时移除的还有PV相关的外部存储系统上的存储资产(asset)。支持这种操作的存储系统有AWS EBS、GCE PD、Azure Disk或cinder。动态创建的PV资源的回收策略取决于相关存储类上的定义,存储类上相关的默认策略为Delete,大多数情况下,管理员都需要按用户期望的处理机制修改此默认策略,以免导致数据非计划内的误删除

扩展VPC

K8S 1.8版本起增加了扩展PV空间的特性,到目前为止,它所支持的扩展的PVC机制的存储卷有以下几种

  • gce PersistentDisk
  • awsElasticBlockStore
  • Cinder
  • glusterfs
  • rbd
    “PersistentVolumeClaimResize”准入插件负责对支持空间大小变动的存储卷执行更多的验证操作,管理员需要事先启用此插件才能使用PVC扩展规则,那些将”allowVolumeExpansion”字段的值设置为”true”的存储类即可动态扩展存储卷空间。随后,用户改动Claim请求更大的空间即可用触发底层PV空间扩展从而带来PVC存储卷的扩展
    对于包含文件系统的存储卷来是,只有在有新的Pod资源基于读写模式开始使用PVC时才会执行文件系统的大小调整操作。换句话说,如果某被扩展的存储卷已经由Pod资源所使用,则需要重建此Pod对象才能触发文件系统大小的调整操作。支持空间调整的文件系统仅有XFS和ext3、ext4