Persistent Volume

PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 提供了方便的持久化卷:PV 提供网络存储资源,而 PVC 请求存储资源。这样,设置持久化的工作流包括配置底层文件系统或者云数据卷、创建持久性数据卷、最后创建 PVC 来将 Pod 跟数据卷关联起来。PV 和 PVC 可以将 pod 和数据卷解耦,pod 不需要知道确切的文件系统或者支持它的持久化引擎。

Volume 生命周期

Volume 的生命周期包括 5 个阶段

  1. Provisioning,即 PV 的创建,可以直接创建 PV(静态方式),也可以使用 StorageClass 动态创建
  2. Binding,将 PV 分配给 PVC
  3. Using,Pod 通过 PVC 使用该 Volume,并可以通过准入控制 StorageObjectInUseProtection(1.9 及以前版本为 PVCProtection)阻止删除正在使用的 PVC
  4. Releasing,Pod 释放 Volume 并删除 PVC
  5. Reclaiming,回收 PV,可以保留 PV 以便下次使用,也可以直接从云存储中删除
  6. Deleting,删除 PV 并从云存储中删除后段存储

根据这 5 个阶段,Volume 的状态有以下 4 种

  • Available:可用
  • Bound:已经分配给 PVC
  • Released:PVC 解绑但还未执行回收策略
  • Failed:发生错误

API 版本对照表

Kubernetes 版本 PV/PVC 版本 StorageClass 版本
v1.5-v1.6 core/v1 storage.k8s.io/v1beta1
v1.7+ core/v1 storage.k8s.io/v1

PV

PersistentVolume(PV)是集群之中的一块网络存储。跟 Node 一样,也是集群的资源。PV 跟 Volume (卷) 类似,不过会有独立于 Pod 的生命周期。比如一个 NFS 的 PV 可以定义为

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv0003
  5. spec:
  6. capacity:
  7. storage: 5Gi
  8. accessModes:
  9. - ReadWriteOnce
  10. persistentVolumeReclaimPolicy: Recycle
  11. nfs:
  12. path: /tmp
  13. server: 172.17.0.2

PV 的访问模式(accessModes)有三种:

  • ReadWriteOnce(RWO):是最基本的方式,可读可写,但只支持被单个节点挂载。
  • ReadOnlyMany(ROX):可以以只读的方式被多个节点挂载。
  • ReadWriteMany(RWX):这种存储可以以读写的方式被多个节点共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是 NFS。在 PVC 绑定 PV 时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。

PV 的回收策略(persistentVolumeReclaimPolicy,即 PVC 释放卷的时候 PV 该如何操作)也有三种

  • Retain,不清理, 保留 Volume(需要手动清理)
  • Recycle,删除数据,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持)
  • Delete,删除存储资源,比如删除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)

StorageClass

上面通过手动的方式创建了一个 NFS Volume,这在管理很多 Volume 的时候不太方便。Kubernetes 还提供了 StorageClass 来动态创建 PV,不仅节省了管理员的时间,还可以封装不同类型的存储供 PVC 选用。

StorageClass 包括四个部分

  • provisioner:指定 Volume 插件的类型,包括内置插件(如 kubernetes.io/glusterfs)和外部插件(如 external-storage 提供的 ceph.com/cephfs)。
  • mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hardnfsvers=4.1 等选项。
  • parameters:指定 provisioner 的选项,比如 kubernetes.io/aws-ebs 支持 typezoneiopsPerGB 等参数。
  • reclaimPolicy:指定回收策略,同 PV 的回收策略。

在使用 PVC 时,可以通过 DefaultStorageClass 准入控制设置默认 StorageClass, 即给未设置 storageClassName 的 PVC 自动添加默认的 StorageClass。而默认的 StorageClass 带有 annotation storageclass.kubernetes.io/is-default-class=true

Volume Plugin Internal Provisioner Config Example
AWSElasticBlockStore AWS
AzureFile Azure File
AzureDisk Azure Disk
CephFS - -
Cinder OpenStack Cinder
FC - -
FlexVolume - -
Flocker -
GCEPersistentDisk GCE
Glusterfs Glusterfs
iSCSI - -
PhotonPersistentDisk -
Quobyte Quobyte
NFS - -
RBD Ceph RBD
VsphereVolume vSphere
PortworxVolume Portworx Volume
ScaleIO ScaleIO
StorageOS StorageOS
Local - Local

修改默认 StorageClass

取消原来的默认 StorageClass

  1. kubectl patch storageclass <default-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

