Kubernetes源码分析之存储相关

本节所有的代码基于1.13.4版本。

前言

在kubernetes中,与存储相关的controller主要由三种:
1、AttachDetachController,简称AD Controller,主要处理真实的与volume相关的操作;
2、PersistentVolumeBinderController,其实就是PV Controller,主要负责pv和pvc的生命周期以及状态的切换;
3、VolumeExpandController,主要负责volume的扩容操作。

PersistentVolumeBinderController

首先追踪PersistentVolumeBinderController方法,直接进入其Run方法。

很直观,依赖于三个goroutine:
1、resync;
2、volumeWorker;
3、claimWorker。

resync

进入resync,代码很简单,如下:

resync的主要作用就是不停获取pvc和pv的信息,传入到相应的缓存队列中去。这两个队列就是在volumeWorker和claimWorker用到的数据源的信息。

volumeWorker

volumeWorker通过for循环,不停获取resync缓冲的队列信息,对pv,即volume做相应的更新操作。主要实现方法updateVolume

进入 updateVolume方法,主要调用 syncVolume方法,这个方法是整个volumeWorker的核心。工作流程如下:
1、如果volume没有被使用,更新PV的状态为 Available
2、volume已经被pvc持有:

  • 如果volume还没有被绑定到pvc上,更新PV状态为Available
  • 如果pvc信息为空,pv的状态不为Released且不为Failed,更新PV状态为Released,按照配置的回收策略执行pv的回收(调用reclaimVolume方法);
  • pvc中指定volume的字段和当前volume一致,更新PV状态为Bound
  • 如果都不满足,根据状态判断是执行回收策略还是解绑(unbindVolume)操作。

claimWorker

claimWorker的工作流程和volumeWorker类似,核心调用方法为updateClaim-->syncClaim,主要处理的是pvc生命周期中的各种状态:Pending、Bound以及Lost。不做过多赘述。

总结

PersistentVolumeBinderController的执行流程很清晰,依赖三个goroutine的协作,分别处理数据的获取、pv的生命周期的状态更新和pvc的生命周期的状态更新。整个逻辑中,没有对具体的volume做操作,更新的仅仅是kubernetes中定义的pv和pvc资源的信息,说白了就是etcd中的数据。具体干活的主要还是AttachDetachController,即AD Controller。

AttachDetachController

首先由Run方法进入AD Controller的启动方法,如下:

主要有以下几个步骤构成启动步骤:
1、同步各资源的信息,包括Pod、Node、PV、PVC;
2、调用 populateActualStateOfWorld方法获取Node上Volume的信息;
3、调用 populateDesiredStateOfWorld方法获取Pod需要对应的Volume信息;
4、 reconciler.Run负责检查挂载状态,判断是否需要挂载或卸载(真正干活的);
5、 desiredStateOfWorldPopulator.Run同步Pod与Volume的挂载信息,相应信息输送给 reconciler.Run使用;
6、 pvcWorker控制pvc的流控;
7、相应的信息注册到 metrics中,供Prometheus采集数据使用。

populateActualStateOfWorld

populateActualStateOfWorld方法主要处理的是Node与Volume之间的关系,主要作用是将Node Volume当前的状态存入到actualStateOfWorld中。主要方法如下:

主要步骤如下:
1、获取所有的Node信息;
2、一一遍历获取到的所有的Node,针对Node上已经 attached的Volume,分别置于已经attached状态和 in-user状态,将Volume信息添加到 actualStateOfWorld中,并将Node添加到 desiredStateOfWorld中。 actualStateOfWorlddesiredStateOfWorld的数据会在 reconciler.Run使用。

populateDesiredStateOfWorld

populateDesiredStateOfWorld方法主要处理的是Pod与Volume之间的关系,主要作用是将Pod Volume期望的状态添加到desiredStateOfWorld中去。和populateActualStateOfWorld类似,主要就是针对Volume做标记操作,并将相应的Pod信息缓存到desiredStateOfWorld中或者从desiredStateOfWorld中剔除不匹配的Pod信息。

desiredStateOfWorldPopulator.Run

desiredStateOfWorldPopulator.Run方法通过不停的循环,调用findAndAddActivePods方法,通过获取所有的Pod,判断是否需要添加到desiredStateOfWorld中去。

reconciler.Run

前面几步主要的目的是为了获取Node Volume和Pod Volume的状态。其中,Node上的Volume是已经存在的,故称作为actualStateOfWorld,而Pod Volume是最终需要生效的资源,故称之为desiredStateOfWorldreconciler.Run的作用就是通过不停获取actualStateOfWorlddesiredStateOfWorld状态,将Pod与Volume置于相对应的状态,保证磁盘的最终挂载成功或者卸载成功。主要方法如下:

不停循环调用 reconciliationLoopFunc方法。
首先进入 reconcile方法,这是真正干活的地方。
reconcile使用了三个大的for循环,处理三类事件:
1、首先剔除需要解绑的Volume,调用 UnmountVolume方法最终调用后台存储的解绑接口;
2、将需要Attach或者Mount的volumes调用后台存储接口执行Attach或者Mount操作;
3、将需要Detach或者Unmount的devices调用后台存储接口执行Detach或者Unmount操作。
其中,Attach操作指的是将Volume在Node上生成卷标,如常见的 /dev/xx等。Mount操作包含了MountDevice和Mount两部分,其中,MountDevice将生成的卷标挂载成Node的路径,一般在 /var/lib/kubelet/xx/kubernetes.io/xx下,依赖于不同的存储,Mount最终将MountDevice生成的路径和Pod需要使用的路径Mount起来,一般路径为 /var/lib/kubelet/pods/xx/volumes/xx
sync方法主要完成Volume的后续操作。如果Volume未被成功绑定,将Volume进行重建或者解绑操作。

ExpandController

Kubernetes在1.8开始支持卷的扩容操作,1.11功能已经处于Beta阶段。主要代码如下:

pvcPopulator.Run监听PVC的变化,只要PVC中Request字段的Storage值比Status中的大,即表示PVC容量发生了变化,需要扩容。此时,将相应的PV和PVC的信息缓存到 resizeMap中去。如下:
syncResize则是不停获取 resizeMap中的数据,如果有变化,则调用 ExpandVolume方法生成扩展动作,完成磁盘的扩容和PV、PVC的状态更新操作。代码如下:

总结

Kubernetes的存储主要针对Node、Pod、PV以及PVC四类资源。通过获取Node上Volume状态,将其与Pod中的Volume进行绑定,完成卷的加载。最终的绑定或者解绑等操作依赖的是后台的存储,包括内置的开源存储插件或者自己实现的插件(FlexVolume或者CSI)。
在kubelet中,同时存在VolumeManager去管理其节点上的Volume资源信息,基本功能与AD Controller一致。可以通过kube-controller-manager的--disable-attach-detach-reconcile-sync参数或者kubelet的--enable-controller-attach-detach参数控制是由kube-controller-manager执行volume的attach/detach操作还是kubelet执行相应的操作。

转载于:https://juejin.im/post/5cf114d55188253cec305973

猜你喜欢

转载自blog.csdn.net/weixin_33984032/article/details/91440100