侧边栏壁纸
博主头像
问道

问道的小花园,总能给你带来惊喜

  • 累计撰写 68 篇文章
  • 累计创建 35 个标签
  • 累计收到 6 条评论

kubernetes存储知识辨析:pv、pvc和StorageClass的关系

问道
2022-08-18 / 0 评论 / 0 点赞 / 807 阅读 / 5,827 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-08-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

引子

kubernetes中的概念比较多,对入门或理解kubernetes会造成一定的难度,尤其是有些概念有相近之处,更加让初学者摸不着头脑。

所以对于概念之间的联立比较,区别它们之间的异同,对清晰的理解kubernetes中的有关概念有很大的帮助。

pv、pvc和StorageClass是kubernetes中存储的相关概念,相互联系又有区别,那如何来理解这三个概念呢?

概念简介

PersistentVolume(PV )

PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS 等,都是通过插件机制完成与共享存储的对接。除了EmptyDir类型的存储卷,PV的生命周期独立于使用它的Pod。

PersistentVolumeClaim(PVC

PVC是由用户进行存储的请求。它类似于pod。 Pod消耗节点资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。声明可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)。

StorageClass

StorageClass为管理员提供了一种描述他们提供的存储的“类”的方法。 不同的类可能映射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。

PV是运维人员来创建的,开发操作PVC,可是大规模集群中可能会有很多PV,如果这些PV都需要运维手动来处理这也是一件很繁琐的事情,所以就有了动态供给概念,也就是Dynamic Provisioning,动态供给的关键就是StorageClass,它的作用就是创建PV模板。创建StorageClass里面需要定义PV属性比如存储类型、大小等;另外创建这种PV需要用到存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass,则会为其自动创建PV并进行绑定。

pvc和pv的关系

PVC和PV相当于“接口”和“实现”,所以我们需要将PVC和PV绑定起来才可以使用,而PVC和PV绑定的时候需要满足:

  1. PV 和 PVC 的 spec 字段要匹配,比如PV 的存储(storage)大小,就必须满足 PVC 的要求。
  2. PV 和 PVC 的 storageClassName 字段必须一样才能进行绑定。storageClassName表示的是StorageClass的name属性。

如果我们想要在Pod中使用这个PVC,那么我们可以这么做:

apiVersion: v1
kind: Pod
metadata:
  name: mongodb
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: mongodb-data
    persistentVolumeClaim:
      claimName: mongodb-pvc

在Pod中只需要声明PVC的名字,等Pod创建后kubelet 就会把这个 PVC 所对应的 PV,也就是一个 GCE类型的 Volume,挂载在这个 Pod 容器内的目录上。

PersistentVolumeController会不断地查看当前每一个 PVC,是不是已经处于 Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的 PV,并尝试将其与这个“单身”的 PVC 进行绑定。这个PersistentVolumeController的源码我们下面会进行分析。那么问题来了,k8s为什么要将一个存储卷分成两部分呢?

创建PV

创建一个 hostPath 类型的 PV 资源对象:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-hostpath
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/data/k8s/test/hostpath"

