在K8s中将Pod调度到某一台Node节点之后,后续的状态维护信息则是由对应机器上的kubelet进行维护,如何实时反馈本地运行状态,并通知apiserver则是设计的难点, 本节主要是通过感知Pod状态变化和探测状态改变两个流程来实际分析其核心数据结构,来了解内部设计

1. 状态管理

1.1 静态Pod

 静态Pod主要是指的那些不是通过感知apiserver创建的pod, 因为apiserver上并不包含,但是同时也需要维护和获取这类Pod的状态, k8s中就设计了一个镜像Pod的概念,其实就是为静态Pod镜像出来一个Pod该Pod的主要信息与静态Pod一致,并且在apiserver中进行创建,通过apiserver可以感知的这个镜像Pod来反映真实的静态Pod的状态,

1.2 状态数据源

 statusManager是进行状态同步的关键组件其需要综合当前Pod运行中的数据和apiserver存储的数据,从而决定最终的状态转换, 这里先关注图上画出来的,更多的状态等后续会一一介绍

2. 版本一致性

  1. type versionedPodStatus struct {
  2. status v1.PodStatus
  3. // 单调递增的版本号(每个pod)
  4. version uint64
  5. // Pod name & namespace, for sending updates to API server.
  6. podName string
  7. podNamespace string
  8. }

在Kubelet中为保证与apiserver端信息的同步,在本地保存了一个Pod状态版本信息,其里面除了保存当前Pod的状态数据还有一个版本版本号,通过单调递增的版本号的对比来确定是否进行状态的同步

3. 核心源码实现

statusManager的流程其实还是蛮复杂的,今天我们就只讲一个场景,即kubelet通过apiserver感知到一个Pod更新,然后顺着该功能的数据流来进行梳理statusMangaer里面的数据流转

3.1 核心数据结构

manager中的核心状态相关的数据结构可以主要分为两大类:映射数据维护(podManager、podStatuses、apiStatusVersions)数据通信管道(podStatusChannel), 剩余的则是对与apiserver通信的kublet和进行pod删除检查的 podDeletionSafety

  1. type manager struct {
  2. kubeClient clientset.Interface
  3. // 管理缓存Pod,包含镜像pod和静态pod的映射
  4. podManager kubepod.Manager
  5. // 从pod UID映射到相应pod的版本状态信息 。
  6. podStatuses map[types.UID]versionedPodStatus
  7. podStatusesLock sync.RWMutex
  8. podStatusChannel chan podStatusSyncRequest
  9. // 存储镜像pod的版本
  10. apiStatusVersions map[kubetypes.MirrorPodUID]uint64
  11. podDeletionSafety PodDeletionSafetyProvider
  12. }

3.2 设置Pod状态

设置Pod状态主要是位于kubelet中的syncPod中,在接收到pod事件变更之后,会与apiserver进行 Pod最新数据的同步从而获取当前pod在apiserver端的最新状态

  1. func (m *manager) SetPodStatus(pod *v1.Pod, status v1.PodStatus) {
  2. m.podStatusesLock.Lock()
  3. defer m.podStatusesLock.Unlock()
  4. for _, c := range pod.Status.Conditions {
  5. if !kubetypes.PodConditionByKubelet(c.Type) {
  6. klog.Errorf("Kubelet is trying to update pod condition %q for pod %q. "+
  7. "But it is not owned by kubelet.", string(c.Type), format.Pod(pod))
  8. }
  9. }
  10. // Make sure we're caching a deep copy.
  11. status = *status.DeepCopy()
  12. // 如果Pod被删除了则需要强制与apiserver进行信息的同步
  13. m.updateStatusInternal(pod, status, pod.DeletionTimestamp != nil)
  14. }

3.3 更新内部缓存状态产生同步事件

3.3.1 获取缓存状态

  1. var oldStatus v1.PodStatus
  2. // 检测之前的本地缓存数据
  3. cachedStatus, isCached := m.podStatuses[pod.UID]
  4. if isCached {
  5. oldStatus = cachedStatus.status
  6. } else if mirrorPod, ok := m.podManager.GetMirrorPodByPod(pod); ok {
  7. oldStatus = mirrorPod.Status
  8. } else {
  9. oldStatus = pod.Status
  10. }

3.3.2 检测容器状态

检测容器状态主要是针对容器终止状态转发的合法性进行检测,其实就是根据设定的Pod的RestartPolicy来检测针对一个终止的容器是否可以进行重启

  1. if err := checkContainerStateTransition(oldStatus.ContainerStatuses, status.ContainerStatuses, pod.Spec.RestartPolicy); err != nil {
  2. klog.Errorf("Status update on pod %v/%v aborted: %v", pod.Namespace, pod.Name, err)
  3. return false
  4. }
  5. if err := checkContainerStateTransition(oldStatus.InitContainerStatuses, status.InitContainerStatuses, pod.Spec.RestartPolicy); err != nil {
  6. klog.Errorf("Status update on pod %v/%v aborted: %v", pod.Namespace, pod.Name, err)
  7. return false
  8. }

