Kube-controller-manager(StatefulSet)
Kube-controller-manager(StatefulSet)
基于1.25
StatefulSet Controller控制器主要控制StatefulSet资源
StatefulSet资源主要包含Pod模版、副本数量(Spec.Replicas)、更新策略、管理策略等部分
- StatefulSet在每个关联的Pod的Name会添加一个序号后缀
- 创建、删除Pod按照固定的顺序操作
- 当Pod超出期望的副本数量,或以执行滚动更新策略更新,控制器顺序按顺序从高到低依次删除Pod;反之,如果Pod数量低于期望副本数量,控制器低序号到高序号删除Pod
控制器初始化
- StatefulSet资源对象:监听Add、Uodtaed、Delete事件
- Pod资源对象
主要执行逻辑
- ssc.setLister.StatefulSets
获取完整的STS对象
- ssc.getPodsForStatefulSet
获取关联的Pod
- syncStatefulSet
调谐StatefulSet。调用syncStatefulSet func实现STS调谐。内部主要调用了 ssc.control.UpdateStatefulSet和ssc.enqueueSSAfter func
ssc.control.UodateStatefulSet
调谐Pod的数量。调用ssc.control.UpdateStatefulSet func调谐Pod数量
ssc.control.UpdateStatefulSet func主要工作流程如下:
- 从低到高序号,逐个检查每个序号的Pod,删除Status.Phase为Failed的Pod,并且在序号上缺失的Pod时创建新的Pod
- 从高到低序号,删除超出期望副本数量的序号的Pod
- 根据STS更新策略执行更新Pod。STS创建的POd带有“
controller-revision-hash
“Label,其值根据STS资源对象的名称和Pod模版的哈嘻值生产的字符串,用于对于Pod模版
ssc.enqueueSSAfter
重新加入工作队列。和RS Controller相似,如果Ready的Pod和Available不一致,并且MinReadySeconds不为0,延迟加入
调谐Pod的数量
StatefulSet Controller 为每个Pod分配序号并且把序号写入Pod Name,其命名格式为’{StatefulSet.Name}-序号’
创建Pod的时候,STS Controller按照序号从低到高创建;在Pod更新和删除的时候,STS Controller从高到低删除。
- 最终STSController保证[0-Spec.Replicas-1]序号上的Pod可用
调谐Pod的主要流程如下:
统计要保留的Pod和要删除的Pod
STS Controller主要把现有的Pod分为replicas和condemened俩部分
-
// First we partition pods into two lists valid replicas and condemned Pods
for i := range pods {
if ord := getOrdinal(pods[i]); 0 <= ord && ord < replicaCount {
// if the ordinal of the pod is within the range of the current number of replicas,
// insert it at the indirection of its ordinal
replicas[ord] = pods[i]
} else if ord >= replicaCount {
// if the ordinal is greater than the number of replicas add it to the condemned list
condemned = append(condemned, pods[i])
}
// If the ordinal could not be parsed (ord < 0), ignore the Pod.
}
-
在缺失的Pod的序号上填充Pod模版
STS Controller从0开始遍历replicas数组,如果序号上没有Pod,则说明这个序号缺少Pod,STS Controller会生产一个Pod在序号上占位,但是此刻还没真正创建Pod
-
// for any empty indices in the sequence [0,set.Spec.Replicas) create a new Pod at the correct revision
for ord := 0; ord < replicaCount; ord++ {
if replicas[ord] == nil {
replicas[ord] = newVersionedStatefulSetPod(
currentSet,
updateSet,
currentRevision.Name,
updateRevision.Name, ord)
}
}
-
删除失败的Pod并创建新的Pod
根据Pod关联策略(PodManagement Policy)属性不同
- OrderedReady:严格按照顺序管理Pod,在创建前一序号的Pod已经达到可用状态,或者删除后一序号已经删除中才能处理下一个Pod
- Parallel:不需要等待Pod到达可用之后就可以创建或者删除
STS Controller会检查按照低到高,遍历replicas数组,逐个检查各个序号上的Pod
- 如果是Parall策略,按照顺序执行下面的操作
- 如果当前Pod已经处于Failed状态,则调用kube-apiserver删除该Pod,在该序号上补充一个创建新的Pod的模版,并且执行后续的逻辑
- 如果当前的Pod还没有创建,则使用序号上的Pod模版创建Pod
- 检查Pod的Name、Label、Volume的属性是否与StatefulSet资源对象匹配,并且在不匹配时更新Pod的数据
- 如果是OrderedReady,执行后续逻辑
- 如果当前Pod已经是Failed,则调用kube-apiserver删除这个Pod,在该序号上补充一个创建新的Pod模版
- 如果Pod还没有创建,则创建Pod,退出同步逻辑
- 如果Pod处于删除中,或者不是Ready或Available状态,则退出同步状态
- 检查Pod的Name、Label、Volume属性是否与STS资源匹配,并且在不匹配的时候更新Pod的数据
删除多余的Pod
STS Controller按序号从高到低删除condemned数组中的Pod,即删除序号大于或者等于期望副本数的Pod
- 如果是Parall策略,按照顺序执行下面的操作
- 如果当前Pod已经处于删除中,则跳过当前Pod,继续处理下一个序号的Pod
- 如果当前Pod不处于删除中,则调用kube-apiserver删除当前Pod,继续处理下一个Pod
- 如果是OrderedReady,执行后续逻辑
- 如果当前Pod已经处于删除中,则退出同步状态
- 如果当前Pod不是Ready或Available状态,并且低于该Pod的序号还有未处于Ready状态的Pod,则退出同步逻辑,等待前序Pod全部就绪之后再删除当前Pod
- 调用kube-apiserver删除当前Pod,退出同步
- 如果是Parall策略,按照顺序执行下面的操作
重建过时版本的Pod
STS Controller根据Pod模版字段,从高到低比较Pod版本是否最新,会根据更新策略更新过时的Pod
更新策略
STS Controller支持俩种更新策略
OnDelete:UpddateStatefulSer func直接返回,不会执行Pod删除和创建
用户需要手动删除Pod,才会根据Delete事件创建Pod
// for the OnDelete strategy we short circuit. Pods will be updated when they are manually deleted.
if set.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType {
return &status, nil
}RollingUpdate:STS Controller根据序号从高到低删除Pod并创建新Pod,并且在最新版本的Pod处于Ready状态才会更新下一个Pod
updateMin决定Pod更新的最低序号
用户没有设置STS对象的Parition属性,STS Controller会对所有Pod更新,不然更新[Partition,Repliacas-1]序号上的的Pod
更新过程中,STS Controller从最高的序号开始
-
// we compute the minimum ordinal of the target sequence for a destructive update based on the strategy.
updateMin := 0
if set.Spec.UpdateStrategy.RollingUpdate != nil {
updateMin = int(*set.Spec.UpdateStrategy.RollingUpdate.Partition)
}
// we terminate the Pod with the largest ordinal that does not match the update revision.
for target := len(replicas) - 1; target >= updateMin; target-- {
// delete the Pod if it is not already terminating and does not match the update revision.
if getPodRevision(replicas[target]) != updateRevision.Name && !isTerminating(replicas[target]) {
klog.V(2).InfoS("Pod of StatefulSet is terminating for update",
"statefulSet", klog.KObj(set), "pod", klog.KObj(replicas[target]))
err := ssc.podControl.DeleteStatefulPod(set, replicas[target])
status.CurrentReplicas--
return &status, err
}
// wait for unhealthy Pods on update
if !isHealthy(replicas[target]) {
klog.V(4).InfoS("StatefulSet is waiting for Pod to update",
"statefulSet", klog.KObj(set), "pod", klog.KObj(replicas[target]))
return &status, nil
}
}