PersistentVolume(PV)和PersistentVolumeClaim(PVC)

这两个概念用于pod和volume之间解耦。Pod根据自己的需要提出数据卷的申请,k8s系统将符合条件的数据卷返回给pod。这样一来pod就无需直接和数据卷本身强绑定了。Pod无需知道数据卷的细节信息,比如具体用的是什么存储。

Pod中对数据卷的申请为PVC,用来和PVC绑定的数据卷称为PV。

PV可以有多种存储方式,比如NFS,iSCSI等。

PVC是使用PV资源的声明。

PVC和PV的关系是一对一的, 不能将多个PVC绑定到同一个PV上.PV和PVC的生命周期

供应阶段

PV有两种供应方式

  • static 静态方式由系统管理员创建PV
  • dynamic 如果没有静态的PV。系统会动态创建出PV来满足PVC。PV的提供者为StorageClass。要使用动态供应,PVC必须要指定一个StorageClass。如果StorageClass设置为空,那么针对这个PVC的动态供应特性会被禁用。

绑定阶段

这个阶段指的是PV和PVC绑定的过程。系统有一个机制,循环检查新创建的PVC,然后找到一个符合条件的PV,把他们绑定到一起。如果有多个满足要求的PV可以绑定,会使用消耗资源最小的那个。PV和PVC之间是一对一的关系。如果找不到符合条件的PV,PVC会一致保持未绑定状态。

使用阶段

PVC绑定合适的PV之后,Pod使用这个PV。和PVC绑定并处于使用状态的PV不会被系统删除,防止数据丢失。

如果用户删除了Pod正在使用的PVC,这个PVC不会被立即移除。直到这个PVC不被任何Pod时候的时候,才会被真正的删除。

重新声明阶段

当PV不再使用的时候,k8s根据不同的策略来回收这些PV。

Retain

Retain策略在volume不再使用的时候,不删除旧的数据,等待管理员处理。

Delete

Delete策略会删除不再使用的PV,同时删除这些PV对应真实存储的资源。

Recycle(已废弃)

删除volume内的内容,使得volume可供下次使用。

PV描述文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2

PV的属性

Capacity

PV的容量

VolumeMode

可以使用Filesystem或者Block。Filesystem为默认值。

AccessModes

具有如下可选值:

  • ReadWriteOnce:可以被一个node读写。缩写为RWO。
  • ReadOnlyMany:可以被多个node读取。缩写为ROX。
  • ReadWriteMany:可以摆多个node读写。缩写为RWX。

Class

配置storageClassName。

storage class, 如果使用k3s, rancher lab默认为我们提供了一个local-path, 如果不填写storageClassName, 则默认就是它

1
2
3
[root@VM-33-122-centos ~]# kubectl get storageclasses.storage.k8s.io 
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 41d

Reclaim Policy