3.3.3 更新PodCondition最后转换时间

通过最新的status里面的condition设定对应PodCondition的LastTransitionTime更新时间未当前时间

  1. // Set ContainersReadyCondition.LastTransitionTime.
  2. updateLastTransitionTime(&status, &oldStatus, v1.ContainersReady)
  3. // Set ReadyCondition.LastTransitionTime.
  4. updateLastTransitionTime(&status, &oldStatus, v1.PodReady)
  5. // Set InitializedCondition.LastTransitionTime.
  6. updateLastTransitionTime(&status, &oldStatus, v1.PodInitialized)
  7. // Set PodScheduledCondition.LastTransitionTime.
  8. updateLastTransitionTime(&status, &oldStatus, v1.PodScheduled)

3.3.4 校对时间截断过长信息

首先会根据当前容器的个数,从而决定每个容器最大的字节数大小,然后对容器里面的终止状态里面的Message信息,进行截断,同时进行时间的校对

  1. normalizeStatus(pod, &status)

3.3.5 状态更新条件检测

如果之前已经缓存了对应的数据,并且缓存的数据与当前的状态未发生改变,也不需要强制更新,就直接返回

  1. if isCached && isPodStatusByKubeletEqual(&cachedStatus.status, &status) && !forceUpdate {
  2. // 如果不强制更新 ,默认是true此处不会成立
  3. klog.V(3).Infof("Ignoring same status for pod %q, status: %+v", format.Pod(pod), status)
  4. return false // No new status.
  5. }

3.3.6 生成同步事件更新缓存

生成最新的状态缓存数据,并且递增本地的版本信息

  1. // 构建新的状态
  2. newStatus := versionedPodStatus{
  3. status: status,
  4. version: cachedStatus.version + 1, // 更新器缓存
  5. podName: pod.Name,
  6. podNamespace: pod.Namespace,
  7. }
  8. // 更新新的缓存状态
  9. m.podStatuses[pod.UID] = newStatus
  10. select {
  11. case m.podStatusChannel <- podStatusSyncRequest{pod.UID, newStatus}: // 构建一个新的同步请求
  12. klog.V(5).Infof("Status Manager: adding pod: %q, with status: (%d, %v) to podStatusChannel",
  13. pod.UID, newStatus.version, newStatus.status)
  14. return true
  15. default:
  16. // Let the periodic syncBatch handle the update if the channel is full.
  17. // We can't block, since we hold the mutex lock.
  18. klog.V(4).Infof("Skipping the status update for pod %q for now because the channel is full; status: %+v",
  19. format.Pod(pod), status)
  20. return false
  21. }

3.4 探测状态更新

 探测状态其实就是Pod内容器的运行状态,比如如果设置了Readiness探测,当某个容器探测失败的时候,就会通知对应的service从后端的enpoint中移除该Pod, 让我们一起看看Kubelet是如何将运行状态通知到apiserver端的

