Kubernetes驱逐机制
什么是节点驱逐?
节点压力驱逐是 kubelet 主动终止 Pod 以回收节点上资源的过程。
kubelet 监控集群节点的 CPU、内存、磁盘空间和文件系统的 inode 等资源。 当这些资源中的一个或者多个达到特定的消耗水平, kubelet 可以主动地使节点上一个或者多个 Pod 失效,以回收资源防止饥饿。
在节点压力驱逐期间,kubelet 将所选 Pod 的 PodPhase 设置为 Failed。这将终止 Pod。
kubelet 并不理会你配置的 PodDisruptionBudget 或者是 Pod 的 terminationGracePeriodSeconds。 如果你使用了软驱逐条件,kubelet 会考虑你所配置的 eviction-max-pod-grace-period。 如果你使用了硬驱逐条件,它使用 0s 宽限期来终止 Pod。
如果 Pod 是由替换失败 Pod 的工作负载资源 (例如 StatefulSet 或者 Deployment)管理, 则控制平面或 kube-controller-manager 会创建新的 Pod 来代替被驱逐的 Pod。
说明:
kubelet 在终止最终用户 Pod 之前会尝试回收节点级资源。 例如,它会在磁盘资源不足时删除未使用的容器镜像。
两种驱逐机制
目前kubernetes中存在两种eviction机制,分别由kube-controller-manager和kubelet实现。
kube-controller-manager驱逐机制
kube-controller-manager主要由多个控制器构成,而eviction的功能主要由node controller这个控制器实现。该Eviction会周期性检查所有节点状态,当节点处于NotReady状态超过一段时间后,驱逐该节点上所有pod。
kube-controller-manager提供了以下启动参数控制驱逐:
-
pod-eviction-timeout:即当节点宕机该时间间隔后,开始驱逐机制,驱逐宕机节点上的Pod,默认为5min。
-
node-eviction-rate:驱逐速率,即驱逐Node的速率,由令牌桶流控算法实现,默认为0.1,即每秒驱逐0.1个节点,注意这里不是驱赶Pod的速率,而是驱逐节点的速率。相当于每隔10s,清空一个节点。
-
secondary-node-eviction-rate:二级驱逐速率,当集群中宕机节点过多时,相应的驱逐速率也降低,默认为0.01。
-
unhealthy-zone-threshold:不健康zone阈值,会影响什么时候开启二级驱逐速率,默认为0.55,即当该zone中节点宕机数目超过55%,而认为该zone不健康。
-
large-cluster-size-threshold:大集群阈值,当该zone的节点多余该阈值时,则认为该zone是一个大集群。大集群节点宕机数目超过55%时,则将驱赶速率降为0.01,假如是小集群,则将速率直接降为0。
kubelet驱逐机制
如果节点处于资源压力,那么kubelet就会执行驱逐策略。驱逐会考虑Pod的优先级,资源使用和资源申请。当优先级相同时,资源使用/资源申请最大的Pod会被首先驱逐。
kube-controller-manager的驱逐机制是粗粒度的,即驱赶一个节点上的所有pod,而kubelet则是细粒度的,它驱赶的是节点上的某些Pod,驱赶哪些Pod与Pod的Qos机制有关。该Eviction会周期性检查本节点内存、磁盘等资源,当资源不足时,按照优先级驱逐部分pod。
驱逐阈值分为软驱逐阈值(Soft Eviction Thresholds)和强制驱逐(Hard Eviction Thresholds)两种机制,如下:
-
软驱逐阈值:当node的内存/磁盘空间达到一定的阈值后,kubelet不会马上回收资源,如果改善到低于阈值就不进行驱逐,若这段时间一直高于阈值就进行驱逐。
-
强制驱逐:强制驱逐机制则简单的多,一旦达到阈值,直接把pod从本地驱逐。
kubelet提供了以下参数控制eviction:
-
eviction-soft:软驱逐阈值设置,具有一系列阈值,比如memory.available<1.5Gi时,它不会立即执行pod eviction,而会等待eviction-soft-grace-period时间,假如该时间过后,依然还是达到了eviction-soft,则触发一次pod eviction。
-
eviction-soft-grace-period:默认为90秒,当eviction-soft时,终止Pod的grace的时间,即软驱逐宽限期,软驱逐信号与驱逐处理之间的时间差。
-
eviction-max-pod-grace-period:最大驱逐pod宽限期,停止信号与kill之间的时间差。
-
eviction-pressure-transition-period:默认为5分钟,脱离pressure condition的时间,超过阈值时,节点会被设置为memory pressure或者disk pressure,然后开启pod eviction。
-
eviction-minimum-reclaim:表示每一次eviction必须至少回收多少资源。
-
eviction-hard:强制驱逐设置,也具有一系列的阈值,比如memory.available<1Gi,即当节点可用内存低于1Gi时,会立即触发一次pod eviction。
本节我们重点介绍kubelet的驱逐机制。
驱逐信号
驱逐信号是特定资源在特定时间点的当前状态。 kubelet 使用驱逐信号,通过将信号与驱逐条件进行比较来做出驱逐决定, 驱逐条件是节点上应该可用资源的最小量。
kubelet 使用以下驱逐信号:
驱逐信号 | Node Condition | 描述 |
---|---|---|
memory.available | MemoryPressue | memory.available := node.status.capacity[memory] - node.stats.memory.workingSet |
nodefs.available | DiskPressure | nodefs.available := node.stats.fs.available |
nodefs.inodesFree | DiskPressure | nodefs.inodesFree := node.stats.fs.inodesFree |
imagefs.available | DiskPressure | imagefs.available := node.stats.runtime.imagefs.available |
imagefs.inodesFree | DiskPressure | imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree |
pid.available | PIDPressure | pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc |
在上表中,描述列显示了 kubelet 如何获取信号的值。每个信号支持百分比值或者是字面值。 kubelet 计算相对于与信号有关的总量的百分比值。
kubelet 支持以下文件系统分区:
-
nodefs:节点的主要文件系统,用于本地磁盘卷、emptyDir、日志存储等。 例如,nodefs 包含 /var/lib/kubelet/。
-
imagefs:可选文件系统,供容器运行时存储容器镜像和容器可写层。
kubelet 会自动发现这些文件系统并忽略其他文件系统。kubelet 不支持其他配置。
驱逐条件
你可以为 kubelet 指定自定义驱逐条件,以便在作出驱逐决定时使用。
驱逐条件的形式为 [eviction-signal][operator][quantity],其中:
-
eviction-signal 是要使用的驱逐信号。
-
operator 是你想要的关系运算符, 比如 <(小于)。
-
quantity 是驱逐条件数量,例如 1Gi。 quantity 的值必须与 Kubernetes 使用的数量表示相匹配。 你可以使用文字值或百分比(%)。
例如,如果一个节点的总内存为 10Gi 并且你希望在可用内存低于 1Gi 时触发驱逐, 则可以将驱逐条件定义为 memory.available<10% 或 memory.available< 1G。 你不能同时使用二者。
默认驱逐条件
下面是 kubelet 默认的关于节点存储的驱逐触发条件:
nodefs.available<10%(容器 volume 使用的文件系统的可用空间,包括文件系统剩余大小和 inode 数量)
imagefs.available<15%(容器镜像使用的文件系统的可用空间,包括文件系统剩余大小和 inode 数量)
当 imagefs 使用量达到阈值时,kubelet 会尝试删除不使用的镜像来清理磁盘空间。
// DefaultEvictionHard includes default options for hard eviction.
var DefaultEvictionHard = map[string]string{
"memory.available": "100Mi",
"nodefs.available": "10%",
"nodefs.inodesFree": "5%",
"imagefs.available": "15%",
}
驱逐类型
软驱逐
软驱逐条件
软驱逐条件将驱逐条件与管理员所必须指定的宽限期配对。 在超过宽限期之前,kubelet 不会驱逐 Pod。 如果没有指定的宽限期,kubelet 会在启动时返回错误。
你可以既指定软驱逐条件宽限期,又指定 Pod 终止宽限期的上限,给 kubelet 在驱逐期间使用。 如果你指定了宽限期的上限并且 Pod 满足软驱逐阈条件,则 kubelet 将使用两个宽限期中的较小者。 如果你没有指定宽限期上限,kubelet 会立即杀死被驱逐的 Pod,不允许其体面终止。
你可以使用以下标志来配置软驱逐条件:
-
eviction-soft
:一组驱逐条件,如 memory.available<1.5Gi, 如果驱逐条件持续时长超过指定的宽限期,可以触发 Pod 驱逐。 -
eviction-soft-grace-period
:一组驱逐宽限期, 如 memory.available=1m30s,定义软驱逐条件在触发 Pod 驱逐之前必须保持多长时间。 -
eviction-max-pod-grace-period
:在满足软驱逐条件而终止 Pod 时使用的最大允许宽限期(以秒为单位)。
硬驱逐
硬驱逐条件
硬驱逐条件没有宽限期。当达到硬驱逐条件时, kubelet 会立即杀死 pod,而不会正常终止以回收紧缺的资源。
你可以使用 eviction-hard 标志来配置一组硬驱逐条件, 例如 memory.available<1Gi。
kubelet 具有以下默认硬驱逐条件:
- memory.available<100Mi
- nodefs.available<10%
- imagefs.available<15%
- nodefs.inodesFree<5%(Linux 节点)
驱逐过程
驱逐监测间隔
kubelet 根据其配置的 housekeeping-interval(默认为 10s)评估驱逐条件。
回收节点资源
kubelet 在驱逐最终用户 Pod 之前会先尝试回收节点级资源。
当报告 DiskPressure 节点状况时,kubelet 会根据节点上的文件系统回收节点级资源。
有 imagefs
如果节点有一个专用的 imagefs 文件系统供容器运行时使用,kubelet 会执行以下操作:
-
如果 nodefs 文件系统满足驱逐条件,kubelet 垃圾收集死亡 Pod 和容器。
-
如果 imagefs 文件系统满足驱逐条件,kubelet 将删除所有未使用的镜像。
没有 imagefs
如果节点只有一个满足驱逐条件的 nodefs 文件系统, kubelet 按以下顺序释放磁盘空间:
-
对死亡的 Pod 和容器进行垃圾收集
-
删除未使用的镜像
垃圾回收参数
除了驱逐之外,Kubelet 还支持一系列的容器和镜像垃圾回收选项,它们未来将会被驱逐替代:
垃圾回收参数 | 驱逐参数 | 解释 |
---|---|---|
--image-gc-high-threshold | --eviction-hard 或 --eviction-soft | 现存的驱逐回收信号可以触发镜像垃圾回收 |
--image-gc-low-threshold | --eviction-minimum-reclaim | 驱逐回收实现相同行为 |
--minimum-image-ttl-duration | 由于驱逐不包括TTL配置,所以它还会继续支持 | |
--maximum-dead-containers | 一旦旧日志存储在容器上下文之外,就会被弃用 | |
--maximum-dead-containers-per-container | 一旦旧日志存储在容器上下文之外,就会被弃用 | |
--minimum-container-ttl-duration | 一旦旧日志存储在容器上下文之外,就会被弃用 | |
--low-diskspace-threshold-mb | --eviction-hard or eviction-soft | 驱逐回收将磁盘阈值泛化到其他资源 |
--outofdisk-transition-frequency | --eviction-pressure-transition-period | 驱逐回收将磁盘压力转换到其他资源 |
Pod驱逐顺序
如果 kubelet 回收节点级资源的尝试没有使驱逐信号低于条件, 则 kubelet 开始驱逐最终用户 Pod。
kubelet 使用以下参数来确定 Pod 驱逐顺序:
-
Pod 的资源使用是否超过其请求
-
Pod 相对于请求的资源使用情况
因此,kubelet 按以下顺序排列和驱逐 Pod:
-
首先考虑资源使用量超过其请求的 BestEffort 或 Burstable Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。
-
资源使用量少于请求量的 Guaranteed Pod 和 Burstable Pod 根据其优先级被最后驱逐。
最小驱逐回收
在某些情况下,驱逐 Pod 只会回收少量的紧俏资源。 这可能导致 kubelet 反复达到配置的驱逐条件并触发多次驱逐。
你可以使用 --eviction-minimum-reclaim 标志或 kubelet 配置文件 为每个资源配置最小回收量。 当 kubelet 注意到某个资源耗尽时,它会继续回收该资源,直到回收到你所指定的数量为止。
例如,以下配置设置最小回收量:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "500Mi"
nodefs.available: "1Gi"
imagefs.available: "100Gi"
evictionMinimumReclaim:
memory.available: "0Mi" # 没有最少回收资源限制
nodefs.available: "500Mi" # 最少回收 500Mi
imagefs.available: "2Gi" # 最少回收 2Gi
在这个例子中,如果 nodefs.available 信号满足驱逐条件, kubelet 会回收资源,直到信号达到 1Gi 的条件, 然后继续回收至少 500Mi 直到信号达到 1.5Gi。
类似地,kubelet 会回收 imagefs 资源,直到 imagefs.available 信号达到 102Gi。
对于所有资源,默认的 eviction-minimum-reclaim 为 0。
节点内存不足行为
如果节点在 kubelet 能够回收内存之前遇到内存不足(OOM)事件, 则节点依赖 oom_killer 来响应。
kubelet 根据 Pod 的服务质量(QoS)为每个容器设置一个 oom_score_adj 值。
服务质量 | oom_score_adj |
---|---|
Guaranteed | -998 |
BestEffort | 1000 |
Burstable | min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
说明:
kubelet 还将具有 system-node-critical 优先级 的 Pod 中的容器 oom_score_adj 值设为 -997。
如果 kubelet 在节点遇到 OOM 之前无法回收内存, 则 oom_killer 根据它在节点上使用的内存百分比计算 oom_score, 然后加上 oom_score_adj 得到每个容器有效的 oom_score。 然后它会杀死得分最高的容器。
这意味着低 QoS Pod 中相对于其调度请求消耗内存较多的容器,将首先被杀死。
与 Pod 驱逐不同,如果容器被 OOM 杀死, kubelet 可以根据其 RestartPolicy 重新启动它。
参考文档