kubelet源码分析 创建/更新/调谐 pod
admin
2024-02-15 16:11:38
0

kubelet源码 创建/更新/协调pod

这篇写的就是创建、更新、调谐、同步时触发的函数变化。
一、调谐(Reconcile)

一、调谐(Reconcile)

写过CRD的应该对这个函数很熟悉,当CRD有变化的时候或每过2s都是通过触发这个函数进行比对,调谐pod的状态
但是这个调谐和CRD的不一样,这里只是调谐READY和被驱逐或者失败的pod
kubectl apply -f demo.yaml
1.类似于kubelet删除pod中的流程2

		case kubetypes.RECONCILE:klog.V(4).InfoS("SyncLoop RECONCILE", "source", u.Source, "pods", klog.KObjs(u.Pods))handler.HandlePodReconcile(u.Pods)

这里触发了reconcile函数

2.这个函数主要三个流程步骤

  • 先判断是否是静态pod,如果是静态pod,更新一下静态pod的缓存
  • 判断这个pod是否需要调谐(流程3)
  • 这个pod是否是已经完成或者失败的,如果是,则进行清理(流程8)
func (kl *Kubelet) HandlePodReconcile(pods []*v1.Pod) {start := kl.clock.Now()for _, pod := range pods {kl.podManager.UpdatePod(pod)if status.NeedToReconcilePodReadiness(pod) {mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)kl.dispatchWork(pod, kubetypes.SyncPodSync, mirrorPod, start)}****//流程8}
}

3.验证是否需要调谐

  • 第二行查看是否设置了readinessGates,如果没设置,直接返回。
  • 第5行开始是检测设定这个条件的实际状态(流程4)
  • 第7行是检测运行中为ready类型的conditions(就是验证一下是否就绪了)。这里可以简单说一下,如果用户添加了readinessGates,那readinessGates必须通过检查,否则ready类型的conditions会变为false(流程6)
  • 如果运行中的和实际的条件不符,则是需要调谐
func NeedToReconcilePodReadiness(pod *v1.Pod) bool {if len(pod.Spec.ReadinessGates) == 0 {return false}podReadyCondition := GeneratePodReadyCondition(&pod.Spec, pod.Status.Conditions, pod.Status.ContainerStatuses, pod.Status.Phase)i, curCondition := podutil.GetPodConditionFromList(pod.Status.Conditions, v1.PodReady)if i >= 0 && (curCondition.Status != podReadyCondition.Status || curCondition.Message != podReadyCondition.Message) {return true}return false
}

4.验证一下实际状态

  • 前四行是获得状态,后续会验证状态(流程5)
  • 从GenerateContainersReadyCondition函数开始
  • 如果containerStatuses(容器状态)为空,则代表肯定没有ready,直接status返回false
  • 如果有容器,但是!containerStatus.Ready容器没有运行中。则也是没有ready的
  • 如果容器都没有,肯定也是没ready
  • 第29行,如果容器已经运行完成了(源码中会显示Succeeded,如果kubectl会显示Completed)并且没有没启动的容器,则返回未就绪,原因是已完成
  • 第36行开始,是把没有容器和容器未ready的汇总一下,拼接成一个字符串。如果错误不为空,则返回的是没有ready
  • 如果没有任何错误,返回的是ready
func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {containersReady := GenerateContainersReadyCondition(spec, containerStatuses, podPhase)*******
}func GenerateContainersReadyCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {if containerStatuses == nil {return v1.PodCondition{Type:   v1.ContainersReady,Status: v1.ConditionFalse,Reason: UnknownContainerStatuses,}}unknownContainers := []string{}unreadyContainers := []string{}for _, container := range spec.Containers {if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok {if !containerStatus.Ready {unreadyContainers = append(unreadyContainers, container.Name)}} else {unknownContainers = append(unknownContainers, container.Name)}}if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 {return v1.PodCondition{Type:   v1.ContainersReady,Status: v1.ConditionFalse,Reason: PodCompleted,}}unreadyMessages := []string{}if len(unknownContainers) > 0 {unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))}if len(unreadyContainers) > 0 {unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unready status: %s", unreadyContainers))}unreadyMessage := strings.Join(unreadyMessages, ", ")if unreadyMessage != "" {return v1.PodCondition{Type:    v1.ContainersReady,Status:  v1.ConditionFalse,Reason:  ContainersNotReady,Message: unreadyMessage,}}return v1.PodCondition{Type:   v1.ContainersReady,Status: v1.ConditionTrue,}
}