3.4.1 获取当前状态

  1. func (m *manager) SetContainerReadiness(podUID types.UID, containerID kubecontainer.ContainerID, ready bool) {
  2. m.podStatusesLock.Lock()
  3. defer m.podStatusesLock.Unlock()
  4. // 获取本地的容器
  5. pod, ok := m.podManager.GetPodByUID(podUID)
  6. if !ok {
  7. klog.V(4).Infof("Pod %q has been deleted, no need to update readiness", string(podUID))
  8. return
  9. }
  10. // 获取当前的状态
  11. oldStatus, found := m.podStatuses[pod.UID]
  12. if !found {
  13. klog.Warningf("Container readiness changed before pod has synced: %q - %q",
  14. format.Pod(pod), containerID.String())
  15. return
  16. }
  17. // 获取当前的容器状态
  18. containerStatus, _, ok := findContainerStatus(&oldStatus.status, containerID.String())
  19. if !ok {
  20. klog.Warningf("Container readiness changed for unknown container: %q - %q",
  21. format.Pod(pod), containerID.String())
  22. return
  23. }

3.4.2 检测状态是否发生改变

  1. // 检测前后的就绪状态是否发生改变
  2. if containerStatus.Ready == ready {
  3. klog.V(4).Infof("Container readiness unchanged (%v): %q - %q", ready,
  4. format.Pod(pod), containerID.String())
  5. return
  6. }

3.4.3 修改容器的就绪状态

获取容器的状态,修改就绪为当前的状态

  1. status := *oldStatus.status.DeepCopy()
  2. containerStatus, _, _ = findContainerStatus(&status, containerID.String())
  3. containerStatus.Ready = ready

3.4.4 根据最新的容器状态修改

会根据当前运行时的容器探测的状态,来修改对应PodCondition里面的状态,最后调用内部的更新逻辑

  1. updateConditionFunc := func(conditionType v1.PodConditionType, condition v1.PodCondition) {
  2. conditionIndex := -1
  3. // 获取Pod对应的PodCondition状态
  4. for i, condition := range status.Conditions {
  5. if condition.Type == conditionType {
  6. conditionIndex = i
  7. break
  8. }
  9. }
  10. // 修改或追加Pod对应的PodCondition状态
  11. if conditionIndex != -1 {
  12. status.Conditions[conditionIndex] = condition
  13. } else {
  14. klog.Warningf("PodStatus missing %s type condition: %+v", conditionType, status)
  15. status.Conditions = append(status.Conditions, condition)
  16. }
  17. }
  18. // 计算Ready状态
  19. updateConditionFunc(v1.PodReady, GeneratePodReadyCondition(&pod.Spec, status.Conditions, status.ContainerStatuses, status.Phase))
  20. // 计算容器Ready状态
  21. updateConditionFunc(v1.ContainersReady, www.sangyulpt.comGenerateContainersReadyCondition(&pod.Spec, status.ContainerStatuses, status.Phase))
  22. m.updateStatusInternal(pod, status, false)

3.5 启动后台同步任务

statusManager会启动一个后台的线程来进行更新管道里面同步请求的消费

  1. func (m *manager) Start() {
  2. // 省略非核心代码
  3. go wait.Forever(func() {
  4. select {
  5. case syncRequest := <-m.podStatusChannel:
  6. // 获取最新的状态信息,更新apiserver
  7. klog.V(5).Infof("Status Manager: syncing pod: %q, with status: (%d, %v) from podStatusChannel",
  8. syncRequest.podUID, syncRequest.status.version, syncRequest.status.status)
  9. m.syncPod(syncRequest.podUID,www.huanhua2zhuc.cn syncRequest.status)
  10. case <-syncTicker:
  11. m.syncBatch()
  12. }
  13. }, 0)
  14. }

3.6 同步Pod状态

3.6.1 同步条件检测

同步条件检测主要是检测镜像Pod的版本是否发送变化、Pod当前是否被删除,如果pod没有被删除则返回false,即对一个没有删除的Pod我们还是需要继续更新其状态的

  1. if !m.needsUpdate(uid, status) {
  2. klog.V(1).Infof("Status for pod %q is up-to-date; skipping", uid)
  3. return
  4. }

3.6.2 通过apiserver获取最新Pod数据

如果没有获取到Pod信息,则直接进行退出即可

  1. pod, err := m.kubeClient.CoreV1().Pods(status.podNamespace).Get(status.podName, metav1.GetOptions{www.henxingyule.com })
  2. if errors.IsNotFound(err) {
  3. klog.V(3).Infof("Pod %q does not exist on the server", format.PodDesc(status.podName, status.podNamespace, uid))
  4. // 如果Pod已经被删除了,就直接退出就行
  5. return
  6. }
  7. if err != nil {
  8. klog.Warningf("Failed to get status for pod %q:  www.jinniuyulzc.cn %v", format.PodDesc(www.wangffzc.cn status.podName, status.podNamespace, www.tainfengyue.cn uid), err)
  9. return
  10. }

3.6.3 调用Patch接口进行更新

这里面会通过将最小的状态与之前的状态来进行merge合并,然后调用kubeClient进行apiserver端状态的修改

  1. oldStatus := pod.Status.DeepCopy(www.yixingylzc.cn  )
  2. // 更新服务端的状态
  3. newPod, patchBytes, err := statusutil.PatchPodStatus(m.kubeClient, pod.Namespace, pod.Name, pod.UID, *oldStatus, mergePodStatus(*oldStatus, status.status))
  4. klog.V(3).Infof("Patch status for pod %q with %q", format.Pod(pod), patchBytes)
  5. if err != nil {
  6. klog.Warningf("Failed to update status for pod %q: %v", format.Pod(pod), err)
  7. return
  8. }

3.6.4 更新本地的Apiserver端的版本信息

  1. // 当前是最新的状态
  2. pod = newPod
  3. klog.V(3).Infof("Status for pod %q updated successfully: (%d, %+v)", format.Pod(pod), status.version, status.status)
  4. m.apiStatusVersions[kubetypes.MirrorPodUID(pod.UID)] = status.version

3.6.5 检测删除Pod

这里主要是最后阶段,即Pod对应的资源都已经释放了,则才最终删除apiserver端的Pod

  1. // 如果pod的DeletionTimestamp被设置,则对应的Pod需要被删除
  2. if m.canBeDeleted(pod, status.status) {
  3. deleteOptions := metav1.NewDeleteOptions(www.tengyao3zc.cn)
  4. deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(pod.UID))
  5. // 调用apiserver对Pod进行删除
  6. err = m.kubeClient.CoreV1().Pods(pod.Namespace).Delete(pod.Name, deleteOptions)
  7. if err != nil {
  8. klog.Warningf("Failed to delete status for pod %q: %v", format.Pod(pod), err)
  9. return
  10. }
  11. klog.V(3).Infof("Pod %q fully terminated and removed from etcd", format.Pod(pod))
  12. m.deletePodStatus(uid)

图解kubernetes容器状态同步机制核心实现的更多相关文章

  1. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  2. kubernetes容器编排系统介绍

    版权声明:本文由turboxu原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/152 来源:腾云阁 https://www. ...

  3. 一文带你看透kubernetes 容器编排系统

    本文由云+社区发表 作者:turboxu Kubernetes作为容器编排生态圈中重要一员,是Google大规模容器管理系统borg的开源版本实现,吸收借鉴了google过去十年间在生产环境上所学到的 ...

  4. Kubernetes容器集群管理环境 - Prometheus监控篇

    一.Prometheus介绍之前已经详细介绍了Kubernetes集群部署篇,今天这里重点说下Kubernetes监控方案-Prometheus+Grafana.Prometheus(普罗米修斯)是一 ...

  5. Kubernetes容器集群管理环境 - 完整部署(下篇)

    在前一篇文章中详细介绍了Kubernetes容器集群管理环境 - 完整部署(中篇),这里继续记录下Kubernetes集群插件等部署过程: 十一.Kubernetes集群插件 插件是Kubernete ...

  6. Kubernetes 学习(十)Kubernetes 容器持久化存储

    0. 前言 最近在学习张磊老师的 深入剖析Kubernetes 系列课程,最近学到了 Kubernetes 容器持久化存储部分 现对这一部分的相关学习和体会做一下整理,内容参考 深入剖析Kuberne ...

  7. 从零入门 Serverless | 一文讲透 Serverless Kubernetes 容器服务

    作者 | 张维(贤维) 阿里云函数计算开发工程师 导读:Serverless Kubernetes 是以容器和 kubernetes 为基础的 Serverless 服务,它提供了一种简单易用.极致弹 ...

  8. [内核同步]浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  9. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

随机推荐

  1. ASP.NETCore -----导入Excel文件

    前端上传excel文件利用npoi读取数据转换成datatable(netcore坑爹啊,用的vs2017竟然不能可视化) 前端界面 @{ Layout = null; } <!DOCTYPE ...

  2. GAN网络进行图片增强

    GAN网络进行图片增强 基于Tensorflow框架 调用ModifyPictureSize.py文件 代码如下: from skimage import io,transform,color imp ...

  3. [JZOJ]1293.气象牛[区间DP]

    Description 为了研究农场的气候,Betsy帮助农夫John做了N(1 <= N <= 100)次气压测量并按顺序记录了结果M_1-M_N(1 <= M_i <= 1 ...

  4. ubuntu12.04 eclipse安装PyDev

    在ubuntu软件中心安装的eclipse版本为3.7,install new software时,搜索出来的PyDev版本较高(5.6...): 高版本的PyDev要求较高版本的eclipse.详情 ...

  5. OleDbCommand 的用法

    OleDbCommand 的用法 OleDbConnection con=new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; dat ...

  6. 实验吧-隐写术-黑与白(二)(反转+五笔+Image steganography)

    反转有二:颜色反转.文件名反转 文件名这么乱,毫无规律,好奇怪,进行反转后发现是:steganography(就是隐写术的意思),这还是个图片文件,有一款工具正好叫Image steganograph ...

  7. JAVA中序列化和反序列化中的静态成员问题

    关于这个标题的内容是面试笔试中比较常见的考题,大家跟随我的博客一起来学习下这个过程. ? ? JAVA中的序列化和反序列化主要用于: (1)将对象或者异常等写入文件,通过文件交互传输信息: (2)将对 ...

  8. 使用模拟器调试react-native步骤(安卓机)

    1.在cmd界面搭建react-native 环境: 可参考https://reactnative.cn/docs/0.51/getting-started.html#content (1)npm i ...

  9. java课程课后作业190616之个人学期总结

    在团队开始的那一周,我们做了作品的功能畅想,在讲台上谈论了自己的产品可能会有的功能,比如说课程查找功能,空教室查找功能,霸屏功能,课程留言功能等,当然,随着开发的推进,我也发现了有些功能上实现的困难, ...

  10. Social GAN代码要点记录

    近日在阅读Social GAN文献的实验代码,加深对模型的理解,发现源代码的工程化很强,也比较适合构建实验模型的学习,故细致阅读.下文是笔者阅读中一些要点总结,有关于pytorch,也有关于模型自身的 ...