PV PVC这种设计,是否有种过度设计的嫌疑

我们完全可以自我维护一套NFS或者Ceph服务器,不必学习Kubernetes,而开发人员,可以直接在Pod中声明Volumes,而不是PVC

看起来可以,但是如果Kubernetes内置的20种持久化数据卷实现,都没法满足这个容器存储的需求时候,该怎么办,而且,我们需要更换实际上提供PV的StorageClass,该怎么办?

而且,现在来说,我们使用远程存储服务,不如直接使用宿主机上的本地磁盘目录,而不是依赖于远程存储服务,来提供持久化的Volume

毕竟一个本地的SSD磁盘来提供持久化,可比远程存储快多了

所以,Kubernetes在v1.10之后.提供了一个Local Persistent Volume

当然,这个Local Persistent Volume不适合于所有的应用,适用的范围非常固定,高优先级的系统应用,对IO敏感,分布式数据存储 MongoDB Cassandra 分布式文件存储 Ceph,因为Local Persistent Volume节点宕机会导致数据的丢失,所以最好有数据备份或者同步的能力

那么,看起来比较容易实现的Local Persistent Volume,在我看来,就是hostPath 加上指定Node的NodeAffinity嘛

但是,不应该将一个宿主机上的目录当做PV使用,因为这种本地的存储其实并不可控,所在的磁盘随时都可能被应用写满,甚至导致宿主机宕机,因为并不具有物理上的隔离机制

一个Local Persistent Volume对应的存储,应该是一个额外挂载的宿主机上的磁盘或者块设备,额外的意思是指一个PV一个对应磁盘的含义

第二个难点在于,调度器如何保证Pod始终保证Pod能被正确的调度到所请求的Local Persistent Volume节点上?

造成这个问题是,Kubernetes是先调度Pod到某个节点上,然后在二阶段的处理这个机器上的Volume目录,完成Volume目录和容器的绑定挂载

可是,对于Local PV来说,磁盘是绑定在特定节点上的

对于这种情况,就需要调度器知道节点和Local Persistent Volume对应的磁盘关系,然后根据这个信息来调度Pod

这个原则,就是调度时候考虑Volume分布,在Kubernetes的调度器里,有一个VolumeBindingChecker的过滤条件来负责这个事情,有一个叫做VolumeBindingChecker的过滤条件来负责这件事

但是在实际使用这个Local Persistent Volume之前,首先在集群中配置好磁盘或者块设备

也就是额外挂载一个磁盘

我们可以先创建一个挂载点,用来模拟磁盘

然后为本地磁盘来创建对应的PV了

apiVersion: v1

kind: PersistentVolume

metadata:

name: example-pv

spec:

capacity:

storage: 5Gi

volumeMode: Filesystem

accessModes:

– ReadWriteOnce

persistentVolumeReclaimPolicy: Delete

storageClassName: local-storage

local:

path: /mnt/disks/vol1

nodeAffinity:

required:

nodeSelectorTerms:

– matchExpressions:

– key: kubernetes.io/hostname

operator: In

values:

– node-1

这个配置中,指定了这是一个Local Persisten Volume,指定了本地的路径 /mnt/disks/vo1

然后在nodeAffinity中指定了绑定的Pod,比如就是node-1,这样调度器在调度Pod的时候,就需要知道一个PV和节点对应的关系,做出正确的选择,这就是kubernetes需要在调度的时候考虑Volume分布的主要方法

然后我们看这个PV创建后,进入了Available状态

那么这样,我们在创建一个对应的PVC,来将PV进行绑定,为了方便绑定操作,我们还声明了一个StorageClass来描述这个PV

kind: StorageClass

apiVersion: storage.k8s.io/v1

metadata:

name: local-storage

provisioner: kubernetes.io/no-provisioner

volumeBindingMode: WaitForFirstConsumer

这个StorageClass指定的Provisioner是no-provisioner,这是Local Persistent Volume目前不支持Dynamic Provisioning,所以没法在创建PVC的时候,自动创建出对应的PV,也就是说创建PV的操作,是不可以省略的

除此外,StorageClass还定义了一个voluemBindingMode=WaitForFirstConsumer的属性,是Local Persistent Volume中的一个重要特性,延迟绑定

