简介

  管理存储和管理计算有着明显的不同。PersistentVolume子系统给用户和管理员提供了一套API,从而抽象出存储是如何提供和消耗的细节。在这里,我们介绍两种新的API资源:PersistentVolume(简称PV)和PersistentVolumeClaim(简称PVC)。
  PersistentVolume(持久卷,简称PV)是集群内,由管理员提供的网络存储的一部分。就像集群中的节点一样,PV也是集群中的一种资源。它也像Volume一样,是一种volume插件,但是它的生命周期却是和使用它的Pod相互独立的。PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节。
  PersistentVolumeClaim(持久卷声明,简称PVC)是用户的一种存储请求。它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源。Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式(可以被映射为一次读写或者多次只读)。
  PVC允许用户消耗抽象的存储资源,用户也经常需要各种属性(如性能)的PV。集群管理员需要提供各种各样、不同大小、不同访问模式的PV,而不用向用户暴露这些volume如何实现的细节。因为这种需求,就催生出一种StorageClass资源。
  StorageClass提供了一种方式,使得管理员能够描述他提供的存储的等级。集群管理员可以将不同的等级映射到不同的服务等级、不同的后端策略。
  请查看detailed walkthrough with working examples

volume和claim的生命周期

  PV是集群中的资源,PVC是对这些资源的请求,同时也是这些资源的“提取证”。PV和PVC的交互遵循以下生命周期:

供给

  有两种PV提供的方式:静态和动态。

静态

  集群管理员创建多个PV,它们携带着真实存储的详细信息,这些存储对于集群用户是可用的。它们存在于Kubernetes API中,并可用于存储使用。

动态

  当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass:PVC必须请求这样一个等级,而管理员必须已经创建和配置过这样一个等级,以备发生这种动态供给的情况。请求等级配置为“”的PVC,有效地禁用了它自身的动态供给功能。

绑定

  用户创建一个PVC(或者之前就已经就为动态供给创建了),指定要求存储的大小和访问模式。master中有一个控制回路用于监控新的PVC,查找匹配的PV(如果有),并把PVC和PV绑定在一起。如果一个PV曾经动态供给到了一个新的PVC,那么这个回路会一直绑定这个PV和PVC。另外,用户总是至少能得到它们所要求的存储,但是volume可能超过它们的请求。一旦绑定了,PVC绑定就是专属的,无论它们的绑定模式是什么。
  如果没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态,一旦PV可用了,PVC就会又变成绑定状态。比如,如果一个供给了很多50G的PV集群,不会匹配要求100G的PVC。直到100G的PV添加到该集群时,PVC才会被绑定。

使用

  Pod使用PVC就像使用volume一样。集群检查PVC,查找绑定的PV,并映射PV给Pod。对于支持多种访问模式的PV,用户可以指定想用的模式。
  一旦用户拥有了一个PVC,并且PVC被绑定,那么只要用户还需要,PV就一直属于这个用户。用户调度Pod,通过在Pod的volume块中包含PVC来访问PV。

释放

  当用户使用PV完毕后,他们可以通过API来删除PVC对象。当PVC被删除后,对应的PV就被认为是已经是“released”了,但还不能再给另外一个PVC使用。前一个PVC的属于还存在于该PV中,必须根据策略来处理掉。

