[TOC]
参考
简书介绍
前面我们学习了Kubernetes中的Volume,我们可以发现前文中的Volume(无论何种类型)和使用它的Pod都是一种静态绑定关系,在Pod定义文件中,同时定义了它使用的Volume。在这种情况下,Volume是Pod的附属品,我们无法像创建其他资源(例如Pod,Node,Deployment等等)一样创建一个Volume。
因此Kubernetes提出了PersistentVolume(PV)的概念。PersistentVolume和Volume一样,代表了集群中的一块存储区域,然而Kubernetes将PersistentVolume抽象成了一种集群资源,类似于集群中的Node对象,这意味着我们可以使用Kubernetes API来创建PersistentVolume对象。PV与Volume最大的不同是PV拥有着独立于Pod的生命周期。
而PersistentVolumeClaim(PVC)代表了用户对PV资源的请求。用户需要使用PV资源时,只需要创建一个PVC对象(包括指定使用何种存储资源,使用多少GB,以何种模式使用PV等信息),Kubernetes会自动为我们分配我们所需的PV。如果把PersistentVolume类比成集群中的Node,那么PersistentVolumeClaim就相当于集群中的Pod,Kubernetes为Pod分配可用的Node,为PersistentVolumeClaim分配可用的PersistentVolume。
PV的静态创建
首先是一个创建PV的简单例子:
1 | apiVersion: v1 |
PV 的访问模式(accessModes)有三种:
- ReadWriteOnce(RWO):是最基本的方式,可读可写,但只支持被单个 Pod 挂载。
- ReadOnlyMany(ROX):可以以只读的方式被多个 Pod 挂载。
- ReadWriteMany(RWX):这种存储可以以读写的方式被多个 Pod 共享。
不是每一种PV都支持这三种方式,例如ReadWriteMany,目前支持的还比较少。在 PVC 绑定 PV 时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。
PV 的回收策略(persistentVolumeReclaimPolicy,即 PVC 释放卷的时候 PV 该如何操作)也有三种:
- Retain,不清理, 保留 Volume(需要手动清理)
- Recycle,删除数据,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持)
- Delete,删除存储资源,比如删除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)
PVC释放卷是指用户删除一个PVC对象时,那么与该PVC对象绑定的PV就会被释放。
PV支持的类型
定义PV时,我们需要指定其底层存储的类型,例如上文中创建的PV,底层使用nfs存储。目前Kuberntes支持以下类型:
- 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 (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
- VMware Photon
- Portworx Volumes
- ScaleIO Volumes
- StorageOS
PVC的创建
当我们定义好一个PV后,我们希望像使用Volume那样使用这个PV,那么我们需要做的就是创建一个PVC对象,并在Pod定义中使用这个PVC。
定义一个PVC:
1 | kind: PersistentVolumeClaim |
Pod通过挂在Volume的方式应用PVC:
1 | kind: Pod |
下面简要分析一下定义的PVC文件的关键:
- 首先关注这个配置:
storageClassName: slow
。此配置用于绑定PVC和PV。这表明这个PVC希望使用storageClassName=slow
的PV。返回到上文中PV的定义,我们可以看到PV定义中也包含storageClassName=slow
的配置。 - 接下来是
accessModes = ReadWriteOnce
。这表明这个PV希望使用storageClassName=slow
,并且accessModes = ReadWriteOnce
的PV。 - 在上述条件都满足后,PVC还可以指定PV必须满足的Label,如
matchLabels: release: "stable"
。这表明此PVC希望使用storageClassName=slow
,accessModes = ReadWriteOnce
并且拥有Label:release: "stable"
的PV。 - 最后是
storage: 8Gi
。这表明此PVC希望使用8G的Volume资源。
通过上面的分析,我们可以看到PVC和PV的绑定,不是简单的通过Label来进行。而是要综合
storageClassName
,accessModes
,matchLabels
以及storage
来进行绑定。
PV的动态创建
上文中我们通过PersistentVolume描述文件创建了一个PV。这样的创建方式我们成为静态创建。这样的创建方式有一个弊端,那就是假如我们创建PV时指定大小为50G,而PVC请求80G的PV,那么此PVC就无法找到合适的PV来绑定。因此产生了了PV的动态创建。
PV的动态创建依赖于StorageClass对象。我们不需要手动创建任何PV,所有的工作都由StorageClass为我们完成。一个例子如下:
1 | kind: StorageClass |
这个例子使用AWS提供的插件( kubernetes.io/aws-ebs)创建了一个基于AWS底层存储的StorageClass。这意味着使用这个StorageClass,那么所有的PV都是AWSElasticBlockStore类型的。
StorageClass的定义包含四个部分:
- provisioner:指定 Volume 插件的类型,包括内置插件(如
kubernetes.io/aws-ebs
)和外部插件(如 external-storage 提供的ceph.com/cephfs
)。 - mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.1 等选项。
- parameters:指定 provisioner 的选项,比如 kubernetes.io/aws-ebs 支持 type、zone、iopsPerGB 等参数。
- reclaimPolicy:指定回收策略,同 PV 的回收策略。
手动创建的PV时,我们指定了storageClassName=slow
的配置项,然后Pod定义中也通过指定storageClassName=slow
,从而完成绑定。而通过StorageClass实现动态PV时,我们只需要指定StorageClass的metadata.name
。
回到上文中创建PVC的例子,此时PVC指定了storageClassName=slow
。那么Kubernetes会在集群中寻找是否存在metadata.name=slow
的StorageClass,如果存在,此StorageClass会自动为此PVC创建一个accessModes = ReadWriteOnce
,并且大小为8GB的PV。
通过StorageClass的使用,使我们从提前构建静态PV池的工作中解放出来。
PV的生命周期
PV的生命周期包括 5 个阶段:
- Provisioning,即 PV 的创建,可以直接创建 PV(静态方式),也可以使用 StorageClass 动态创建
- Binding,将 PV 分配给 PVC
- Using,Pod 通过 PVC 使用该 Volume,并可以通过准入控制 StorageProtection(1.9及以前版本为PVCProtection)阻止删除正在使用的 PVC
- Releasing,Pod 释放 Volume 并删除 PVC
- Reclaiming,回收 PV,可以保留 PV 以便下次使用,也可以直接从云存储中删除
Deleting,删除 PV 并从云存储中删除后段存储
根据这 5 个阶段,PV 的状态有以下 4 种
- Available:可用
- Bound:已经分配给 PVC
- Released:PVC 解绑但还未执行回收策略
- Failed:发生错误
一个PV从创建到销毁的具体流程如下:
- 一个PV创建完后状态会变成Available,等待被PVC绑定。
- 一旦被PVC邦定,PV的状态会变成Bound,就可以被定义了相应PVC的Pod使用。
- Pod使用完后会释放PV,PV的状态变成Released。
- 变成Released的PV会根据定义的回收策略做相应的回收工作。有三种回收策略,Retain、Delete 和 Recycle。Retain就是保留现场,K8S什么也不做,等待用户手动去处理PV里的数据,处理完后,再手动删除PV。Delete 策略,K8S会自动删除该PV及里面的数据。Recycle方式,K8S会将PV里的数据删除,然后把PV的状态变成Available,又可以被新的PVC绑定使用。
DefaultStorageClass
前面我们说到,PVC和PV的绑定是通过StorageClassName进行的。然而如果定义PVC时没有指定StorageClassName呢?这取决与admission插件是否开启了DefaultDefaultStorageClass功能:
- 如果DefaultDefaultStorageClass功能开启,那么此PVC的StorageClassName就会被指定为DefaultStorageClass。DefaultStorageClass从何处而来呢?原来在定义StorageClass时,可以在Annotation中添加一个键值对:
storageclass.kubernetes.io/is-default-class: true
,那么此StorageClass就变成默认的StorageClass了。 - 如果DefaultDefaultStorageClass功能没有开启,那么没有指定StorageClassName的PVC只能被绑定到同样没有指定StorageClassName的PV。
中文文档介绍之PV/PVC/StorageClass
介绍
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。 集群中的资源就像一个节点是一个集群资源。 PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期。 该API对象捕获存储的实现细节,即NFS,iSCSI或云提供商特定的存储系统。
PersistentVolumeClaim(PVC)是用户存储的请求。 它类似于pod。Pod消耗节点资源,PVC消耗存储资源。 pod可以请求特定级别的资源(CPU和内存)。 权限要求可以请求特定的大小和访问模式。
虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的PersistentVolumes,用于不同的问题。 群集管理员需要能够提供多种不同于PersistentVolumes的PersistentVolumes,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。 对于这些需求,存在StorageClass资源。
StorageClass为管理员提供了一种描述他们提供的存储的“类”的方法。 不同的类可能映射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。 Kubernetes本身对于什么类别代表是不言而喻的。 这个概念有时在其他存储系统中称为“配置文件”
例子
https://kubernetes.io/docs/user-guide/persistent-volumes/walkthrough/
Lifecycle of a volume and claim
PV是集群中的资源。 PVC是对这些资源的请求,也是对资源的索赔检查。 PV和PVC之间的相互作用遵循这个生命周期:
Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling
Provisioning
这里有两种PV的提供方式:静态或者动态
1 | Static |
Binding
在动态配置的情况下,用户创建或已经创建了具有特定数量的存储请求和特定访问模式的PersistentVolumeClaim。 主机中的控制回路监视新的PVC,找到匹配的PV(如果可能),并将它们绑定在一起。 如果为新的PVC动态配置PV,则循环将始终将该PV绑定到PVC。 否则,用户总是至少得到他们要求的内容,但是卷可能超出了要求。 一旦绑定,PersistentVolumeClaim绑定是排他的,不管用于绑定它们的模式。
如果匹配的卷不存在,PVC将保持无限期。 随着匹配卷变得可用,PVC将被绑定。 例如,提供许多50Gi PV的集群将不匹配要求100Gi的PVC。 当集群中添加100Gi PV时,可以绑定PVC。
Using
Pod使用PVC作为卷。 集群检查声明以找到绑定的卷并挂载该卷的卷。 对于支持多种访问模式的卷,用户在将其声明用作pod中的卷时指定所需的模式。
一旦用户有声明并且该声明被绑定,绑定的PV属于用户,只要他们需要它。 用户通过在其Pod的卷块中包含persistentVolumeClaim来安排Pods并访问其声明的PV。
Releasing
当用户完成卷时,他们可以从允许资源回收的API中删除PVC对象。 当声明被删除时,卷被认为是“释放的”,但是它还不能用于另一个声明。 以前的索赔人的数据仍然保留在必须根据政策处理的卷上.
Reclaiming
PersistentVolume的回收策略告诉集群在释放其声明后,该卷应该如何处理。 目前,卷可以是保留,回收或删除。 保留可以手动回收资源。 对于那些支持它的卷插件,删除将从Kubernetes中删除PersistentVolume对象,以及删除外部基础架构(如AWS EBS,GCE PD,Azure Disk或Cinder卷)中关联的存储资产。 动态配置的卷始终被删除
Recycling
如果受适当的卷插件支持,回收将对卷执行基本的擦除(rm -rf / thevolume / *),并使其再次可用于新的声明。
但是,管理员可以使用Kubernetes控制器管理器命令行参数来配置自定义的回收站pod模板,如这里所述。 定制回收站模板必须包含卷规范,如下例所示:
1 | apiVersion: v1 |
Types of Persistent Volumes
PV当前支持的类型
1 | GCEPersistentDisk |
Persistent Volumes
每个PV包含了Spec和Staus, 在PV的定义中指定该内容
1 | apiVersion: v1 |
Capacity
通常,PV将具有特定的存储容量。 这是使用PV的容量属性设置的。 看到库伯纳斯资源模型,以了解容量预期的单位。
目前,存储大小是唯一可以设置或请求的资源。 未来的属性可能包括IOPS,吞吐量等
Access Modes
PersistentVolume可以以资源提供者支持的任何方式安装在主机上。 如下表所示,提供商将具有不同的功能,每个PV的访问模式都被设置为该特定卷支持的特定模式。 例如,NFS可以支持多个读/写客户端,但是特定的NFS PV可能会以只读方式在服务器上导出。 每个PV都有自己的一组访问模式来描述具体的PV功能。
访问模式:
ReadWriteOnce – the volume can be mounted as read-write by a single node (单node的读写)
ReadOnlyMany – the volume can be mounted read-only by many nodes (多node的只读)
ReadWriteMany – the volume can be mounted as read-write by many nodes (多node的读写)
Notice:单个PV挂载的时候只支持一种访问模式
PV提供插件支持的access mode参考kubernetes官方文档
Class
PV可以有一个类,通过将storageClassName属性设置为StorageClass的名称来指定。 特定类的PV只能绑定到请求该类的PVC。 没有storageClassName的PV没有类,只能绑定到不需要特定类的PVC。
在过去,使用了注释volume.beta.kubernetes.io/storage-class而不是storageClassName属性。 该注释仍然可以工作,但将来Kubernetes版本将不再适用。
Reclaim Policy
目前的回收政策是:
1 | Retain – manual reclamation |
目前,只有NFS和HostPath支持回收。 AWS EBS,GCE PD,Azure Disk和Cinder卷支持删除
Phase
卷将处于以下阶段之一:
1 | Available – a free resource that is not yet bound to a claim |
PersistentVolumeClaims
每个PVC包含spec和status
1 | kind: PersistentVolumeClaim |
Access Modes
当请求具有特定访问模式的存储时,声明使用与卷相同的约定
Resources
声明(如pod)可以请求特定数量的资源。 在这种情况下,请求用于存储。 相同的资源模型适用于卷和声明
Selector
声明可以指定标签选择器以进一步过滤该卷集。 只有标签与选择器匹配的卷才能绑定到声明。 选择器可以由两个字段组成:
1 | matchLabels - 卷必须具有带此值的标签 |
所有来自matchLabels和matchExpressions的要求都与AND一起使用,所有这些要求都必须满足才能匹配。
Class
声明可以通过使用属性storageClassName指定StorageClass的名称来请求特定的类。只有所请求的类的PV,与PVC相同的storageClassName的PV可以绑定到PVC。
PVC不一定要求一个班级。它的storageClassName设置为等于“”的PVC总是被解释为请求没有类的PV,因此它只能绑定到没有类的PV(没有注释或一个等于“”)。没有storageClassName的PVC不完全相同,并且根据是否启用了DefaultStorageClass入门插件,集群的处理方式不同。
如果接纳插件已打开,则管理员可以指定默认的StorageClass。没有storageClassName的所有PVC只能绑定到该默认的PV。通过将StorageClass对象中的annotation storageclass.kubernetes.io/is-default-class设置为“true”来指定默认的StorageClass。如果管理员没有指定默认值,则集群会对PVC创建做出响应,就像入门插件被关闭一样。如果指定了多个默认值,则验收插件禁止创建所有PVC。
如果入门插件已关闭,则不存在默认StorageClass的概念。没有storageClassName的所有PVC只能绑定到没有类的PV。在这种情况下,没有storageClassName的PVC的处理方式与将其storageClassName设置为“”的PVC相同。
根据安装方法,安装过程中可以通过addon manager在Kubernetes群集中部署默认的StorageClass。
当PVC指定一个选择器,除了请求一个StorageClass之外,这些要求被AND组合在一起:只有所请求的类和所请求的标签的PV可能被绑定到PVC。请注意,当前,具有非空选择器的PVC不能为其动态配置PV。
在过去,使用了注释volume.beta.kubernetes.io/storage-class,而不是storageClassName属性。该注释仍然可以工作,但在未来的Kubernetes版本中它将不被支持。
声明PVC作为Volumes
Pods通过将声明用作卷来访问存储。 声明必须存在于与使用声明的pod相同的命名空间中。 群集在pod的命名空间中查找声明,并使用它来获取支持声明的PersistentVolume。 然后将体积安装到主机并进入Pod。
1 | kind: Pod |
PersistentVolumes绑定是独占的,并且由于PersistentVolumeClaims是命名空间对象,所以只能在一个命名空间内安装“许多”模式(ROX,RWX)—PVC支持被多个pod挂载
StorageClasses
每个StorageClass包含字段provisioninger和参数,当属于类的PersistentVolume需要动态配置时使用。
StorageClass对象的名称很重要,用户可以如何请求特定的类。 管理员在首次创建StorageClass对象时设置类的名称和其他参数,并且在创建对象后无法更新对象。
管理员可以仅为不要求任何特定类绑定的PVC指定默认的StorageClass:有关详细信息,请参阅PersistentVolumeClaim部分。
1 | kind: StorageClass |
Provisioner
存储类有一个供应商,它确定用于配置PV的卷插件。 必须指定此字段。
您不限于指定此处列出的“内部”供应商(其名称前缀为“kubernetes.io”并与Kubernetes一起运送)。 您还可以运行和指定外部提供程序,它们是遵循Kubernetes定义的规范的独立程序。 外部提供者的作者对代码的生命周期,供应商的运输状况,运行状况以及使用的卷插件(包括Flex)等都有充分的自主权。存储库kubernetes-incubator /外部存储库包含一个库 用于编写实施大部分规范的外部提供者以及各种社区维护的外部提供者。
Parameters
存储类具有描述属于存储类的卷的参数。 取决于供应商,可以接受不同的参数。 例如,参数类型的值io1和参数iopsPerGB特定于EBS。 当省略参数时,使用一些默认值。
AWS/GCE/Glusterfs/
OpenStack Cinder
1 | kind: StorageClass |
其它类型的storageClass配置参考
1 | https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes |
配置
如果您正在编写在各种群集上运行并需要持久存储的配置模板或示例,我们建议您使用以下模式:
在您的配置文件夹(包括部署,ConfigMaps等)中包含PersistentVolumeClaim对象。
在配置中不要包含PersistentVolume对象,因为实例化配置的用户可能没有创建PersistentVolumes的权限。
给用户提供实例化模板时提供存储类名称的选项。
1. 如果用户提供存储类名称,并且集群是1.4或更高版本,请将该值放入PVC的volume.beta.kubernetes.io/storage-class注释中。如果集群的管理员启用了StorageClasses,这将导致PVC与正确的存储类匹配。
2. 如果用户不提供存储类名称或者集群是版本1.3,那么在PVC上放置一个volume.alpha.kubernetes.io/storage-class:default注释。
这将导致在某些群集上为用户自动配置PV,并具有合理的默认特性。
尽管在名称中使用了alpha,但这个注释背后的代码具有beta级别的支持。
3. 不要使用volume.beta.kubernetes.io/storage-class:包含空字符串的任何值,因为它将阻止DefaultStorageClass接纳控制器运行(如果启用)。
在您的工具中,请注意在一段时间后未绑定的PVC,并将其表示给用户,因为这可能表明集群没有动态存储支持(在这种情况下,用户应创建匹配的PV)或集群没有存储系统(在这种情况下,用户无法部署需要PVC的配置)。
在将来,我们预计大多数集群都将启用DefaultStorageClass,并提供某种形式的存储。但是,可能没有任何存储类名可用于所有集群,因此默认情况下继续不设置。在某种程度上,alpha注释将不再有意义,但PVC上的未设置的storageClass字段将具有所需的效果。