kubelet源码 删除pod pod_workers.go(三)
上篇中UpdatePod函数结束,然后进入managePodLoop函数。
UpdatePod负责对pod的状态流程进行更新,对pod状态的标记
managePodLoop函数主要负责就是上述标记后处理这些pod了
1.managePodLoop函数里整个都是一个for循环,接收podUpdates管道传过来的数据
func (p *podWorkers) managePodLoop(podUpdates <-chan podWork) {
var lastSyncTime time.Time
var podStarted bool
for update := range podUpdates {
//******
}
}
2.这里主要校验的是静态pod,只有静态pod是允许启动的才可以走下面流程,如果是不存在的或者已准备删除的,则直接关闭这个静态pod的goroutine,不参与循环。如果这个pod正等待启动中则跳过当前这次循环即可。(流程4详解)
if !podStarted {
canStart, canEverStart := p.allowPodStart(pod)
if !canEverStart {
p.completeUnstartedTerminated(pod)
if start := update.Options.StartTime; !start.IsZero() {
metrics.PodWorkerDuration.WithLabelValues("terminated").Observe(metrics.SinceInSeconds(start))
}
klog.V(4).InfoS("Processing pod event done", "pod", klog.KObj(pod), "podUID", pod.UID, "updateType", update.WorkType)
return
}
if !canStart {
klog.V(4).InfoS("Pod cannot start yet", "pod", klog.KObj(pod), "podUID", pod.UID)
continue
}
podStarted = true
}
3.流程2代码块的第三行。验证这个pod是否允许启动
- 判断是否为静态pod,如果不是,直接返回true
- 如果是静态pod,但是流程里并未创建过,不允许启动,也不是始终如一启动的。都会返回false(必须回到updatePod中重新创建流程链路)
- 如果pod已经停止了,则都返回false
- 如果静态pod已经有其他的同名启动成功。或者没有其他的同名启动成功,但是等待中的和当前pod不是同一个。则会执行流程2的continue(流程4介绍这步)
- 如果静态pod允许启动,则都返回true
func (p *podWorkers) allowPodStart(pod *v1.Pod) (canStart bool, canEverStart bool) {
if !kubetypes.IsStaticPod(pod) {
return true, true
}
p.podLock.Lock()
defer p.podLock.Unlock()
status, ok := p.podSyncStatuses[pod.UID]
if !ok {
klog.ErrorS(nil, "Pod sync status does not exist, the worker should not be running", "pod", klog.KObj(pod), "podUID", pod.UID)
return false, false
}
if status.IsTerminationRequested() {
return false, false
}
if !p.allowStaticPodStart(status.fullname, pod.UID) {
p.workQueue.Enqueue(pod.UID, wait.Jitter(p.backOffPeriod, workerBackOffPeriodJitterFactor))
status.working = false
return false, true
}
return true, true
}
4.流程3第15行。这个函数场景是静态pod有全名是一样的。如果当前的已经有启动的了并且是自己,则返回true。如果不是自己,是其他同名的启动成功,则返回false。
- 先获取已启动的静态pod是当前pod,证明是当前pod已经启动了,返回true
- 如果已启动的静态pod不是当前pod,证明有其他pod已经启动了,返回false(这种场景不应该关闭goroutine,继续一直循环状态,等待静态pod关闭,它好继续争抢)
- 如果未启动成功,查看等待排队启动的静态pod
- 如果等待启动的pod没有链路信息或者已经关闭或者关闭中,跳过这个pod
- 如果等待启动的第一个pod和当前要处理的pod不是一个id,则直接返回false,并且更新缓存当前pod为最新的排队中的(这种场景不需要关闭goroutine,继续一直循环状态,下次循环进入,排队的第一个就是当前pod了)
- 如果等待启动的pod和当前要处理的是同一个,则将当前pod变成已经启动的。并且把这个pod从未启动的pod数组移除。
func (p *podWorkers) allowStaticPodStart(fullname string, uid types.UID) bool {
startedUID, started := p.startedStaticPodsByFullname[fullname]
if started {
return startedUID == uid
}
waitingPods := p.waitingToStartStaticPodsByFullname[fullname]
for i, waitingUID := range waitingPods {
status, ok := p.podSyncStatuses[waitingUID]
if !ok || status.IsTerminationRequested() || status.IsTerminated() {
continue
}
if waitingUID != uid {
p.waitingToStartStaticPodsByFullname[fullname] = waitingPods[i:]
return false
}
waitingPods = waitingPods[i+1:]
break
}
if len(waitingPods) != 0 {
p.waitingToStartStaticPodsByFullname[fullname] = waitingPods
} else {
delete(p.waitingToStartStaticPodsByFullname, fullname)
}
p.startedStaticPodsByFullname[fullname] = uid
return true
}
5.流程2代码块的第4行 如果这个静态pod已经收到关闭或者删除信号了,将这个pod的更新信息及管道都清除。
- cleanupPodUpdates函数关闭这个uid的管道。并且删除podUpdates的map,删除lastUndeliveredWorkUpdate的map
- 从podSyncStatuses中取出这个pod的状态链,关闭working状态,finished状态为true,删除时间为现在。
- 如果startedStaticPodsByFullname已经启动的pod中有此pod,则删掉这个pod的map信息
func (p *podWorkers) completeUnstartedTerminated(pod *v1.Pod) {
p.podLock.Lock()
defer p.podLock.Unlock()
klog.V(4).InfoS("Pod never started and the worker can now stop", "pod", klog.KObj(pod), "podUID", pod.UID)
p.cleanupPodUpdates(pod.UID)
if status, ok := p.podSyncStatuses[pod.UID]; ok {
if status.terminatingAt.IsZero() {
klog.V(4).InfoS("Pod worker is complete but did not have terminatingAt set, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
if !status.terminatedAt.IsZero() {
klog.V(4).InfoS("Pod worker is complete and had terminatedAt set, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
status.finished = true
status.working = false
status.terminatedAt = time.Now()
if p.startedStaticPodsByFullname[status.fullname] == pod.UID {
delete(p.startedStaticPodsByFullname, status.fullname)
}
}
}
- 这里的case判断是否为孤儿pod,如果是孤儿pod,不做什么操作,因为配置文件及状态已经没有了。如果不是孤儿pod则第六行,
在本地缓存(运行时pod)作比较。如果本地缓存的pod的时间大于当前时间,则使用本地缓存的数据,否则返回个空
最后一行contextForWorker,这里是给pod注册一个context.WithCancel函数,为了后续推出使用,上一篇说到过这个函数。
var status *kubecontainer.PodStatus
var err error
switch {
case update.Options.RunningPod != nil:
default:
status, err = p.podCache.GetNewerThan(pod.UID, lastSyncTime)
}
if err != nil {
p.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedSync, "error determining status: %v", err)
return err
}
ctx := p.contextForWorker(pod.UID)
7.这里进行处理pod了
- 第2行,如果是删除的pod工作(后续再看,量太大,执行了kubelet.go中的syncTerminatedPod函数)
- 第5行,如果是进行删除的pod工作。第七行中如果pod有删除状态,取出删除状态的优雅删除事件,
- 第10行是将pod的删除状态设置为true,同时注册一个函数,函数是这个pod的PodStatusFunc不为空时才会有。这个函数是当pod被kill时,执行可以覆盖pod状态
- 第12行调用kubelet.go的处理函数,开始删除pod下的容器等信息(后续再看,量太大,需要几篇去写。执行了kubelet.go中的syncTerminatingPod函数)
- 第15行代表这个pod是进行同步中的,调用kubelet.go的syncPod(后续再看,量太大)
- 最后记录一下当前时间。
switch {
case update.WorkType == TerminatedPodWork:
err = p.syncTerminatedPodFn(ctx, pod, status)
case update.WorkType == TerminatingPodWork:
var gracePeriod *int64
if opt := update.Options.KillPodOptions; opt != nil {
gracePeriod = opt.PodTerminationGracePeriodSecondsOverride
}
podStatusFn := p.acknowledgeTerminating(pod)
err = p.syncTerminatingPodFn(ctx, pod, status, update.Options.RunningPod, gracePeriod, podStatusFn)
default:
isTerminal, err = p.syncPodFn(ctx, update.Options.UpdateType, pod, update.Options.MirrorPod, status)
}
lastSyncTime = time.Now()
return err
8.上面的函数在kubelet.go中已经对容器和沙箱处理完了。开始对pod的状态进行处理
- 第2行,如果是ctx退出的,则打印日志
- 第5行,如果err不等nil,打印错误日志
- 第8行,如果是已经删除的(流程9)
- 第16行,如果是删除中的(流程10)
- 第29行,如果是同步终端的(流程11)
switch {
case err == context.Canceled:
klog.V(2).InfoS("Sync exited with context cancellation error", "pod", klog.KObj(pod), "podUID", pod.UID, "updateType", update.WorkType)
case err != nil:
klog.ErrorS(err, "Error syncing pod, skipping", "pod", klog.KObj(pod), "podUID", pod.UID)
case update.WorkType == TerminatedPodWork:
p.completeTerminated(pod)
if start := update.Options.StartTime; !start.IsZero() {
metrics.PodWorkerDuration.WithLabelValues("terminated").Observe(metrics.SinceInSeconds(start))
}
klog.V(4).InfoS("Processing pod event done", "pod", klog.KObj(pod), "podUID", pod.UID, "updateType", update.WorkType)
return
case update.WorkType == TerminatingPodWork:
if update.Options.RunningPod != nil {
p.completeTerminatingRuntimePod(pod)
if start := update.Options.StartTime; !start.IsZero() {
metrics.PodWorkerDuration.WithLabelValues(update.Options.UpdateType.String()).Observe(metrics.SinceInSeconds(start))
}
klog.V(4).InfoS("Processing pod event done", "pod", klog.KObj(pod), "podUID", pod.UID, "updateType", update.WorkType)
return
}
p.completeTerminating(pod)
phaseTransition = true
case isTerminal:
klog.V(4).InfoS("Pod is terminal", "pod", klog.KObj(pod), "podUID", pod.UID, "updateType", update.WorkType)
p.completeSync(pod)
phaseTransition = true
}
9.流程8中的第8行
- 先关掉pod的工作状态,因为是删除的了,所以pod的goroutine管道可以关闭了。同时删除这个pod的map信息和还未执行的变化。
- 如果这个pod曾经有过整条状态链路,finished代表结束。同时将working工作中变为false,这样就不会阻塞后续的变化(虽然已经没有了)
- 如果这个pod是已经启动的静态pod,删掉它
func (p *podWorkers) completeTerminated(pod *v1.Pod) {
p.podLock.Lock()
defer p.podLock.Unlock()
klog.V(4).InfoS("Pod is complete and the worker can now stop", "pod", klog.KObj(pod), "podUID", pod.UID)
p.cleanupPodUpdates(pod.UID)
if status, ok := p.podSyncStatuses[pod.UID]; ok {
if status.terminatingAt.IsZero() {
klog.V(4).InfoS("Pod worker is complete but did not have terminatingAt set, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
if status.terminatedAt.IsZero() {
klog.V(4).InfoS("Pod worker is complete but did not have terminatedAt set, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
status.finished = true
status.working = false
if p.startedStaticPodsByFullname[status.fullname] == pod.UID {
delete(p.startedStaticPodsByFullname, status.fullname)
}
}
}
//上面函数的第七行
func (p *podWorkers) cleanupPodUpdates(uid types.UID) {
if ch, ok := p.podUpdates[uid]; ok {
close(ch)
}
delete(p.podUpdates, uid)
delete(p.lastUndeliveredWorkUpdate, uid)
}
10.流程8的第16行
这里和上面的流程基本一样,需要看的是最后一个处理,将最后一个未更新的状态,变为当前pod,并且状态为已删除,这样下次再循环到这个pod的时候,就是进行彻底删除了
func (p *podWorkers) completeTerminating(pod *v1.Pod) {
p.podLock.Lock()
defer p.podLock.Unlock()
klog.V(4).InfoS("Pod terminated all containers successfully", "pod", klog.KObj(pod), "podUID", pod.UID)
if status, ok := p.podSyncStatuses[pod.UID]; ok {
if status.terminatingAt.IsZero() {
klog.V(4).InfoS("Pod worker was terminated but did not have terminatingAt set, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
status.terminatedAt = time.Now()
for _, ch := range status.notifyPostTerminating {
close(ch)
}
status.notifyPostTerminating = nil
status.statusPostTerminating = nil
}
p.lastUndeliveredWorkUpdate[pod.UID] = podWork{
WorkType: TerminatedPodWork,
Options: UpdatePodOptions{
Pod: pod,
},
}
}
11.流程8的第29行,与上方大同小异
func (p *podWorkers) completeSync(pod *v1.Pod) {
p.podLock.Lock()
defer p.podLock.Unlock()
klog.V(4).InfoS("Pod indicated lifecycle completed naturally and should now terminate", "pod", klog.KObj(pod), "podUID", pod.UID)
if status, ok := p.podSyncStatuses[pod.UID]; ok {
if status.terminatingAt.IsZero() {
status.terminatingAt = time.Now()
} else {
klog.V(4).InfoS("Pod worker attempted to set terminatingAt twice, likely programmer error", "pod", klog.KObj(pod), "podUID", pod.UID)
}
status.startedTerminating = true
}
p.lastUndeliveredWorkUpdate[pod.UID] = podWork{
WorkType: TerminatingPodWork,
Options: UpdatePodOptions{
Pod: pod,
},
}
}
12.最后的工作
- 第9行,如果状态转换成功了,添加到woekqueue队列,这个队列后续kubelt会读取,然后执行syncCh函数
- 第11行,如果没有错误,写入队列
- 第13行,如果错误是network is not ready网络原因,暂时退出过一会重试。写入队列
- 第15行 在同步过程中发生错误;写入队列
- 第18行为主要 (流程13)
- 第3行,记录一下这个pod此次处理的开始时间到现在的秒数做记录
p.completeWork(pod, phaseTransition, err)
if start := update.Options.StartTime; !start.IsZero() {
metrics.PodWorkerDuration.WithLabelValues(update.Options.UpdateType.String()).Observe(metrics.SinceInSeconds(start))
}
//上面第一行函数
func (p *podWorkers) completeWork(pod *v1.Pod, phaseTransition bool, syncErr error) {
switch {
case phaseTransition:
p.workQueue.Enqueue(pod.UID, 0)
case syncErr == nil:
p.workQueue.Enqueue(pod.UID, wait.Jitter(p.resyncInterval, workerResyncIntervalJitterFactor))
case strings.Contains(syncErr.Error(), NetworkNotReadyErrorMsg):
p.workQueue.Enqueue(pod.UID, wait.Jitter(backOffOnTransientErrorPeriod, workerBackOffPeriodJitterFactor))
default:
p.workQueue.Enqueue(pod.UID, wait.Jitter(p.backOffPeriod, workerBackOffPeriodJitterFactor))
}
p.completeWorkQueueNext(pod.UID)
}
13.这里是对pod的goroutine进行一些处理
- 如果这个pod还有排队阻塞中的请求,就把这个请求推入到这个pod的goroutine中。
- 如果没有了最新请求,让这个goroutine工作状态变为没有工作中。这样后续的数据才不会阻塞继续执行
func (p *podWorkers) completeWorkQueueNext(uid types.UID) {
p.podLock.Lock()
defer p.podLock.Unlock()
if workUpdate, exists := p.lastUndeliveredWorkUpdate[uid]; exists {
p.podUpdates[uid] <- workUpdate
delete(p.lastUndeliveredWorkUpdate, uid)
} else {
p.podSyncStatuses[uid].working = false
}
}