k8s中涉及存储的组件主要有:attach/detach controller、pv controller、volume manager、volume plugins、scheduler。每个组件分工明确:
这里借其他博客的图,展示一下kubernetes的存储架构:
控制器模式是k8s非常重要的概念,一般一个controller会去管理一个或多个API对象,以让对象从实际状态/当前状态趋近于期望状态。
所以attach/detach controller的作用其实就是去attach期望被attach的volume,detach期望被detach的volume。后续结合现网案例(也向社区提交了pr),深入理解下ad controller。
对于ad controller来说,理解了其内部的数据结构,再去理解逻辑就事半功倍。ad controller在内存中维护2个数据结构:
actualStateOfWorld
—— 表征实际状态(后面简称asw)desiredStateOfWorld
—— 表征期望状态(后面简称dsw)很明显,对于声明式API来说,是需要随时比对实际状态和期望状态的,所以ad controller中就用了2个数据结构来分别表征实际状态和期望状态。
actualStateOfWorld
包含2个map:
attachedVolumes
: 包含了那些ad controller认为被成功attach到nodes上的volumesnodesToUpdateStatusFor
: 包含要更新node.Status.VolumesAttached
的nodes1、在启动ad controller时,会populate asw,此时会list集群内所有node对象,然后用这些node对象的node.Status.VolumesAttached
去填充attachedVolumes
。
2、之后只要有需要attach的volume被成功attach了,就会调用MarkVolumeAsAttached
(GenerateAttachVolumeFunc
中)来填充到attachedVolumes中
。
1、只有在volume被detach成功后,才会把相关的volume从attachedVolumes
中删掉。(GenerateDetachVolumeFunc
中调用MarkVolumeDetached
)
1、detach volume失败后,将volume add back到nodesToUpdateStatusFor
- `GenerateDetachVolumeFunc` 中调用`AddVolumeToReportAsAttached`
1、在detach volume之前会先调用RemoveVolumeFromReportAsAttached
从nodesToUpdateStatusFor
中先删除该volume相关信息
desiredStateOfWorld
中维护了一个map:
nodesManaged
:包含被ad controller管理的nodes,以及期望attach到这些node上的volumes。
1、在启动ad controller时,会populate asw,list集群内所有node对象,然后把由ad controller管理的node填充到nodesManaged
2、ad controller的nodeInformer
watch到node有更新也会把node填充到nodesManaged
3、另外在populate dsw和podInformer
watch到pod有变化(add, update)时,往nodesManaged
中填充volume和pod的信息
4、desiredStateOfWorldPopulator
中也会周期性地去找出需要被add的pod,此时也会把相应的volume和pod填充到nodesManaged
1、当删除node时,ad controller中的nodeInformer
watch到变化会从dsw的nodesManaged
中删除相应的node
2、当ad controller中的podInformer
watch到pod的删除时,会从nodesManaged
中删除相应的volume和pod
3、desiredStateOfWorldPopulator
中也会周期性地去找出需要被删除的pod,此时也会从nodesManaged
中删除相应的volume和pod
ad controller的逻辑比较简单:
1、首先,list集群内所有的node和pod,来populate actualStateOfWorld
(attachedVolumes
)和desiredStateOfWorld
(nodesManaged
)
2、然后,单独开个goroutine运行reconciler
,通过触发attach, detach操作周期性地去reconcile asw(实际状态)和dws(期望状态)
3、之后,又单独开个goroutine运行DesiredStateOfWorldPopulator
,定期去验证dsw中的pods是否依然存在,如果不存在就从dsw中删除
接下来结合现网的一个案例,来详细看看reconciler
的逻辑。
ContainerCreating
。kubectl describe pod
kubectl get node xxx -oyaml
的volumesAttached
和volumesInUse
volumesAttached:
- devicePath: /dev/disk/by-id/virtio-disk-6w87j3wv
name: kubernetes.io/qcloud-cbs/disk-6w87j3wv
volumesInUse:
- kubernetes.io/qcloud-cbs/disk-6w87j3wv
- kubernetes.io/qcloud-cbs/disk-7bfqsft5
Volume not attached according to node status
,也就是说kubelet认为cbs没有按照node的状态去挂载。这个从node info也可以得到证实:volumesAttached
中的确没有这个cbs盘(disk-7bfqsft5)。volumesInUse
中还有这个cbs。说明没有unmount成功很明显,cbs要能被pod成功使用,需要ad controller和volume manager的协同工作。所以这个问题的定位首先要明确:
volumesAttached
和volumesInUse
在ad controller和kubelet之间充当什么角色?这里只对分析volume manager做简要分析。
Volume not attached according to node status
在代码中找到对应的位置,发现在GenerateVerifyControllerAttachedVolumeFunc
中。仔细看代码逻辑,会发现volumesToMount
的podsToMount
)volumeToMount
是否已经attach了这个volumeToMount
是由podManager
中的podInformer
加入到相应内存中,然后desiredStateOfWorldPopulator
周期性同步到dsw中的GenerateVerifyControllerAttachedVolumeFunc
中会去遍历本节点的node.Status.VolumesAttached
,如果没有找到就报错(Volume not attached according to node status
)node.Status.VolumesAttached
中来判断volume有无被attach成功。node.Status.VolumesAttached
?ad controller的数据结构nodesToUpdateStatusFor
就是用来存储要更新到node.Status.VolumesAttached
上的数据的。node.Status.VolumesAttached
,而又新建了pod,__desiredStateOfWorldPopulator
从podManager中的内存把新建pod引用的volume同步到了volumesToMount
中,在验证volume是否attach时,就会报错(Volume not attached according to node status)WaitForAttachAndMount
去等待volumeattach和mount成功,由于前面一直无法成功,等待超时,才会有会面timeout expired
的报错所以接下来主要需要看为什么ad controller那边没有更新node.Status.VolumesAttached
。
接下来详细分析下ad controller的逻辑,看看为什么会没有更新node.Status.VolumesAttached
,但从事件看ad controller却又认为volume已经挂载成功。
从流程简述中表述可见,ad controller主要逻辑是在reconciler中。
reconciliationLoopFunc
,周期为100ms。reconciliationLoopFunc
的主要逻辑在reconcile()
中:1、 首先,确保该被detach的volume被detach掉
- 遍历asw中的`attachedVolumes`,对于每个volume,判断其是否存在于dsw中
- 去dsw.nodesManaged中判断node和volume是否存在
- 存在的话,再判断volume是否存在
- 如果volume存在于asw,且不存在于dsw,则意味着需要进行detach
- 之后,根据`node.Status.VolumesInUse`来判断volume是否已经unmount完成,unmount完成或者等待6min timeout时间到后,会继续detach逻辑
- 在执行detach volume之前,会先调用`RemoveVolumeFromReportAsAttached`从asw的`nodesToUpdateStatusFor`中去删除要detach的volume
- 然后patch node,也就等于从`node.status.VolumesAttached`删除这个volume
- 之后进行detach,detach失败主要分2种
- 如果真正执行了`volumePlugin`的具体实现`DetachVolume`失败,会把volume add back到`nodesToUpdateStatusFor`(之后在attach逻辑结束后,会再次patch node)
- 如果是operator_excutor判断还没到backoff周期,就会返回`backoffError`,直接跳过`DetachVolume`
- backoff周期起始为500ms,之后指数递增至2min2s。已经detach失败了的volume,在每个周期期间进入detach逻辑都会直接返回`backoffError`
2、之后,确保该被attach的volume被attach成功
- 遍历dsw的`nodesManaged`,判断volume是否已经被attach到该node,如果已经被attach到该node,则跳过attach操作
- 去asw.attachedVolumes中判断是否存在,若不存在就认为没有attach到node
- 若存在,再判断node,node也匹配就返回`attachedConfirmed`
- 而`attachedConfirmed`是由asw中`AddVolumeNode`去设置的,`MarkVolumeAsAttached`设置为true。(true即代表该volume已经被attach到该node了)
- 之后判断是否禁止多挂载,再由operator_excutor去执行attach
3、 最后,UpdateNodeStatuses
去更新node status
attachedVolumes
中删除node.status.VolumesAttached
中删除volume,之后才去执行detachbackoffError
不会把该volumeadd back node.status.VolumesAttached
nodesManaged
attachedConfirmed
为truedsw.nodesManaged
了,这样volume同时存在于asw和dsw中了,实际状态和期望状态一致,被认为就不需要进行detach了。node.status.VolumesAttached
。所以就出现了现象中的node info中没有该volume,而ad controller又认为该volume被attach成功了node.status.VolumesAttached
,所以volume manager在验证volume是否attach成功,发现node.status.VolumesAttached
中没有这个voume,也就认为没有attach成功,所以就有了现象中的报错Volume not attached according to node status
syncPod
在等待pod所有的volume attach和mount成功时,就超时了(现象中的另一个报错timeout expired wating...
)。ContainerCreating
node.status.VolumesAttached
中删除该volume,再去执行真正的DetachVolume
。backoff期间直接返回backoffError
,跳过DetachVolume
,不会add backnode.status.VolumesAttached
中就永远没有了该volumenode.status.VolumesAttached
,所以kubelet认为没有attach成功,新创建的pod就一直处于ContianerCreating
了node.status.VolumesAttached
和以下两个逻辑:node.status.VolumesAttached
。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。