一、名词介绍

首先介绍几个 K8s 原生的概念名词:

  • PV - PersistVolume: 可被 Pod 挂载的持久化数据卷在 K8s 中表示
  • PVC - PersistVolumeClaim: 用于动态管理的持久化存储资源请求对象,定义需要的存储类型,大小,访问方式等
  • StorageClass:定义动态管理行为的资源对象,一般由管理员维护,定义存储资源的来源,权限,分配回收策略等
  • provisioner:运行在集群中,执行实际管理动作的工作负载。主要承担数据卷的 create、delete 等工作。
  • csi (Container Storage Interface): 旨在能为容器编排引擎和存储系统间建立一套标准的存储调用接口,通过该接口能为容器编排引擎提供存储服务。K8s 1.13 版本对 csi 支持进入了 GA(General Availability)状态
  • ceph-csi:ceph 社区的 csi 实现(我们的K8S服务就是使用的ceph-csi对接的叶权他们那边的存储)。

PV 通常代表一个存储卷,PVCPV是一一对应关系,通常一个PV必须被Bound到一个PVC上,才能被Pod访问和使用。

Kubernetes从1.4版本开始引入了一个新的资源对象StorageClass(动态管理),用于标记存储资源的特性和性能。到1.6版本时,StorageClass和动态资源供应的机制得到了完善,实现了存储卷的按需创建,在共享存储的自动化管理进程中实现了重要的一步。

动态管理的场景中,PV 不需要预先创建,而是由PVC中关联的StorageClass,根据PVC中指定的卷大小、访问模式等参数,动态创建得来

注意:只是动态管理是不需要预先创建PV,在StorageClass之前是需要管理员手动创建的。

PV,PVC,StorageClass都属于共享存储

Kubernetes支持两种资源的供应模式:静态模式(Static)和动态模式(Dynamic)。资源供应的结果就是创建好的PV。

  • 静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置。
  • 动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种类型。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及与PVC的绑定。PVC可以声明Class为"",说明该PVC禁止使用动态模式。

二、为什么会有这么多存储定义?

为了方便开发人员更加容易的使用存储出现了PV,PVC,StorageClass三个概念。

通常我们在一个POD中定义使用存储是这样的方式,我们以hostpath类型来说:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: nginx
    name: mynginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: html
  volumes:
  - name: html # 名称
    hostPath: # 存储类型
      path: /data # 物理节点上的真实路径
      type: Directory # 如果该路径不存在讲如何处理,Directory是要求目录必须存在

其实通过上面可以看出来,无论你使用什么类型的存储你都需要手动定义,指明存储类型以及相关配置。

这里的hostpath类型还是比较简单的,如果是其他类型的比如分布式存储,那么这对开发人员来说将会是一种挑战,因为毕竟真正的存储是由存储管理员来设置的他们会更加了解,那么有没有一种方式让我们使用存储更加容易,对上层使用人员屏蔽底层细节呢?

答案是肯定的,这就是PV、PVC的概念。

不过需要注意的是我们在集群中通常不使用hostPath、emptyDir这种类型,除非你只是测试使用。

三、PV

PV全称叫做Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的。这个通常都是有运维或者数据存储工程师来定义。

比如下面我们定义一个NFS类型的PV:

apiVersion: v1
kind: PersistentVolume
metadata:  # PV建立不要加名称空间,因为PV属于集群级别的
  name: nfs-pv001  # PV名称
  labels: # 这些labels可以不定义
    name: nfs-pv001
    storetype: nfs
spec:  # 这里的spec和volumes里面的一样
  storageClassName: normal
  accessModes:  # 设置访问模型
    - ReadWriteMany
    - ReadWriteOnce
    - ReadOnlyMany
  capacity: # 设置存储空间大小
    storage: 500Mi
  persistentVolumeReclaimPolicy: Retain # 回收策略
  nfs:
    path: /work/volumes/v1
    server: stroagesrv01.contoso.com

accessModes:支持三种类型

  • ReadWriteMany:多路读写,卷能被集群多个Node挂载并读写

  • ReadWriteOnce:单路读写,卷只能被单一集群Node挂载读写

  • ReadOnlyMany:多路只读,卷能被多个集群Node挂载且只能读

这里的访问模型总共有三种,但是不同的存储类型支持的访问模型不同,具体支持什么需要查询官网。

比如我们这里使用nfs,它支持全部三种。但是ISCI就不支持ReadWriteMany;HostPath就不支持ReadOnlyMany和ReadWriteMany。