PV资源对象需要设置的关键配置参数如下。

  1. 存储容量(Capacity):描述存储的容量,目前仅支持对存储空间的设置(storage=xx),未来可能加入IOPS、吞吐率等设置。

  2. 存储卷模式(Volume Modes)可以设置的选项包括Filesystem(文件系统,默认值)和Block(块设备)。文件系统模式的PV将以目录(Directory)形式挂载到Pod内。如果模式为块设备,但是设备是空的,则Kubernetes会自动在块设备上创建一个文件系统。支持块设备的存储类型会以裸设备(Raw Block Device)的形式挂载到容器内,并且不会创建任何文件系统,适用于需要直接操作裸设备(速度最快)的应用程序。

  3. 访问模式(Access Modes)

    ReadWriteOnce(RWO):读写权限,并且只能被单个Node挂载。

    ReadOnlyMany(ROX):只读权限,允许被多个Node挂载。

    ReadWriteMany(RWX):读写权限,允许被多个Node挂载。

  4. 存储类别(Class)PV可以设定其存储的类别,通过storageClassName参数指定一个StorageClass资源对象的名称。具有特定类别的PV只能与请求了该类别的PVC绑定。未设定类别的PV则只能与不请求任何类别的PVC绑定。

  5. 回收策略(Reclaim Policy)

    Retain:保留数据,需要手工处理。

    Recycle:简单清除文件的操作(例如运行rm-rf/thevolume/*命令)。

    Delete:与PV相连的后端存储完成Volume的删除操作。

  6. 挂载选项(Mount Options)在将PV挂载到一个Node上时,根据后端存储的特点,可能需要设置额外的挂载选项的参数,这个可以在PV定义中的mountOptions字段进行设置。

创建PVC

PVC作为用户对存储资源的需求申请,主要涉及存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。

PVC的的创建示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-hostpath
spec:
  storageClassName: manual
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

对PVC的关键配置参数说明如下。

  1. 资源请求(Resources):描述对存储资源的请求,通过resources.requests.storage字段设置需要的存储空间大小。
  2. 访问模式(Access Modes):PVC也可以设置访问模式,用于描述用户应用对存储资源的访问权限。其三种访问模式的设置与PV的设置相同。
  3. 存储卷模式(Volume Modes):PVC也可以设置存储卷模式,用于描述希望使用的PV存储卷模式,包括文件系统(Filesystem)和块设备(Block)。PVC设置的存储卷模式应该与PV存储卷模式相同,以实现绑定;如果不同,则可能出现不同的绑定结果。
  4. PV选择条件(Selector):通过Label Selector的设置,可使PVC对于系统中已存在的各种PV进行筛选。系统将根据标签选出合适的PV与该PVC进行绑定。对选择条件可以使用matchLabels和matchExpressions进行设置,如果两个字段都已设置,则Selector的逻辑将是两组条件同时满足才能完成匹配。
  5. 存储类别(Class):PVC在定义时可以设定需要的后端存储的类别(通过storageClassName字段指定),以减少对后端存储特性的详细信息的依赖。只有设置了该Class的PV才能被系统选出,并与该PVC进行绑定。PVC也可以不设置Class需求,如果storageClassName字段的值被设置为空(storageClassName=""),则表示该PVC不要求特定的Class,系统将只选择未设定Class的PV与之匹配和绑定。

pod使用pvc

在PVC创建成功之后,Pod就可以以存储卷(Volume)的方式使用PVC的存储资源了。PVC受限于命名空间,Pod在使用PVC时必须与PVC处于同一个命名空间。

Kubernetes为Pod挂载PVC的过程如下:系统在Pod所在的命名空间中找到其配置的PVC,然后找到PVC绑定的后端PV,将PV存储挂载到Pod所在Node的目录下,最后将Node的目录挂载到Pod的容器内。

apiVersion: v1
kind: Pod
metadata:
  name: pv-hostpath-pod
spec:
  volumes:
  - name: pv-hostpath
    persistentVolumeClaim:
      claimName: pvc-hostpath
  nodeSelector:
    kubernetes.io/hostname: ydzs-node1
  containers:
  - name: task-pv-container
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: pv-hostpath

PV和PVC之间的相互作用的生命周期

图片来自网络

Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling

  • 供应准备Provisioning---通过集群外的存储系统或者云平台来提供存储持久化支持。
      • 静态提供Static:集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费
      • 动态提供Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,集群可能会尝试为PVC动态配置卷。 此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用动态配置。
  • 绑定Binding---用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。
  • 使用Using---用户可在pod中像volume一样使用pvc。
  • 释放Releasing---用户删除pvc来回收存储资源,pv将变成“released”状态。由于还保留着之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他pvc使用。
  • 回收Recycling---pv可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。
      • 保留策略:允许人工处理保留的数据。
      • 删除策略:将删除pv和外部关联的存储资源,需要插件支持。
      • 回收策略:将执行清除操作,之后可以被新的pvc使用,需要插件支持。

StorageClass的Dynamic Provisioning

在上面我们说的PV和PVC绑定的过程称为Static Provisioning,需要手动的创建PV,我们在研发中可能有这样的情况,就是管理员没有及时给我们创建对应的PV,难道一直等着吗?所以这个时候就需要用到StorageClass了,StorageClass提供了Dynamic Provisioning机制,可以根据模板创建PV。

StorageClass 对象会定义如下两个部分内容:

  1. PV 的属性。比如,存储类型、Volume 的大小等等。
  2. 创建这种 PV 需要用到的存储插件。比如,Ceph 等等。

这样k8s就能够根据用户提交的 PVC,找到一个对应的 StorageClass ,然后调用该 StorageClass 声明的存储插件,创建出需要的 PV。

例如声明如下StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: block-service
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

这里定义了名叫 block-service 的 StorageClass,provisioner 字段的值是:kubernetes.io/gce-pd,这是k8s内置的存储插件,type字段也是跟着provisioner定义的,官方默认支持 Dynamic Provisioning 的内置存储插件:https://kubernetes.io/docs/concepts/storage/storage-classes/

然后就可以在PVC中声明storageClassName为block-service,当创建好PVC 对象之后,k8s就会调用相应的存储插件API创建一个PV对象。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: block-service
  resources:
    requests:
      storage: 30Gi

这种自动创建PV的机制就是Dynamic Provisioning,Kubernetes 就能够根据用户提交的 PVC,找到一个对应的 StorageClass ,然后会调用StorageClass 声明的存储插件,创建出需要的 PV。

需要注意的是,如果没有声明StorageClassName在PVC中,PVC 的 storageClassName 的值就是"",这也意味着它只能够跟 storageClassName 也是""的 PV 进行绑定。

总结

简单来讲,为了将数据长久保存在容器外,需要用到PV,应用通过PVC来连接对应的PV,从而消费存储。为了避免创建的PVC没有对应的PV,引入StorageClass来自动创建PV。

0

评论区