5.重新回到上面的函数,获取了容器实际状态后,要做返回值判断了

  • 第4行,如果容器未能ready,则返回pod未ready
  • 如果容器都ready了,需要验证用户自定义的readinessGates了
  • 第14行开始循环它,如果未找到,则输入错误,如果找到了但是为ready,也是输入错误
  • 如果错误不为空,则返回pod未ready
  • 如果错误为空,代表pod是ready状态的
func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {containersReady := GenerateContainersReadyCondition(spec, containerStatuses, podPhase)if containersReady.Status != v1.ConditionTrue {return v1.PodCondition{Type:    v1.PodReady,Status:  containersReady.Status,Reason:  containersReady.Reason,Message: containersReady.Message,}}unreadyMessages := []string{}for _, rg := range spec.ReadinessGates {_, c := podutil.GetPodConditionFromList(conditions, rg.ConditionType)if c == nil {unreadyMessages = append(unreadyMessages, fmt.Sprintf("corresponding condition of pod readiness gate %q does not exist.", string(rg.ConditionType)))} else if c.Status != v1.ConditionTrue {unreadyMessages = append(unreadyMessages, fmt.Sprintf("the status of pod readiness gate %q is not \"True\", but %v", string(rg.ConditionType), c.Status))}}if len(unreadyMessages) != 0 {unreadyMessage := strings.Join(unreadyMessages, ", ")return v1.PodCondition{Type:    v1.PodReady,Status:  v1.ConditionFalse,Reason:  ReadinessGatesNotReady,Message: unreadyMessage,}}return v1.PodCondition{Type:   v1.PodReady,Status: v1.ConditionTrue,}
}

6.这里比较简单,找到conditions中为ready类型的返回。然后返回流程3的最后一步

func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) {if conditions == nil {return -1, nil}for i := range conditions {if conditions[i].Type == conditionType {return i, &conditions[i]}}return -1, nil
}

7.这是流程3的代码,如果需要调谐,则进入到dispatchWork函数,然后触发了podworkers.updatepod。之后的就可以参考kubelet源码 删除pod(二)

		if status.NeedToReconcilePodReadiness(pod) {mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)kl.dispatchWork(pod, kubetypes.SyncPodSync, mirrorPod, start)}func (kl *Kubelet) dispatchWork(pod *v1.Pod, syncType kubetypes.SyncPodType, mirrorPod *v1.Pod, start time.Time) {// Run the sync in an async worker.zkl.podWorkers.UpdatePod(UpdatePodOptions{Pod:        pod,MirrorPod:  mirrorPod,UpdateType: syncType,StartTime:  start,})*****
}

8.接下来要看pod是不是被驱逐和失败的状态,从缓存中取到pod信息,进行删除

		if eviction.PodIsEvicted(pod.Status) {if podStatus, err := kl.podCache.Get(pod.UID); err == nil {kl.containerDeletor.deleteContainersInPod("", podStatus, true)}}

二、添加

1触发HandlePodAdditions函数进行创建

  • 对pod根据时间戳升序排序
  • 如果是静态pod,走静态pod处理
  • 第17行,如果这个pod不是在停止中,就需要进行特殊处理。(pod是否能在node上创建的校验)
  • 第18行,流程2
  • 第19行,流程3
  • 第20行,rejectPod函数如果不能创建,则记录一下原因。
  • 通过校验后。dispatchWork触发,执行和上述一样的函数进行创建pod
  • 最后一行,把这个pod增加到定时探测中