可以使用如下回收策略:

  • Retain 手工回收
  • Recycle (rm -rf /thevolume/*)
  • Delete 删除相关Volume

Mount Options

可以指定存储的类型。

Node Affinity

Local volume必须要设定这个属性。该属性规定了volume可以通过哪些node访问。

PV的阶段(Phase)

  • Available:可用状态,没有被任何PVC绑定。
  • Bound:已绑定到了PVC。
  • Released:PVC已删除,但是资源没有被集群回收。
  • Failed:PV自动回收失败。

PVC的描述文件

pv和pvc的绑定规则

当 PVC 绑定 PV 时,需考虑以下参数来筛选当前集群内是否存在满足条件的 PV

参数描述
VolumeMode主要定义 volume 是文件系统(FileSystem)类型还是块(Block)类型,PV 与 PVC 的 VolumeMode 标签必须相匹配。
StorageclassPV 与 PVC 的 storageclass 类名必须相同(或同时为空)。
AccessMode主要定义 volume 的访问模式,PV 与 PVC 的 AccessMode 必须相同。
Size主要定义 volume 的存储容量,PVC 中声明的容量必须小于等于 PV,如果存在多个满足条件的 PV,则选择最小的 PV 与 PVC 绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- {key: environment, operator: In, values: [dev]}

PVC的配置参数

AccessModes

访问模式。和PV相同。

VolumeMode

volume的挂载模式,可以为Filesystem或block。

Resources

约定声明PV的大小等参数。

Selector

用于限制volume,只有label匹配的volume才能绑定到PVC上。可以使用matchLabels或者matchExpressions来约束。

Class

用于实现动态供给。只有StorageClassName相同的PV和PVC才能够绑定到一起。如果PVC的StorageClassName设置为"",那么它只会和StorageClassName也为""的PV绑定到一起。

创建StorageClass的时候如果将它的storageclass.kubernetes.io/is-default-class annotation设置为true,那么此StorageClass会成为默认的StorageClass。如果PVC没有指定StorageClassName,那么它会和StorageClassName为默认StorageClass的PV绑定到一起。当然,使用这个特性必须要开启DefaultStorageClass的admission plugin。

如果没有启动这个admission plugin。没有指定StorageClass的PVC只能和没有指定StorageClass的PV绑定在一起。没有指定StorageClass和设置StorageClass的值为"",含义是相同的。

在Pod中使用PVC

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

LocalPersistentVolume

这种类型的PV把数据储存在集群中某个节点上。

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

注意:nodeAffinity属性是必须要指定的,因为local模式一定要确定数据保存在哪个机器。

对应StorageClass如下:

1
2
3
4
5
6
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

StorageClass

样例

  • 以下创建一个使用 hostpath 的底层存储的 StorageClass
1
2
3
4
5
6
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: prometheus-store
provisioner: kubernetes.io/host-path
reclaimPolicy: Delete
  • 之后就可以在 PVC(PersistentVolumeClaim)中使用它
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-pvc
# 需要注意pvc和pod一定要处于同一个namespace下
namespace: test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: prometheus-store
  • 当 PVC 创建成功后,Kubernetes 将会自动为其创建一个 PV(PersistentVolume)。PV 是集群中的实际存储资源,用于满足 PVC 的请求。Kubernetes 会根据 storageClass 的配置,选择一个满足 PVC 要求的 PV。如果没有可用的 PV,则 Kubernetes 将会自动创建一个 PV。如果在创建 PVC 后,Kubernetes 没有自动创建 PV,可能原因如下

    • 没有配置动态存储卷。如果集群中没有配置动态存储卷,Kubernetes 就无法根据 PVC 的需求自动创建 PV。在这种情况下,需要手动创建 PV,并将其绑定到 PVC 上。

    • 没有合适的 PV 可用。如果没有已经存在的 PV 能够满足 PVC 的需求,并且也没有配置动态存储卷,Kubernetes 就无法自动创建 PV。在这种情况下,可以手动创建一个 PV,并将其绑定到 PVC 上。如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      apiVersion: v1
      kind: PersistentVolume
      metadata:
      name: prometheus-pv
      spec:
      accessModes:
      - ReadWriteOnce
      capacity:
      storage: 50Gi
      hostPath:
      path: /var/data/prometheus
      type: DirectoryOrCreate
      persistentVolumeReclaimPolicy: Retain
      storageClassName: prometheus-store
      volumeMode: Filesystem
  • 将创建的 PVC 绑定到 Pod 中。可以通过在 Pod 的 YAML 文件中指定 volumesvolumeMounts 字段来完成。例如,以下 YAML 文件创建了一个 Pod,并将 prometheus-pvc PVC 挂载到了容器的 /data 目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
yamlCopy codeapiVersion: v1
kind: Pod
metadata:
name: my-pod
namespace: test
spec:
containers:
- name: my-container
image: nginx
volumeMounts:
- name: my-volume
mountPath: /data
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: prometheus-pvc

需要注意的是,使用 hostpath 存储有一些限制,因为它仅在单个节点上工作,并且不能在 Pod 迁移时保留数据。如果需要在生产环境中使用持久化存储,建议考虑使用更稳定和可靠的解决方案,例如 NFS 或 AWS EBS 等。

Provisioner(供应者)

Kubernetes 中自带的 Provisioner有以下类型

  1. kubernetes.io/aws-ebs:用于提供 AWS Elastic Block Store(EBS)存储。
  2. kubernetes.io/gce-pd:用于提供 Google Compute Engine(GCE)Persistent Disk(PD)存储。
  3. kubernetes.io/glusterfs:用于提供 GlusterFS 存储。
  4. kubernetes.io/nfs:用于提供 NFS 存储。
  5. kubernetes.io/rbd:用于提供 Ceph RBD 存储。
  6. kubernetes.io/cinder:用于提供 OpenStack Cinder 存储。
  7. kubernetes.io/azure-disk:用于提供 Azure Disk 存储。
  8. kubernetes.io/azure-file:用于提供 Azure File 存储。

使用这些自带的 Provisioner,可以轻松地将不同类型的存储资源集成到 Kubernetes 集群中,从而为应用程序提供持久化存储支持。需要注意的是,不同的 Provisioner 支持的存储类型和功能也不同,应该选择最适合您的应用程序和环境的 Provisioner。

K3s

如果使用k3s, rancher lab默认为我们提供了一个local-path(local-path只有ReadWriteOnce的权限)

1
2
3
[root@VM-33-122-centos ~]# kubectl get storageclasses.storage.k8s.io 
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 41d

rancher.io/local-pathkubernetes.io/host-path的区别

rancher.io/local-pathkubernetes.io/host-path 都是 Kubernetes 中用于提供基于主机路径的存储的 Provisioner,它们的原理和功能类似,都是在宿主机上创建一个目录,用于提供持久化存储。

不过,rancher.io/local-pathkubernetes.io/host-path 在实现细节和特性上有一些不同,具体如下:

  1. 实现方式:kubernetes.io/host-path 是 Kubernetes 原生的 Provisioner,由 Kubernetes 组件直接实现;而 rancher.io/local-path 则是 Rancher 自己开发的 Provisioner,基于 hostPath 的 PVC Controller 实现。
  2. 特性支持:相较于 kubernetes.io/host-pathrancher.io/local-path 支持更多的特性,例如:
    • 多副本模式:支持在多个节点上创建相同的 PV;
    • 动态 PVC 配额:支持为 PVC 动态分配存储空间;
    • 数据迁移:支持在 Pod 迁移时将数据迁移到新的宿主机上。
  3. 安全性:相较于 kubernetes.io/host-pathrancher.io/local-path 的安全性更高。由于 Kubernetes 的 hostPath 特性会将宿主机上的目录直接暴露给 Pod,可能存在潜在的安全风险;而 rancher.io/local-path 则使用了一些安全措施,例如通过 Kubernetes Secret 来存储目录的访问控制信息,从而降低了安全风险。

综上所述,rancher.io/local-path 在实现细节和特性上都比 kubernetes.io/host-path 更为灵活和安全,因此建议在 Rancher 环境中使用 rancher.io/local-path

StorageClass 的选择和 PV/PVC 的绑定关系

StorageClass 的选择与 PV/PVC 之间的绑定关系见下图: