volumeManager 的运行
1. volumeManager的创建
volumeManager的创建发生在创建Kubelet时,函数NewMainKubelet用于创建kubelet。里面调用 NewVolumeManager 来创建一个mgr。

先看来一下 volumemanager 这个结构体吧
volumeManager struct {
kubeClient clientset.Interface // DesiredStateOfWorldPopulator用来与API服务器通信以获取PV和PVC对象的kube API客户端
volumePluginMgr *volume.VolumePluginMgr // 实现初始化好的插件管理对象
desiredStateOfWorld cache.DesiredStateOfWorld // 它包含了vm想要达成的一个状态:那些卷应该被连接,那个pods会引用这些卷。从 podManager获取数据
actualStateOfWorld cache.ActualStateOfWorld // 它包含了实际的vm状态。在 attach, detach, mount, and unmount 操作的时候记录这些信息
operationExecutor operationexecutor.OperationExecutor // 异步执行 attach, detach, mount,unmount操作
reconciler reconciler.Reconciler // 运行异步定期循环,通过使用operationExecutor 来协调desiredStateOfWorld与actualStateOfWorld
desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator // 运行异步定期循环,使用 PodManager 填充 desiredStateOfWorld
}
2. volumeManager的运行

主要启动了两个go routine,一个执行vm.desiredStateOfWorldPopulator.Run(sourcesReady,stopCh),另一个执行vm.reconciler.Run(stopCh)
3. desiredStateOfWorld 和 actualStateOfWorld
desiredStateOfWorld按照单词的意思,可以理解为理想的volume情况,它主要是根据podManger获取所有的Pod信息,从中提取Volume信息。
而actualStateOfWorld则是实际的volume情况。
desiredStateOfWorldPopulator通过podManager去构建desiredStateOfWorld。
而reconciler的工作主要是比较actualStateOfWorld和desiredStateOfWorld的差别,然后进行volume的创建、删除和修改,最后使二者达到一致。
4. desiredStateOfWorldPopulator.Run(sourcesReady,stopCh)


首先执行 findAndAddNewPods,然后执行 findAndRemoveDeletedPods,由于findAndRemoveDeletedPods 代价比较高昂,因此会检查执行的间隔时间。
findAndAddNewPods的逻辑比较简单。就是通过podManager获取所有的pods,然后调用processPodVolumes去更新desiredStateOfWorld。但是这样只能更新新增加的Pods的Volume信息。

findAndRemoveDeletedPods会遍历所有的volumeToMount,首先会判断该Volume所属的Pod是否存在于podManager,假如存在则忽略,说明不需要删除。

假如不存在,调用dswp.kubeContainerRuntime.GetPods(false)抓取Pod信息,这里是调用kubeContainerRuntime的GetPods函数。因此获取的都是runningPods信息,即正在运行的Pod信息。由于一个volume可以属于多个Pod,而一个Pod可以包含多个container,每个container都可以使用volume,所以他要扫描该volume所属的Pod的container信息,确保没有container使用该volume,才会删除该volume。

通过以上两步,desiredStateOfWorld就构建出来了,这是理想的volume状态,这里并没有发生实际的volume的创建删除挂载卸载操作。实际的操作由reconciler.Run(sourcesReady, stopCh)完成。
5. reconciler.Run(stopCh)

通过定时任务定期同步,reconcile就是一致性函数,保存desired和actual状态一致。
先确保应该解挂先解挂(unmounted),这个放在第一是因为这个存储可能将要被本主机上面其它的容器所使用,这里先释放挂载,如果实际上是挂载的,但理想状态不是挂载,则执行解挂操作。
for _, mountedVolume := range rc.actualStateOfWorld.GetMountedVolumes() {
if !rc.desiredStateOfWorld.PodExistsInVolume(mountedVolume.PodName, mountedVolume.VolumeName) {
volumeHandler, err := operationexecutor.NewVolumeHandler(mountedVolume.VolumeSpec, rc.operationExecutor)
volumeHandler.UnmountVolumeHandler(mountedVolume.MountedVolume, rc.actualStateOfWorld)
……
确保存储是attached和mounted的状态,如果没有挂载主机则执行AttachVolume挂载操作,如果没有mount怎能执行MountVolume操作。
for _, volumeToMount := range rc.desiredStateOfWorld.GetVolumesToMount() {
volMounted, devicePath, err := rc.actualStateOfWorld.PodExistsInVolume(volumeToMount.PodName, volumeToMount.VolumeName)
if cache.IsVolumeNotAttachedError(err) {
if rc.controllerAttachDetachEnabled || !volumeToMount.PluginIsAttachable { rc.operationExecutor.VerifyControllerAttachedVolume(volumeToMount.VolumeToMount, rc.nodeName, rc.actualStateOfWorld)
} else {
rc.operationExecutor.AttachVolume(volumeToAttach, rc.actualStateOfWorld)
}
……
最后确保应当解挂的存储解挂(detached 和unmounted),这个上面第一个的区别是,上面只是解挂unmounted,下面则是包括了存储detach和unmount两个步骤。
for _, attachedVolume := range rc.actualStateOfWorld.GetUnmountedVolumes() {
volumeHandler.UnmountDeviceHandler(attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.mounter)
或者
rc.operationExecutor.DetachVolume(attachedVolume.AttachedVolume, false /* verifySafeToDetach */, rc.actualStateOfWorld)
reconcile首先从actualStateOfWorld获取已经挂载的volume信息,然后查看该volume是否存在于desiredStateOfWorld,假如不存在就卸载。
接着从desiredStateOfWorld获取需要挂载的volumes。与actualStateOfWorld比较,假如没有挂载,则进行挂载。
这样存储就可以加载到主机attach,并挂载到容器目录mount。