case kubetypes.ADD:klog.V(2).InfoS("SyncLoop ADD", "source", u.Source, "pods", klog.KObjs(u.Pods))handler.HandlePodAdditions(u.Pods)func (kl *Kubelet) HandlePodAdditions(pods []*v1.Pod) {start := kl.clock.Now()sort.Sort(sliceutils.PodsByCreationTime(pods))for _, pod := range pods {existingPods := kl.podManager.GetPods() kl.podManager.AddPod(pod)if kubetypes.IsMirrorPod(pod) {kl.handleMirrorPod(pod, start)continue}if !kl.podWorkers.IsPodTerminationRequested(pod.UID) {activePods := kl.filterOutTerminatedPods(existingPods)if ok, reason, message := kl.canAdmitPod(activePods, pod); !ok {kl.rejectPod(pod, reason, message)continue}}mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)kl.probeManager.AddPod(pod)}
}

2.把所有pod进行校验,验证一下新创建的pod是否允许创建。

  • 第4行和7行,如果pod停止完成了或者运行完成和失败的并且不是停止中。就跳过
  • 否则记录存入切片里返回。获得已经正在成功运行的pod后续好计算CPU和内存
func (kl *Kubelet) filterOutInactivePods(pods []*v1.Pod) []*v1.Pod {filteredPods := make([]*v1.Pod, 0, len(pods))for _, p := range pods {if kl.podWorkers.IsPodKnownTerminated(p.UID) {continue}if kl.isAdmittedPodTerminal(p) && !kl.podWorkers.IsPodTerminationRequested(p.UID) {continue}filteredPods = append(filteredPods, p)}return filteredPods
}

3.校验是否能在node上创建
第四行为函数校验,这里情况比较多,不全部贴代码,大概是验证一下pod的可创建型,有以下几种函数

  • node是否已经ready,如果没有ready,可以根据pod的优先级是否可以创建(2000000000优先级可以直接创建)
  • sysctl是否是有效的
  • node节点上的资源是否足够
func (kl *Kubelet) canAdmitPod(pods []*v1.Pod, pod *v1.Pod) (bool, string, string) {attrs := &lifecycle.PodAdmitAttributes{Pod: pod, OtherPods: pods}for _, podAdmitHandler := range kl.admitHandlers {if result := podAdmitHandler.Admit(attrs); !result.Admit {return false, result.Reason, result.Message}}return true, "", ""
}

上面的验证都通过,就会创建pod了

三、更新

1.同上原理

	case kubetypes.UPDATE:klog.V(2).InfoS("SyncLoop UPDATE", "source", u.Source, "pods", klog.KObjs(u.Pods))handler.HandlePodUpdates(u.Pods)

2.因为触发的是updates函数,所以和删除是一个道理,参考kubelet源码 删除pod(二)

相关内容

热门资讯

122.(leaflet篇)l... 听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
Vue使用pdf-lib为文件... 之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vu...
PyQt5数据库开发1 4.1... 文章目录 前言 步骤/方法 1 使用windows身份登录 2 启用混合登录模式 3 允许远程连接服...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
Linux基础命令大全(上) ♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维...
再谈解决“因为文件包含病毒或潜... 前面出了一篇博文专门来解决“因为文件包含病毒或潜在的垃圾软件”的问题,其中第二种方法有...
南京邮电大学通达学院2023c... 题目展示 一.问题描述 实验题目1 定义一个学生类,其中包括如下内容: (1)私有数据成员 ①年龄 ...
PageObject 六大原则 PageObject六大原则: 1.封装服务的方法 2.不要暴露页面的细节 3.通过r...
【Linux网络编程】01:S... Socket多进程 OVERVIEWSocket多进程1.Server2.Client3.bug&...
数据结构刷题(二十五):122... 1.122. 买卖股票的最佳时机 II思路:贪心。把利润分解为每天为单位的维度,然后收...
浏览器事件循环 事件循环 浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间࿰...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
计算机二级Python备考(2... 目录  一、选择题 1.在Python语言中: 2.知识点 二、基本操作题 1. j...
端电压 相电压 线电压 记得刚接触矢量控制的时候,拿到板子,就赶紧去测各种波形,结...
如何使用Python检测和识别... 车牌检测与识别技术用途广泛,可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计...
带环链表详解 目录 一、什么是环形链表 二、判断是否为环形链表 2.1 具体题目 2.2 具体思路 2.3 思路的...
【C语言进阶:刨根究底字符串函... 本节重点内容: 深入理解strcpy函数的使用学会strcpy函数的模拟实现⚡strc...
Django web开发(一)... 文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和s...