专栏首页k8s技术圈图解 K8S 控制器 Node 生命周期管理

图解 K8S 控制器 Node 生命周期管理

Node其实就对应着kubernetes中的工作组件,今天我们来看下kubernetes中针对Node的生命周期的管理包括心跳检测/污点/容忍/中断等机制的实现

1. 基础概念

1.1 心跳机制

心跳机制是分布式调度系统中常见的设计, kubernetes中通过Lease这种资源来进行实现心跳信息的传递,其在kubelet上运行nodelease的线程定时向apiserver更新自己的心跳信息, 同时controller控制器中的nodelifecycle会通过informer来监听集群中的lease信息

1.2 node状态

Node的状态除了相关资源压力、网络不可达之外,其实只有一个Ready状态, 对应的Status则同其他资源一样三种:ConditionTrue、conditionFalse、conditionUnknown,那什么时候会是一个ready状态呢, 其实在kubernetes中我们有三大列资源: CRI、CNI、CSI,如果任一一个运行时出现错误,则当前节点就不是Ready状态

1.3 Taint和Toleration

Taint(污点)和Toleration(容忍)是kubernetes中的调度约束的一种实现,其通过一种打标签的方式,来进行调度约束,当一个Node节点被打上了一个Taint则调度器在进行调度的时候,如果发现当前调度的pod没有容忍这种污点,则当前节点就不能调度过去, 其次如果发现对应节点上已经运行的pod不能容忍node新标记的Taint, 则可能会被从当前节点中驱逐

1.4 基于心跳/Taint/Toleration的故障转移

kubernetes中针对Node节点down机的故障转移的实现,其实就是记住这几个机制来进行的,在发现无法接收到对应节点的心跳之后,kubernetes就会给对应节点打上一些异常的Taint, 并且根据之前缓存的节点上的Pod来进行检查,如果发现没有对应的Toleration,则就会尝试驱逐这些Pod, 然后由对应的控制器发现Pod的删除, 就开始重新进行补偿, 然后经过调度器选择新的节点运行

2. 容忍与驱逐设计剖析

前面我们介绍了基础的一些概念, 接下来我们一起去探索其内部的具体设计与实现

2.1 Node的Taints由来

在node生命周期控制器中,Node为了实现驱逐节点的目标,主要是通过为Node生成对应的taings来进行,那Node中的taints除了运维为指定节点赋予的之外,其余的则是通过Status里面的Conditions来进行计算而来

nodeConditionToTaintKeyStatusMap = map[v1.NodeConditionType]map[v1.ConditionStatus]string{
        v1.NodeReady: {
            v1.ConditionFalse:   v1.TaintNodeNotReady,
            v1.ConditionUnknown: v1.TaintNodeUnreachable,
        },
        v1.NodeMemoryPressure: {
            v1.ConditionTrue: v1.TaintNodeMemoryPressure,
        },
        v1.NodeDiskPressure: {
            v1.ConditionTrue: v1.TaintNodeDiskPressure,
        },
        v1.NodeNetworkUnavailable: {
            v1.ConditionTrue: v1.TaintNodeNetworkUnavailable,
        },
        v1.NodePIDPressure: {
            v1.ConditionTrue: v1.TaintNodePIDPressure,
        },
    }

这里是一个对应表,当节点的Status里面的状态还有COnditionType的值来决定为其添加哪个对应的Taint

2.2 Node Taints增量更新

上面通过Condition状态计算出节点需要增加或者删除的Taint之后,其实就会调用对应的apiserver当前node的最新的Taints, 至此node controller完成了第一阶段的工作, 即根据当前的状态来进行更新对应的Taint, 有了这些Taint别的组件就可以进行Pod的驱逐了

2.3 Pod状态更新

那如果Pod的状态更新,我也需要关注吗或者说node controller关注了Pod主要会做什么呢?其实这是一个联动操作,如果发现一个pod当前是Ready状态,但是node已经不是Ready状态了,则此时就要立马更新pod的Status,其实就是为了通知对应的endpoints摘除对应的pod因为他所在的node已经出现问题了

