以volume create 为例
volumen 资源创建后,volume Informer watch 到资源变化,volume controller 的worker 开始处理,因为longhorn-manager 实例有多个,也就意味着informer有多个,那么,究竟是哪个manager的informer才是处理某次资源变化的负责人呢?
代码探析:
==controller/volume_controller.go==1
2
3
4
5
6
7
8// 当前的节点 manager 是否负责运行sync逻辑
isResponsible, err := vc.isResponsibleFor(volume, defaultEngineImage)
if err != nil {
return err
}
if !isResponsible {
return nil
}
我们查看下 isResponsibleFor 方法的逻辑,代码省略了判错及一些不影响主流程阅读的内容
==controller/volume_controller.go==1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34// isResponsibleFor picks a running node that has the default engine image deployed.
// We need the default engine image deployed on the node to perform operation like backup operations.
// Prefer picking the node v.Spec.NodeID if it meet the above requirement. 如果满足上述要求,则优先选择节点 v.Spec.NodeID
func (vc *VolumeController) isResponsibleFor(v *longhorn.Volume, defaultEngineImage string) (bool, error) {
var err error
isResponsible := isControllerResponsibleFor(vc.controllerID, vc.ds, v.Name, v.Spec.NodeID, v.Status.OwnerID)
// No node in the system has the default engine image,
// Fall back to the default logic where we pick a running node to be the owner
if len(readyNodesWithDefaultEI) == 0 {
return isResponsible, nil
}
preferredOwnerEngineAvailable, err := vc.ds.CheckEngineImageReadiness(defaultEngineImage, v.Spec.NodeID)
currentOwnerEngineAvailable, err := vc.ds.CheckEngineImageReadiness(defaultEngineImage, v.Status.OwnerID)
currentNodeEngineAvailable, err := vc.ds.CheckEngineImageReadiness(defaultEngineImage, vc.controllerID)
// 如果当前节点engine不可用,那么就不负责处理了
// 当前节点engine可用 && (最优、继续、需要新的,都是当前节点)
isPreferredOwner := currentNodeEngineAvailable && isResponsible
// (当前那节点engine可用 && 最优节点engine 不可用 && 当前控制器就是的节点就是之前的owner节点) 继续当前节点
continueToBeOwner := currentNodeEngineAvailable && !preferredOwnerEngineAvailable && vc.controllerID == v.Status.OwnerID
// (当前那节点engine可用 && 最优节点engine 不可用 && owner节点不engine不可用) 选择当前节点
requiresNewOwner := currentNodeEngineAvailable && !preferredOwnerEngineAvailable && !currentOwnerEngineAvailable
return isPreferredOwner || continueToBeOwner || requiresNewOwner, nil
}
我们继续看下 isControllerResponsibleFor,这个函数是longhorn-manager 资源用来判断当前 manager是否负责处理 sync 逻辑的 通用函数
==controller/controller_manager.go==
1 | // 挑选指导原则:优先 Spec.NodeId,优先最优节点 |
总结:
通过以上代码,我们可以总结出:lognhorn-manager 实例主要是判断当前节点是否可负责处理sync逻辑,而判断能否使用当前节点:
最优先条件是当前节点是 perfer(最优节点,perfer的值一般是 xx.Spec.NodeID)
其次是当前节点就是owner节点
最次是最优节点及owner节点都不可用了,那么就使用当前节点
可以看出,节点的挑选最终的优化方向是往 “资源.Spec.NodeId 是哪个,那么就由这个期望的node上的manager来处理”
==注:voulume contoller 这一块加入了 节点engineImage 的判断,与其他资源(例:replica)有些不同,但核心的处理逻辑还是一致的==
(最优不可用,Owner不可用),两个或多个节点都获取到了负责权,这种情况下,是通过 更新 Status.OwnerID,通过 k8s 的版本冲突机制来保证只有最先更新的manager获得处理权,即这一段代码1
2
3
4
5
6
7
8
9
10
11
12
13
14// 这里是 让哪个 vc instance 来处理 volume,如果是volume create,那就是第一个update 的 vc 获取处理权
// 如果只是 volume (update等)其他操作,也是谁先更新谁获得处理权
if volume.Status.OwnerID != vc.controllerID {
volume.Status.OwnerID = vc.controllerID
volume, err = vc.ds.UpdateVolumeStatus(volume)
if err != nil {
// we don't mind others coming first
if apierrors.IsConflict(errors.Cause(err)) {
return nil
}
return err
}
log.Debugf("Volume got new owner %v", vc.controllerID)
}