kubelet源码 删除pod pod_workers.go(三)

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)
		}
	}
}
  1. 这里的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
	}
}

上一篇 kubelet源码 删除pod(二)

下一篇 kubelet源码 删除pod(SyncTerminatingPod函数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值