回收

  PV的回收策略告诉集群,在PV被释放之后集群应该如何处理该PV。当前,PV可以被Retained(保留)、 Recycled(再利用)或者Deleted(删除)。保留允许手动地再次声明资源。对于支持删除操作的PV卷,删除操作会从Kubernetes中移除PV对象,还有对应的外部存储(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。动态供给的卷总是会被删除。

Recycled(再利用)

  如果PV卷支持再利用,再利用会在PV卷上执行一个基础的擦除操作(rm -rf /thevolume/*),使得它可以再次被其他PVC声明利用。
  管理员可以通过Kubernetes controller manager的命令行工具(点击查看),来配置自定义的再利用Pod模板。自定义的再利用Pod模板必须包含PV卷的详细内容,如下示例:

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler-
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "gcr.io/google_containers/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

  如上,在volumes部分的指定路径,应该被替换为PV卷需要再利用的路径。
  

PV类型

  PV类型使用插件的形式来实现。Kubernetes现在支持以下插件:
  GCEPersistentDisk
  AWSElasticBlockStore
  AzureFile
  AzureDisk
  FC (Fibre Channel)
  Flocker
  NFS
  iSCSI
  RBD (Ceph Block Device)
  CephFS
  Cinder (OpenStack block storage)
  Glusterfs
  VsphereVolume
  Quobyte Volumes
  HostPath (仅测试过单节点的情况——不支持任何形式的本地存储,多节点集群中不能工作)
  VMware Photon
  Portworx Volumes
  ScaleIO Volumes

PV介绍

  每个PV都包含一个spec和状态,即说明书和PV卷的状态。

  apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv0003
  spec:
    capacity:
      storage: 5Gi
    accessModes:
      - ReadWriteOnce
    persistentVolumeReclaimPolicy: Recycle
    storageClassName: slow
    nfs:
      path: /tmp
      server: 172.17.0.2

Capacity(容量)

  一般来说,PV会指定存储的容量,使用PV的capacity属性来设置。查看Kubernetes的Resource Model来了解capacity
  当前,存储大小是唯一能被设置或请求的资源。未来可能包含IOPS,吞吐率等属性。

访问模式

  PV可以使用存储资源提供商支持的任何方法来映射到host中。如下的表格中所示,提供商有着不同的功能,每个PV的访问模式被设置为卷支持的指定模式。比如,NFS可以支持多个读/写的客户端,但可以在服务器上指定一个只读的NFS PV。每个PV有它自己的访问模式。
  访问模式包括:
   ▷ ReadWriteOnce —— 该volume只能被单个节点以读写的方式映射
   ▷ ReadOnlyMany —— 该volume可以被多个节点以只读方式映射
   ▷ ReadWriteMany —— 该volume只能被多个节点以读写的方式映射
  在CLI中,访问模式可以简写为:
   ▷ RWO - ReadWriteOnce
   ▷ ROX - ReadOnlyMany
   ▷ RWX - ReadWriteMany
  注意:即使volume支持很多种访问模式,但它同时只能使用一种方式来映射。比如,GCEPersistentDisk可以被单个节点映射为ReadWriteOnce,或者多个节点映射为ReadOnlyMany,但不能同时使用这两种方式来映射。

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
FC -
FlexVolume -
Flocker - -
GCEPersistentDisk -
Glusterfs
HostPath - -
iSCSI -
PhotonPersistentDisk - -
Quobyte
NFS
RBD -
VsphereVolume - -
PortworxVolume -
ScaleIO -

Class

  一个PV可以有一种class,通过设置storageClassName属性来选择指定的StorageClass。有指定class的PV只能绑定给请求该class的PVC。没有设置storageClassName属性的PV只能绑定给未请求class的PVC。
  过去,使用volume.beta.kubernetes.io/storage-class注解,而不是storageClassName属性。该注解现在依然可以工作,但在Kubernetes的未来版本中已经被完全弃用了。

回收策略

  当前的回收策略有:
   ▷ Retain:手动回收
   ▷ Recycle:需要擦出后才能再使用
   ▷ Delete:相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除
  当前,只有NFS和HostPath支持回收利用,AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作。

阶段

  一个volume卷处于以下几个阶段之一:
   ▷ Available:空闲的资源,未绑定给PVC
   ▷ Bound:绑定给了某个PVC
   ▷ Released:PVC已经删除了,但是PV还没有被集群回收
   ▷ Failed:PV在自动回收中失败了
  CLI可以显示PV绑定的PVC名称。

映射选项

  当PV被映射到一个node上时,Kubernetes管理员可以指定额外的映射选项。可以通过使用标注volume.beta.kubernetes.io/mount-options来指定PV的映射选项。
  比如:

apiVersion: "v1"
kind: "PersistentVolume"
metadata:
  name: gce-disk-1
  annotations:
    volume.beta.kubernetes.io/mount-options: "discard"
spec:
  capacity:
    storage: "10Gi"
  accessModes:
    - "ReadWriteOnce"
  gcePersistentDisk:
    fsType: "ext4"
    pdName: "gce-disk-1

  映射选项是当映射PV到磁盘时,一个可以被递增地添加和使用的字符串。
  注意,并非所有的PV类型都支持映射选项。在Kubernetes v1.6中,以下的PV类型支持映射选项。
   ● GCEPersistentDisk
   ● AWSElasticBlockStore
   ● AzureFile
   ● AzureDisk
   ● NFS
   ● iSCSI
   ● RBD (Ceph Block Device)
   ● CephFS
   ● Cinder (OpenStack block storage)
   ● Glusterfs
   ● VsphereVolume
   ● Quobyte Volumes
   ● VMware Photon

PersistentVolumeClaims(PVC)

  每个PVC都包含一个specstatus,即该PVC的规则说明和状态。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

访问模式

  当请求指定访问模式的存储时,PVC使用的规则和PV相同。

资源

  PVC,就像pod一样,可以请求指定数量的资源。请求资源时,PV和PVC都使用相同的资源样式

选择器(Selector)

  PVC可以指定标签选择器进行更深度的过滤PV,只有匹配了选择器标签的PV才能绑定给PVC。选择器包含两个字段:
   ● matchLabels(匹配标签) - PV必须有一个包含该值得标签
   ● matchExpressions(匹配表达式) - 一个请求列表,包含指定的键、值的列表、关联键和值的操作符。合法的操作符包含In,NotIn,Exists,和DoesNotExist。
  所有来自matchLabelsmatchExpressions的请求,都是逻辑与关系的,它们必须全部满足才能匹配上。

等级(Class)

  PVC可以使用属性storageClassName来指定StorageClass的名称,从而请求指定的等级。只有满足请求等级的PV,即那些包含了和PVC相同storageClassName的PV,才能与PVC绑定。
  PVC并非必须要请求一个等级。设置storageClassName为“”的PVC总是被理解为请求一个无等级的PV,因此它只能被绑定到无等级的PV(未设置对应的标注,或者设置为“”)。未设置storageClassName的PVC不太相同,DefaultStorageClass的权限插件打开与否,集群也会区别处理PVC。
   • 如果权限插件被打开,管理员可能会指定一个默认的StorageClass。所有没有指定StorageClassName的PVC只能被绑定到默认等级的PV。要指定默认的StorageClass,需要在StorageClass对象中将标注storageclass.kubernetes.io/is-default-class设置为“true”。如果管理员没有指定这个默认值,集群对PVC创建请求的回应就和权限插件被关闭时一样。如果指定了多个默认等级,那么权限插件禁止PVC创建请求。
   • 如果权限插件被关闭,那么久没有默认StorageClass的概念。所有没有设置StorageClassName的PVC都只能绑定到没有等级的PV。因此,没有设置StorageClassName的PVC就如同设置StorageClassName为“”的PVC一样被对待。
   根据安装方法的不同,默认的StorageClass可能会在安装过程中被插件管理默认的部署在Kubernetes集群中。
   当PVC指定selector来请求StorageClass时,所有请求都是与操作的。只有满足了指定等级和标签的PV才可能绑定给PVC。当前,一个非空selector的PVC不能使用PV动态供给。
   过去,使用volume.beta.kubernetes.io/storage-class注解,而不是storageClassName属性。该注解现在依然可以工作,但在Kubernetes的未来版本中已经被完全弃用了。

使用PVC

  Pod通过使用PVC(使用方式和volume一样)来访问存储。PVC必须和使用它的pod在同一个命名空间,集群发现pod命名空间的PVC,根据PVC得到其后端的PV,然后PV被映射到host中,再提供给pod。

kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: dockerfile/nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

命名空间注意事项

  PV绑定是独有的,因为PVC是命名空间对象,映射PVC时只能在同一个命名空间中使用多种模式(ROXRWX)。

StorageClass

  每个StorageClass都包含字段provisionerparameters,在所属的PV需要动态供给时使用这些字段。
  StorageClass对象的命名是非常重要的,它是用户请求指定等级的方式。当创建StorageClass对象时,管理员设置等级的名称和其他参数,但对象不会在创建后马上就被更新。
  管理员可以指定一个默认的StorageClass,用于绑定到那些未请求指定等级的PVC。详细信息可参考PersistentVolumeClaim章节。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2

Provisioner

  StorageClass都有存储供应商provisioner,用来决定哪种volume插件提供给PV使用。必须制定该字段。
  你不限于指定此处列出的“内部”供应商(其名称前缀为“kubernetes.io”并与Kubernetes一起分发)。你还可以运行和指定外部供应商,它们是遵循Kubernetes定义的规范的独立程序。外部提供者的作者对代码的生命周期,供应商的分发方式,运行状况以及使用的卷插件(包括Flex)等都有充分的自主权。库kubernetes-incubator/external-storage存放了一个库, 用于编写外部存储供应商,而这些提供者实现了大量的规范,并且是各种社区维护的。

参数

  StorageClass有一些参数用于描述归属于该StorageClass的volume。不同的存储提供商可能需要不同的参数。比如,参数type对应的值io1,还有参数iopsPerGB,都是EBS专用的参数。当参数省略时,就会使用它的默认值。

AWS

...

GCE

...

Glusterfs

...

OpenStack Cinder

...

vSphere

...

Ceph RBD

  apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    name: fast
  provisioner: kubernetes.io/rbd
  parameters:
    monitors: 10.16.153.105:6789
    adminId: kube
    adminSecretName: ceph-secret
    adminSecretNamespace: kube-system
    pool: kube
    userId: kube
    userSecretName: ceph-secret-user

  ● monitors:Ceph的monitor,逗号分隔。该参数是必须的。
  ● adminId:Ceph的客户端ID,可在pool中创建镜像。默认的是“admin”。
  ● adminSecretNamespace:adminSecret的命名空间,默认值是“default”。
  ● adminSecretNameadminId的Secret Name。改参数是必须的,提供的秘钥必须有类型“kubernetes.io/rbd”。
  ● pool:Ceph的RBD pool,默认值是“rbd”。
  ● userId:Ceph的客户ID,用于映射RBD镜像的,默认值和adminId参数相同。
  ● userSecretName:Ceph Secret的名称,userId用该参数来映射RBD镜像。它必须和PVC在相同的命名空间。该参数也是必须的。提供的秘钥必须有类型“kubernetes.io/rbd”。比如,按照下面的方式来创建:

$ kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' --namespace=kube-system

Quobyte

...

Azure Disk

...

Portworx Volume

...

ScaleIO

...

配置

  如果你在写配置模板和示例,用于在需要持久化存储的集群中使用,那么,我们建议你使用以下的一些模式:
   ● 在你的捆绑配置(如Deployment、ConfigMap胖)中包含PVC对象。
   ● 在配置中不要包含PersistentVolume对象,因为实例化配置的用户可能没有创建PersistentVolumes的权限
   ● 当用户提供实例化模板时,给用户提供存储类名称的选项。
    ▷ 如果用户提供了一个StorageClass名称,并且Kubernetes版本是1.4及以上,那么将该值设置在PVC的volume.beta.kubernetes.io/storage-class标注上。这会使得PVC匹配到正确的StorageClass
    ▷ 如果用户没有提供StorageClass名称,或者集群版本是1.3,那么久需要在PVC配置中设置volume.alpha.kubernetes.io/storage-class: default标注。
     ☞ 这会使得在一些默认配置健全的集群中,PV可以动态的提供给用户。
     ☞ 尽管在名称中包含了alpha单词,但是该标注对应的代码有着beta级别的支持。
     ☞ 不要使用volume.beta.kubernetes.io/storage-class,无论设置什么值,甚至是空字符串。因为它会阻止DefaultStorageClass许可控制器。
   ● 在你的工具中,要监视那些一段时间后还没有获得绑定的PVC,并且展示给用户。因为这可能表明集群没有支持动态存储(此时我们应该创建匹配的PV),或者集群没有存储系统(此时用户不能部署需要PVC的情况)。
   ● 未来,我们期望大多数集群都可以使能DefaultStorageClass,并且能有一些可用的存储形式。然而,可能没有行在所有集群都能运的StorageClass,所以默认情况下不要只设置一种。在某些时候,alpha标注将不再具有意义,但复位PVC的storageClass字段将具有所需的效果。