标记新的默认 StorageClass

  1. kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

GCE 示例

单个 GCE 节点最大支持挂载 16 个 Google Persistent Disk。开启 AttachVolumeLimit 特性后,根据节点的类型最大可以挂载 128 个。

  1. kind: StorageClass
  2. apiVersion: storage.k8s.io/v1
  3. metadata:
  4. name: slow
  5. provisioner: kubernetes.io/gce-pd
  6. parameters:
  7. type: pd-standard
  8. zone: us-central1-a

Glusterfs 示例

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: slow
  5. provisioner: kubernetes.io/glusterfs
  6. parameters:
  7. resturl: "http://127.0.0.1:8081"
  8. clusterid: "630372ccdc720a92c681fb928f27b53f"
  9. restauthenabled: "true"
  10. restuser: "admin"
  11. secretNamespace: "default"
  12. secretName: "heketi-secret"
  13. gidMin: "40000"
  14. gidMax: "50000"
  15. volumetype: "replicate:3"

OpenStack Cinder 示例

  1. kind: StorageClass
  2. apiVersion: storage.k8s.io/v1
  3. metadata:
  4. name: gold
  5. provisioner: kubernetes.io/cinder
  6. parameters:
  7. type: fast
  8. availability: nova

Ceph RBD 示例

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: fast
  5. provisioner: kubernetes.io/rbd
  6. parameters:
  7. monitors: 10.16.153.105:6789
  8. adminId: kube
  9. adminSecretName: ceph-secret
  10. adminSecretNamespace: kube-system
  11. pool: kube
  12. userId: kube
  13. userSecretName: ceph-secret-user

Local Volume

Local Volume 允许将 Node 本地的磁盘、分区或者目录作为持久化存储使用。注意,Local Volume 不支持动态创建,使用前需要预先创建好 PV。

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: example-pv
  5. spec:
  6. capacity:
  7. storage: 100Gi
  8. # volumeMode field requires BlockVolume Alpha feature gate to be enabled.
  9. volumeMode: Filesystem
  10. accessModes:
  11. - ReadWriteOnce
  12. persistentVolumeReclaimPolicy: Delete
  13. storageClassName: local-storage
  14. local:
  15. path: /mnt/disks/ssd1
  16. nodeAffinity:
  17. required:
  18. nodeSelectorTerms:
  19. - matchExpressions:
  20. - key: kubernetes.io/hostname
  21. operator: In
  22. values:
  23. - example-node
  24. ---
  25. kind: StorageClass
  26. apiVersion: storage.k8s.io/v1
  27. metadata:
  28. name: local-storage
  29. provisioner: kubernetes.io/no-provisioner
  30. volumeBindingMode: WaitForFirstConsumer

推荐配置

  • 对于需要强 IO 隔离的场景,推荐使用整块磁盘作为 Volume
  • 对于需要容量隔离的场景,推荐使用分区作为 Volume
  • 避免在集群中重新创建同名的 Node(无法避免时需要先删除通过 Affinity 引用该 Node 的 PV)
  • 对于文件系统类型的本地存储,推荐使用 UUID (如 ls -l /dev/disk/by-uuid)作为系统挂载点
  • 对于无文件系统的块存储,推荐生成一个唯一 ID 作软链接(如 /dev/dis/by-id)。这可以保证 Volume 名字唯一,并不会与其他 Node 上面的同名 Volume 混淆

PVC

PV 是存储资源,而 PersistentVolumeClaim (PVC) 是对 PV 的请求。PVC 跟 Pod 类似:Pod 消费 Node 资源,而 PVC 消费 PV 资源;Pod 能够请求 CPU 和内存资源,而 PVC 请求特定大小和访问模式的数据卷。

  1. kind: PersistentVolumeClaim
  2. apiVersion: v1
  3. metadata:
  4. name: myclaim
  5. spec:
  6. accessModes:
  7. - ReadWriteOnce
  8. resources:
  9. requests:
  10. storage: 8Gi
  11. storageClassName: slow
  12. selector:
  13. matchLabels:
  14. release: "stable"
  15. matchExpressions:
  16. - {key: environment, operator: In, values: [dev]}

PVC 可以直接挂载到 Pod 中:

  1. kind: Pod
  2. apiVersion: v1
  3. metadata:
  4. name: mypod
  5. spec:
  6. containers:
  7. - name: myfrontend
  8. image: dockerfile/nginx
  9. volumeMounts:
  10. - mountPath: "/var/www/html"
  11. name: mypd
  12. volumes:
  13. - name: mypd
  14. persistentVolumeClaim:
  15. claimName: myclaim