2.4 Taint管理器

前面我们提到k8s中会根据conditionType计算出要为node添加的Taints, 然后就直接更新apiserver了,其实剩下的工作都是交给NoExecuteTaintManager这个组件来完成的,这个组件是干什么的呢,从名字就可以看出NoExecute就跟我们自己给Node添加 Taint一样,立即驱逐不满足的Pod

2.5 容忍检测与驱逐

当一个Node更新的时候,首先会获取当前Node上面所有Taint的Effect为NoExecute的Taints,然后会去获取当node上面的所有Pod然后逐个检测Pod是否有对应的Tolerations, 然后没有就通过定时器创建一个驱逐的任务延迟执行

如果一个节点可以正常反馈状态,通过如上步骤其实就可以不断的进行对比那些不能容忍节点Taint的Pod从而达到驱逐的目的,但是如果无法反馈信息呢,则就需要靠心跳检测模块来进行

3. 心跳检测

心跳检测主要是根据当前缓存的node信息来确定在过去一段时间内, node是否正常发送信息, 如果没有发生,则对应的node可能就已经挂了,针对这部分node我们就要进行处理

3.1 自我保护

自我保护是分布式系统里面比较常见的一种机制,其核心是为了防止当发生网络分区的时候,因为主节点被隔离而导致正常节点的心跳全部失效,从而触发异常处理流程,自我保护就是如果发生大面积心跳中断的情况下,集群的一种保护机制

3.2 zone

上面提到的自我保护那么如何判断是否达到自我保护的条件呢, 在k8s中通过zone来将node分区,中断则是判断如果发现当前zone里面一个ready的节点都没有,但是却发现有没有ready节点, 这样就可以知道一个的zone对应的状态,如果遍历当前的所有zone发现全都这样,则就会开启保护关闭中断

3.3 限速队列

清除Node上面Pod的这个操作可不算小, 在kubernetes中以一种比较保守的数值在做这件事情, 官方文档上也有说明, 限速主要是为了保证系统的稳定性,因为其实清除任务是可以被取消的,比如说节点升级的时候,可能几十秒就起来了,那其上的pod还可以依旧运行,没有必要全部转移,通过限速队列可以让删除节点的操作慢下来,从而容忍更多的超时时间,注意这里的限速队列是每个zone都会有一个

3.4 中断标签

node.kubernetes.io/exclude-disruption

在k8s中的node可以打上一个特殊的label标签如上,当发现该标签的时候,node controller就不会对对应的node进行检测,这种情况下,除非对应的Pod/Node重新发送事件来驱动整个系统,否则就好比一座孤岛

3.5 中断控制

在真正健康检查之前,node controller首先会过滤掉前面提到的中断标签,然后对剩余的node这里我们称为当前的zone的node集合,然后对当前的zone下面的状态进行检测,检测上面提到的zone的自我保护,即是当前的中断状态

 switch {
    case readyNodes == 0 && notReadyNodes > 0:
        //全部中断
        return notReadyNodes, stateFullDisruption
    case notReadyNodes > 2 && float32(notReadyNodes)/float32(notReadyNodes+readyNodes) >= nc.unhealthyZoneThreshold: //0.55
        // 部分中断
        return notReadyNodes, statePartialDisruption
    default:
        return notReadyNodes, stateNormal
    }

其次还会对之前的状态做一个检测(每次计算完都会讲之前的zone的结果进行保存),在这两次检测中会去检测一个关键的状态即stateFullDisruption,即是是否发生全部中断,这里为什么要进行前后状态的检测呢,其实是为了状态的转移,即进入和退出FullDIsruption

3.6 完全中断的幕后任务

因为是完全中断所以当前节点的之前所有的驱逐任务都要被取消,其次还要关闭后续的健康检查,怎么关闭呢?答案其实就是用到之前的限速队列,让其值为0,这样就不会继续进行健康检查了

3.7 Node健康检查之检查