persistentVolumeReclaimPolicy:也有三种策略,这个策略是当与之关联的PVC被删除以后,这个PV中的数据如何被处理

  • Retain:保留数据,需要手工处理。回收策略 Retain 使得用户可以手动回收资源。当删除与之绑定的PVC时候,这个PV被标记为released(PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV上,但是该PV不可用,需要手动来处理这些数据并删除该PV。

  • Delete:当删除与之绑定的PVC时候

  • Recycle:这个在1.14版本中以及被废弃,取而代之的是推荐使用动态存储供给策略,它的功能是当删除与该PV关联的PVC时,自动删除该PV中的所有数据

注意:PV必须先于POD创建,而且只能是网络存储不能属于任何Node,虽然它支持HostPath类型但由于你不知道POD会被调度到哪个Node上,所以你要定义HostPath类型的PV就要保证所有节点都要有HostPath中指定的路径。

四、PVC

PVC全称是Persistent Volume Claim,也就是持久化存储声明。

PVC是用来描述希望使用什么样的或者说是满足什么条件的存储,它的开发人员使用这个来描述该容器需要一个什么存储

比如下面使用NFS的PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc001
  namespace: default
  labels: # 这些labels可以不定义
    name: nfs-pvc001
    storetype: nfs
    capacity: 500Mi
spec:
  storageClassName: normal
  accessModes:  # PVC也需要定义访问模式,不过它的模式一定是和现有PV相同或者是它的子集,否则匹配不到PV
  - ReadWriteMany
  resources: # 定义资源要求PV满足这个PVC的要求才会被匹配到
    requests:
      storage: 500Mi  # 定义要求有多大空间

这个PVC就会和上面的PV进行绑定,为什么呢?它有一些原则:

  1. PV和PVC中的spec关键字段要匹配,比如存储(storage)大小。

  2. PV和PVC中的storageClassName字段必须一致,这个后面再说。

注意:上面的labels中的标签只是增加一些描述,对于PVC和PV的绑定没有关系。

应用了上面的PV和PVC,可以看到自动绑定了。

五、在POD中如何使用PVC呢

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      appname: myapp
  template:
    metadata:
      name: myapp
      labels:
        appname: myapp
    spec:
      containers:
      - name: myapp
        image: tomcat:8.5.38-jre8
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        volumeMounts:
          - name: tomcatedata
            mountPath : "/data"
      volumes:
        - name: tomcatedata
          persistentVolumeClaim:
            claimName: nfs-pvc001

六、StorageClass

PV 这个对象的创建,是由运维人员完成的。

但是,在大规模的生产环境里,这其实非常麻烦。因为,一个大规模的 Kubernetes 集群里很可能有成千上万个 PVC,这就意味着运维人员必须得事先创建出成千上万个 PV。更麻烦的是,随着新的 PVC 不断被提交,运维人员就不得不继续添加新的、能满足条件的 PV,否则新的 Pod 就会因为 PVC 绑定不到 PV 而失败。在实际操作中,这几乎没办法靠人工做到。

所以,Kubernetes 为我们提供了一套可以自动创建 PV 的机制,即:Dynamic Provisioning。相比之下,前面人工管理 PV 的方式就叫作 Static Provisioning。Dynamic Provisioning 机制工作的核心,在于一个名叫 StorageClass 的 API资源对象。

而 StorageClass资源对象的作用,其实就是创建 PV 的模板

具体地说,StorageClass 对象会定义如下两个部分内容:

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

有了这两个信息后,K8S就能够根据用户提交的PVC,找到一个对应的 StorageClass 了。然后,Kubernetes 就会调用该 StorageClass 声明的存储插件,创建出需要的 PV。

创建storageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: storage/nfs
reclaimPolicy: Delete
allowVolumeExpansion: True  #允许pvc创建后扩容

 创建pvc(指定用什么PV模板,即StorageClass):

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  # 这个名字要和上面创建的storageclass名称一致
  storageClassName: nfs-storage

创建Pod挂载pvc:

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  type: NodePort
  ports:
  - port: 3306
    nodePort: 30306
    protocol: TCP
    targetPort: 3306
  selector:
    app: mysql

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.7
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: my-pvc

七、其它存储相关

---------------------------------------------------------------------------------------------------------------------------------------

共享存储为分布式系统非常重要的一部分,存储一般要求稳定、可用、性能、可靠。

从用t户角度看


存储就是一块盘或者一个目录,用户不关心盘或者目录如何实现,用户要求非常“简单”,就是稳定,性能好。为了能够提供稳定可靠的存储产品,各个厂家推出了各种各样的存储技术和概念。为了能够让大家有一个整体认识,本文先介绍存储中的这些概念。

存储介质角度


存储介质分为机械硬盘和固态硬盘(SSD)。机械硬盘泛指采用磁头寻址的磁盘设备,包括SATA硬盘和SAS硬盘。由于采用磁头寻址,机械硬盘性能一般,随机IOPS一般在200左右,顺序带宽在150MB/s左右。固态硬盘是指采用Flash/DRAM芯片+控制器组成的设备,根据协议的不同,又分为SATA SSD,SAS SSD,PCIe SSD和NVMe SSD。

从产品定义角度


存储分为本地存储(DAS),网络存储(NAS),存储局域网(SAN)和软件定义存储(SDS)四大类

  • DAS就是本地盘,直接插到服务器上
  • NAS是指提供NFS协议的NAS设备,通常采用磁盘阵列+协议网关的方式
  • SAN跟NAS类似,提供SCSI/iSCSI协议,后端是磁盘阵列
  • SDS是一种泛指,包括分布式NAS(并行文件系统),ServerSAN等

从应用场景角度


存储分为文件存储(Posix/MPI),块存储(iSCSI/Qemu)和对象存储(S3/Swift)三大类。

Kubernetes是如何给存储定义和分类呢?Kubernetes中跟存储相关的概念有PersistentVolume (PV)和PersistentVolumeClaim(PVC),PV又分为静态PV和动态PV。静态PV方式如下:

动态PV需要引入StorageClass的概念,使用方式如下:

市面上的存储产品种类繁多,但是对于容器场景,主要集中在4种方案:分布式文件存储,分布式块存储,Local-Disk和传统NAS。

分布式块存储包括开源社区的Ceph,Sheepdog,商业产品中EMC的Scale IO,Vmware的vSAN等。分布式块存储不适合容器场景,关键问题是缺失RWX的特性。


 



参考:

k8s持久化存储方案

k8s学习笔记之五:volume,PV ,PVC

PV、PVC、StorageClass讲解

kubernetes【存储】1. 共享存储pv、pvc、StorageClass使用详解_ghostwritten的博客-CSDN博客_pv pvc storageclass

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