扩展 PV 空间

ExpandPersistentVolumes 在 v1.8 开始 Alpha,v1.11 升级为 Beta 版。

v1.8 开始支持扩展 PV 空间,支持在不丢失数据和重启容器的情况下扩展 PV 的大小。注意, 当前的实现仅支持不需要调整文件系统大小(XFS、Ext3、Ext4)的 PV,并且只支持以下几种存储插件

  • AzureDisk
  • AzureFile
  • gcePersistentDisk
  • awsElasticBlockStore
  • Cinder
  • glusterfs
  • rbd
  • Portworx

开启扩展 PV 空间的功能需要配置

  • 开启 ExpandPersistentVolumes 功能,即配置 --feature-gates=ExpandPersistentVolumes=true
  • 开启准入控制插件 PersistentVolumeClaimResize,它只允许扩展明确配置 allowVolumeExpansion=true 的 StorageClass,比如
  1. kind: StorageClass
  2. apiVersion: storage.k8s.io/v1
  3. metadata:
  4. name: gluster-vol-default
  5. provisioner: kubernetes.io/glusterfs
  6. parameters:
  7. resturl: "http://192.168.10.100:8080"
  8. restuser: ""
  9. secretNamespace: ""
  10. secretName: ""
  11. allowVolumeExpansion: true

这样,用户就可以修改 PVC 中请求存储的大小(如通过 kubectl edit 命令)请求更大的存储空间。

块存储(Raw Block Volume)

Kubernetes v1.9 新增了 Alpha 版的 Raw Block Volume,可通过设置 volumeMode: Block(可选项为 FilesystemBlock)来使用块存储。

注意:使用前需要为 kube-apiserver、kube-controller-manager 和 kubelet 开启 BlockVolume 特性,即添加命令行选项 --feature-gates=BlockVolume=true,...

支持块存储的 PV 插件包括

  • Local Volume
  • fc
  • iSCSI
  • Ceph RBD
  • AWS EBS
  • GCE PD
  • AzureDisk
  • Cinder

使用示例

  1. # Persistent Volumes using a Raw Block Volume
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: block-pv
  6. spec:
  7. capacity:
  8. storage: 10Gi
  9. accessModes:
  10. - ReadWriteOnce
  11. volumeMode: Block
  12. persistentVolumeReclaimPolicy: Retain
  13. fc:
  14. targetWWNs: ["50060e801049cfd1"]
  15. lun: 0
  16. readOnly: false
  17. ---
  18. # Persistent Volume Claim requesting a Raw Block Volume
  19. apiVersion: v1
  20. kind: PersistentVolumeClaim
  21. metadata:
  22. name: block-pvc
  23. spec:
  24. accessModes:
  25. - ReadWriteOnce
  26. volumeMode: Block
  27. resources:
  28. requests:
  29. storage: 10Gi
  30. ---
  31. # Pod specification adding Raw Block Device path in container
  32. apiVersion: v1
  33. kind: Pod
  34. metadata:
  35. name: pod-with-block-volume
  36. annotations:
  37. # apparmor should be unconfied for mounting the device inside container.
  38. container.apparmor.security.beta.kubernetes.io/fc-container: unconfined
  39. spec:
  40. containers:
  41. - name: fc-container
  42. image: fedora:26
  43. command: ["/bin/sh", "-c"]
  44. args: ["tail -f /dev/null"]
  45. securityContext:
  46. capabilities:
  47. # CAP_SYS_ADMIN is required for mount() syscall.
  48. add: ["SYS_ADMIN"]
  49. volumeDevices:
  50. - name: data
  51. devicePath: /dev/xvda
  52. volumes:
  53. - name: data
  54. persistentVolumeClaim:
  55. claimName: block-pvc

StorageObjectInUseProtection

准入控制 StorageObjectInUseProtection 在 v1.11 版本 GA。

当开启准入控制 StorageObjectInUseProtection(--admission-control=StorageObjectInUseProtection)时,删除使用中的 PV 和 PVC 后,它们会等待使用者删除后才删除(而不是之前的立即删除)。而在使用者删除之前,它们会一直处于 Terminating 状态。

拓扑感知动态调度

拓扑感知动态存储卷调度(topology-aware dynamic provisioning)是 v1.12 版本的一个 Beta 特性,用来支持在多可用区集群中动态创建和调度持久化存储卷。目前的实现支持以下几种存储:

  • AWS EBS
  • Azure Disk
  • GCE PD (including Regional PD)
  • CSI (alpha) - currently only the GCE PD CSI driver has implemented topology support