只有提交了PV和PVC的YAML文件,Kubernetes才能根据对应的属性,以及制定的StorageClass来进行绑定,绑定成功后,Pod才能通过声明这个PVC来使用PV

但是这个流程在Local 上并不好使,因为在Kubernetes集群中,有两个属性相同的Local类型的PV,其中第一个pv的名字是pv-1,对应的磁盘所在节点是node-1,第二个pv的名字叫做pv-2,所在节点是node-2

所以Kuberentes的Volume控制循环中,检查到了一个Pod使用的是pvc1 pvc-1和pv-1在node-1上进行绑定,这样Pod会被调度到

但是,Pod假如不被允许运行在node-1上的话,最后结果会导致这个Pod的调度失败了

那么,我们就需要延迟,PV和PVC绑定的时机

仍然是Pod先部署在某个节点,但是不会出现PV和PVC一旦符合就绑定的情况,而是在Pod部署在某个节点后,在进行绑定

也就可以认为,StorageClass 中的 volumeBindingMode=waitForFirstConsumer的含义,就是告诉Kubernetes中的Volume控制循环,虽然已经发现这个StorageClass关联的PVC和PV可以绑定在一起,但是不要现在就进行绑定

要等到第一个使用这个PVC的Pod出现在调度器之后,调度器综合考虑所有的调度规则,来决定这个PVC到底和那个PV绑定

也就是上面的Pod所使用的的pvc-1并不会和pv-1绑定,而是等Pod部署在了Node2之后,和pv-2绑定,并且Pod会被调度到node-2上

所以,这个延迟绑定机制,就是讲原本实时进行的绑定过程,延迟到了Pod第一次调度时候在调度器中进行,保证了绑定结果不会影响Pod的正常调度

而且,调度器还维护了一个和Volume Controller类似的控制循环,为这些声明了延迟绑定的PV和PVC进行绑定,确保了不拖累主绑定的性能,而且,一旦一个Pod的PVC没法完成绑定,则会将其放入调度队列,等待下一个周期

假如我们创建了这个StorageClass,这个StorageClassName是Local-storage,所以不会立刻进行绑定操作

然后可以考虑创建一个PVC来查看是否会绑定

$ kubectl get pvc

NAME                  STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS    AGE

example-local-claim   Pending                                       local-storage   7s

这样,PVC处于了Pending的状态,还是等待绑定

然后我们编写一个Pod来声明使用这个PVC

kind: Pod

apiVersion: v1

metadata:

name: example-pv-pod

spec:

volumes:

– name: example-pv-storage

persistentVolumeClaim:

claimName: example-local-claim

containers:

– name: example-pv-container

image: nginx

ports:

– containerPort: 80

name: “http-server”

volumeMounts:

– mountPath: “/usr/share/nginx/html”

name: example-pv-storage

这个Pod声明使用我们之前定义的PVC了

立刻编程Bound状态,与前面的PV绑定在一起

所以我们的Pod进入调度器,绑定操作才开始进行

这就说明,像是Kubernetes这样构建出来的,基于本地存储的Volume,可以提供容器存储的功能

而且,删除PV的时候,需要按照如下的流程进行操作

1.删除使用这个PV的Pod

2.从宿主机移除本地磁盘

3.删除PVC

4,删除PV

不按照这个流程删除,这个PV就会删除失败

在本章中,我们介绍了一个Kubernetes中Local Persistent Volume实现方式

正是通过PV和PVC,以及StorageClass这套存储体系,来解耦对现有用户的影响,作为上层的系统,只需要编写对应的Pod文件和PVC文件,并不关心对应的PV和StorageClass如何实现的

这种解耦的考虑,都是考虑为开发者提供更多的可扩展性

延迟绑定的特性导致了Local Persistent Volume不支持Dynamic Provisioning,为何?

一般来说,Dynamic Provision机制是通过PVC来创建PV的,而Local Persistent Volume先创建PV,然后创建PVC,只不过Pod创建的时候才会真正的绑定,这就导致和Pod声明的Node可能冲突,还需要重新创建一个PV,如果Dynamic Provisioning能够推迟PV创建,考虑Pod的调度条件和node信息,才能实现dynamic provisioning

发表评论

邮箱地址不会被公开。 必填项已用*标注