阅读本节前请了解 Advanced DaemonSet 的使用和功能
系列专栏: Openkruise
api: apis/apps/v1alpha1/daemonset_types.go
controller: pkg/controller/daemonset/
⚠️ demonset 保证的是在对应 node 有且仅有一个 pod 运行
Reconcile
准备阶段
采用 k8s informer 机制,内置多种 informer 对象 Listener,用于获取集群中对应的资源
// 拿到对应的 ds
ds, err := dsc.dsLister.DaemonSets(request.Namespace).Get(request.Name)
// 获取所有的 node 信息
nodeList, err := dsc.nodeLister.List(labels.Everything())
调整 pod 分布
这一步通过函数 dsc.manage(ds, nodeList, hash)完成,其中 hash 是该 ds 最新 revision 的hash 值,用于保证版本的一致性。 该函数的目的是让 pod 在它应该在的 node 上,创建缺少的,删除多余的。
- 每个 node 通过函数
nodesNeedingDaemonPodsOnNode, podsToDeleteOnNode := dsc.podsShouldBeOnNode(node, nodeToDaemonPods, ds, hash)来判断 pod 是否应该被调度在 node 上并返回两个数组nodesNeedingDaemonPodsOnNode, podsToDeleteOnNode,其中nodesNeedingDaemonPodsOnNode意味着这里面的 pods 应该在本轮 reconcile 中被创建(shouldRun && !exists),podsToDeleteOnNode意味着这里面的 pods 应该在本轮 reconcile 中被删除
[⚠️注意]:这里面还包含了升级策略为 "RollingUpdate" 且 rollingUpdateType 不为空且配置了 MaxSurge 的情况,具体逻辑可以参考 升级方式
-
处理具有分批灰度的情况
- 需要重新计算
nodesNeedingDaemonPodsOnNode数组以保证分批灰度策略的正确性:nodesNeedingDaemonPods = GetNodesNeedingPods(newPodCount, nodesDesireScheduled, int(partition), isDaemonSetCreationProgressively(ds), nodesNeedingDaemonPods)
- 需要重新计算
-
创建/删除 pods 在对应的 node 上 :
dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash)- 创建不是一次行全部创建好,而是采用一种类似 tcp 慢启动的策略,逐倍调大 batchSize,如果 batchSize 大于最后需要创建的 pod 数量,那么 batchSize 就等于剩余需要创建的 pod 数
-
batchSize := integer.IntMin(createDiff, kubecontroller.SlowStartInitialBatchSize) for pos := 0; createDiff > pos; batchSize, pos = integer.IntMin(2*batchSize, createDiff-(pos+batchSize)), pos+batchSize - 在创建完后进行删除操作,删除是起了多个协程来并行删除,待所有删除任务完成后才返回
滚动升级
前置条件:pod ready 且不处于暂停状态
触发条件:Spec.UpdateStrategy.Type == “OnDelete” or "RollingUpdate"
-
通过
nodeToDaemonPods, err := dsc.getNodesToDaemonPods(ds)获取每个 nodes 上对应的 pods,即该 ds 所应管理的所有 <node, pod> pair 对信息 -
解析 maxSurge 和 maxUnavailable 参数,这是升级策略的依据
-
对
nodeToDaemonPods数组进行进一步的过滤(filterDaemonPodsToUpdate ==>filterDaemonPodsNodeToUpdate),目的是要找出哪些 node 上有 pod 需要更新,筛选逻辑如下- 首先确定该 node 上的 pod 是否需要更新,如果需要才继续
- 如果预先定义了 selector [特性: 标签选择升级],那么根据 selector 选
- 如果没有 selector 特性,则该 node 被选中
- 默认的 partition = 0,但用户也可能定义了一个大于 0 的值,因此我们需要保留 partition 个 pod 不升级,所以需要对前面已经选中的 nodes 进行截断
maxUpdate := len(allNodeNames) - int(partition);,并返回一个截断后的数组sorted = sorted[:maxUpdate]
-
对于 maxSurge == 0 的情况,我们只是先做删除,在下一次 reconcile 的时候才创建
-
首先我们遍历每一个 node 以筛选目标 pods
-
通过函数
newPod, oldPod, ok := findUpdatedPodsOnNode(ds, pods, hash)检查 node 上 pod 的版本,结合podutil.IsPodAvailable和 maxUnavailable参数筛选出需要处理的 pods 到两个数组- allowedReplacementPods: 需要更新且不可用的 pod
- candidatePodsToDelete: 需要更新且可用的 pod
-
-
优先选取 allowedReplacementPods 再根据 maxUnavailable 添加 candidatePodsToDelete 中的 pods 到 oldPodsToDelete 数组,作为我们要处理的 pods
-
对每一个 pod 尝试 inplace 更新
dsc.inPlaceUpdatePods(ds, oldPodsToDelete, curRevision, oldRevisions) -
对剩下的 pods 通过
dsc.syncNodes(ds, oldPodsToDelete, nil, hash)删除剩下的 pods
-
-
对于 maxSurge !=0 时,我们需要先创再删,同样我们也需要遍历每一个 node 以进行筛选分类
- 通过
findUpdatedPodsOnNode和podutil.IsPodAvailable函数以及 maxSurge 来进行筛选。(注意:maxSurge 和 maxUnavailable 是矛盾的) - 筛选最后会产生两个数组分别为 oldPodsToDelete 和 newNodesToCreate,代表哪些 pods 可以删除,哪些 pods 应当被创建
- 调用
dsc.syncNodes(ds, oldPodsToDelete, newNodesToCreate, hash)
- 通过