使用示例

  1. # set WaitForFirstConsumer in storage class
  2. kind: StorageClass
  3. apiVersion: storage.k8s.io/v1
  4. metadata:
  5. name: topology-aware-standard
  6. provisioner: kubernetes.io/gce-pd
  7. volumeBindingMode: WaitForFirstConsumer
  8. parameters:
  9. type: pd-standard
  10. # Refer storage class
  11. apiVersion: apps/v1
  12. kind: StatefulSet
  13. metadata:
  14. name: web
  15. spec:
  16. serviceName: "nginx"
  17. replicas: 2
  18. selector:
  19. matchLabels:
  20. app: nginx
  21. template:
  22. metadata:
  23. labels:
  24. app: nginx
  25. spec:
  26. affinity:
  27. nodeAffinity:
  28. requiredDuringSchedulingIgnoredDuringExecution:
  29. nodeSelectorTerms:
  30. - matchExpressions:
  31. - key: failure-domain.beta.kubernetes.io/zone
  32. operator: In
  33. values:
  34. - us-central1-a
  35. - us-central1-f
  36. podAntiAffinity:
  37. requiredDuringSchedulingIgnoredDuringExecution:
  38. - labelSelector:
  39. matchExpressions:
  40. - key: app
  41. operator: In
  42. values:
  43. - nginx
  44. topologyKey: failure-domain.beta.kubernetes.io/zone
  45. containers:
  46. - name: nginx
  47. image: gcr.io/google_containers/nginx-slim:0.8
  48. ports:
  49. - containerPort: 80
  50. name: web
  51. volumeMounts:
  52. - name: www
  53. mountPath: /usr/share/nginx/html
  54. - name: logs
  55. mountPath: /logs
  56. volumeClaimTemplates:
  57. - metadata:
  58. name: www
  59. spec:
  60. accessModes: [ "ReadWriteOnce" ]
  61. storageClassName: topology-aware-standard
  62. resources:
  63. requests:
  64. storage: 10Gi
  65. - metadata:
  66. name: logs
  67. spec:
  68. accessModes: [ "ReadWriteOnce" ]
  69. storageClassName: topology-aware-standard
  70. resources:
  71. requests:
  72. storage: 1Gi

然后查看 PV,可以发现它们创建在不同的可用区内

  1. $ kubectl get pv -o=jsonpath='{range .items[*]}{.spec.claimRef.name}{"\t"}{.metadata.labels.failure\-domain\.beta\.kubernetes\.io/zone}{"\n"}{end}'
  2. www-web-0 us-central1-f
  3. logs-web-0 us-central1-f
  4. www-web-1 us-central1-a
  5. logs-web-1 us-central1-a

存储快照

存储快照是 v1.12 新增的 Alpha 特性,用来支持给存储卷创建快照。支持的插件包括

image-20181014215558480

在使用前需要开启特性开关 VolumeSnapshotDataSource。

使用示例:

  1. # create snapshot
  2. apiVersion: snapshot.storage.k8s.io/v1alpha1
  3. kind: VolumeSnapshot
  4. metadata:
  5. name: new-snapshot-demo
  6. namespace: demo-namespace
  7. spec:
  8. snapshotClassName: csi-snapclass
  9. source:
  10. name: mypvc
  11. kind: PersistentVolumeClaim
  12. # import from snapshot
  13. apiVersion: snapshot.storage.k8s.io/v1alpha1
  14. kind: VolumeSnapshotContent
  15. metadata:
  16. name: static-snapshot-content
  17. spec:
  18. csiVolumeSnapshotSource:
  19. driver: com.example.csi-driver
  20. snapshotHandle: snapshotcontent-example-id
  21. volumeSnapshotRef:
  22. kind: VolumeSnapshot
  23. name: static-snapshot-demo
  24. namespace: demo-namespace
  25. # provision volume from snapshot
  26. apiVersion: v1
  27. kind: PersistentVolumeClaim
  28. metadata:
  29. name: pvc-restore
  30. Namespace: demo-namespace
  31. spec:
  32. storageClassName: csi-storageclass
  33. dataSource:
  34. name: new-snapshot-demo
  35. kind: VolumeSnapshot
  36. apiGroup: snapshot.storage.k8s.io
  37. accessModes:
  38. - ReadWriteOnce
  39. resources:
  40. requests:
  41. storage: 1Gi

参考文档