前面主要了解了Node健康检查的整体的设计,那究竟是怎么确定一个Node的状态呢,答案其实就是Lease和probeTimestamp, 在每次获取到一个新的Lease的时候,都会更新probeTimestamp即探测时间为当前时间,检测的时候如果发现probeTimestamp加上延迟时间小于当前时间,则就会为其更新对应的Condition的Type和status字段, 并且会缓存当前的数据, 同时如果发现节点前后的ready状态发生改变还会更新apiserver

3.8 根据观测状态进行状态转移

状态转移主要是指的根据观察到的ReadCondition的状态来进行转移,如果发现对应的Ready状态为ConditionFalse和ConditionUnknown则就会将其加入到对应zone的map中的等待后续处理,如果Ready为COnditionTrue则会移除对应的taint从而正常调度pod

4. 设计总结

看完了全部的设计,那如果我们要做一个Node升级的管理器该怎么做呢?我们需要从如下几个点去考虑1.从上面的设计里面我们可以看出如果Node的状态为Ready则表明至少我们的CRI/CNI/CSI组件没有问题,从这个角度至少证明这个Node已经基本正常2.一个Node要被使用则至少需要被apiserver/scheduler/controller所感知,我们可以结合Node的驱逐机制来去通过一个特殊的Taint和一个特殊的Pod来进行调度,这样就可以证明这个Node在集群中是一个可用的状态3.通过上面两种办法我们可以基本证明单台机器基本可用, 则剩下的其实就是怎么加快这个流程,这个其实可以通过设定当前集群的低水位来实现,从而决定当前集群最多可以允许同时灰度多少Node

当然生产环境中肯定有各式各样的问题,需要应对不同的软硬件问题,欢迎一起交流,K8S 学习笔记地址: https://www.yuque.com/baxiaoshi/tyado3

本文分享自微信公众号 - k8s技术圈(kube100)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Kubernetes 源码学习之延时队列

    client-go 中的 workqueue,类似于 golang 语言中的 channel,主要用于并发程序之间的数据同步。Kubernetes 的控制器模型...

    我是阳明
  • 说一说 Serverless 到底是什么?

    Serverless 是一种云原生开发模型,可使开发人员专注构建和运行应用,而无需管理服务器。简单来说 Serverless 就是让你不与或少与运行应用程序所需...

    我是阳明
  • 使用 Elastic Stack 构建 Kubernetes 全栈监控(2/4)

    在前文中我们已经安装配置了 ElasticSearch 的集群,本文我们将来使用 Metricbeat 对 Kubernetes 集群进行监控。Metricbe...

    我是阳明
  • 深入理解Condition

    建议先看一下这篇分享,深入理解AbstractQueuedSynchronizer,这篇文章主要介绍了AQS的同步队列实现,而本篇文章主要介绍AQS条件队列的实...

    Java识堂
  • Node.js 基础

    梨涡浅笑
  • 第一章:NodeJS 概述

    Node 概述 什么是 Node Node.js® is a JavaScript runtime built on Chrome's V8 JavaScrip...

    老马
  • 【专业技术】Node.js 究竟是什么?

    简介 如果您听说过 Node,或者阅读过一些文章,宣称 Node 是多么多么的棒,那么您可能会想:“Node 究竟是什么东西?” 即便是在参阅 Node 的主页...

    程序员互动联盟
  • 深入浅出 Nodejs ( 一 ) :Nodejs 的简介

    我认为 Node 是一门独具风格的技术,它的特点很有意思,本章我们主要讲 Node 的特点,Node 应用场景以及 Node 的使用者。

    serena
  • 【Leetcode】133.克隆图

    给定无向连通图中一个节点的引用,返回该图的深拷贝(克隆)。图中的每个节点都包含它的值 val(Int) 和其邻居的列表(list[Node])。

    Leetcode名企之路
  • 按深度打印二叉树节点数据

    之前去面试,被问到了一个关于二叉树的问题,本身对算法并不擅长,结果想了半天没想出解决方法,经过面试官提点,才恍然大悟,回来后立马把实现写了出来,详见如下...

    RedSheep

扫码关注云+社区

领取腾讯云代金券