K8s 中的 Pod 驱逐
- 1.Pod 被驱逐的原因:抢占和节点压力
- 2.抢占式驱逐
- 2.1 Pod 调度
- 2.1.1 过滤
- 2.1.2 计分
- 2.2 Pod 优先级
- 2.3 优先级示例
- 3.节点压力驱逐
- 3.1 服务质量等级
- 3.1.1 Guaranteed
- 3.1.2 Burstable
- 3.1.3 BestEffort
- 4.其他类型的驱逐
- 4.1 API 发起的驱逐(API-initiated eviction)
- 4.2 基于污点的驱逐(Taint-based eviction)
- 4.3 节点耗尽(Node drain)
- 5.Prometheus 中的 Pod 驱逐监控
- 6.结论
Kubernetes Pod 被驱逐是什么意思?它们被终止,通常是因为没有足够的资源。但为什么会发生这种情况呢?
驱逐 是指分配给节点的 Pod 被要求终止的过程。Kubernetes 中最常见的情况之一是 抢占(Preemption
),即为了在资源有限的节点上调度新 Pod,需要终止另一个 Pod,将资源留给第一个 Pod。
此外,Kubernetes 还会不断检查资源,并在需要时驱逐 Pod,这一过程被称为 节点压力驱逐(Node-pressure eviction
)。
每天,数以千计的 Pod 被驱逐出它们的家园。它们困顿而迷茫,不得不放弃以前的生活方式。他们中的一些人甚至变成了无节点人。当前社会对 CPU 和内存提出了更高的要求,这也是问题的一部分。
在本文中,您将发现:
- Pod 被驱逐的原因:抢占和节点压力
- 抢占式驱逐
- Pod 调度
- Pod 优先级
- 优先级示例
- 节点压力驱逐
- 服务质量等级
- 其他类型的驱逐
- API 发起的驱逐
- 基于污点的驱逐
- Node 级别排空(drain)
- Prometheus 中的 Pod 驱逐监控
1.Pod 被驱逐的原因:抢占和节点压力
在 Kubernetes 中发生 Pod 驱逐的原因有多种。最重要的是:
- 抢占(
Preemption
) - 节点压力驱逐(
Node-pressure eviction
)
2.抢占式驱逐
抢占是这样一个过程:如果一个新的 Pod 需要被调度,但是没有任何合适的节点拥有足够的资源,那么 kube-scheduler
将通过驱逐终止一些优先级较低的 Pod 来检查新的 Pod 是否可以成为那个节点的一部分。
我们先了解一下 Kubernetes 调度是如何工作的。
2.1 Pod 调度
Kubernetes 调度是将 Pod 分配到节点的过程。默认情况下,有一个名为 kube-scheduler
的 Kubernetes 实体负责调度,它将在控制平面中运行。Pod 在找到匹配的节点之前将处于待处理状态。将 Pod 分配到节点的过程如下:
- 过滤
- 计分
2.1.1 过滤
在筛选步骤中,kube-scheduler
将选择当前 Pod 可能放置的所有节点。这里会考虑到污点和容忍度等特征。完成后,它将列出适合该 Pod 的节点列表。
2.1.2 计分
在评分步骤中,kube-scheduler
将获取上一步的结果列表并为每个节点分配一个分数。这样,候选节点从最适合到最不适合排序。如果两个节点的分数相同,kube-scheduler
会随机对它们进行排序。
但是,如果没有适合 Pod 运行的节点怎么办?在这种情况下,Kubernetes 将启动抢占过程,尝试驱逐优先级较低的 Pod,以便分配新的 Pod。
2.2 Pod 优先级
在抢占过程中如何防止特定 Pod 被驱逐?很有可能,一个特定的 Pod 对你来说很重要,永远不应该被终止。这就是 Kubernetes 具有 Priority Classes 的原因。
优先级类是一个 Kubernetes 对象,它允许我们将数字优先级值映射到特定的 Pod。那些具有更高价值的 Pod 被归类为更重要并且不太可能被驱逐。
您可以使用以下方式查询当前的优先级:
$ kubectl get priorityclasses
$ kubectl get pc
NAME VALUE GLOBAL-DEFAULT AGE
system-cluster-critical 2000000000 false 2d
system-node-critical 2000001000 false 2d
2.3 优先级示例
让我们使用 Lovenstein 先生的 Berry Club 漫画做一个实际的例子。
假设,有三个 Pod 分别代表 蓝莓、树莓 和 草莓:
NAME READY STATUS RESTARTS AGE
blueberry 1/1 Running 0 4h41m
raspberry 1/1 Running 0 58m
strawberry 1/1 Running 0 5h22m
并且有两个优先等级:trueberry
和 falseberry
。第一个将具有更高的值,表示更高的优先级。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: trueberry
value: 1000000
globalDefault: false
description: "This fruit is a true berry"
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: falseberry
value: 5000
globalDefault: false
description: "This fruit is a false berry"
- 蓝莓将具有
trueberry
优先级(值 = 1000000) - 树莓和草莓都将具有
falseberry
优先级(值 = 5000)
这意味着在抢占的情况下,树莓和草莓更有可能被驱逐,为更高优先级的 Pod 腾出空间。
然后通过将此添加到 Pod 定义来将优先级类分配给 Pod:
priorityClassName: trueberry
现在让我们尝试再添加三种水果,但要稍加改动。所有新水果都将包含更高的优先级,称为 trueberry
。
由于三个新水果有节点无法满足的内存或 CPU 要求,kubelet
驱逐所有优先级低于新水果的 Pod。Blueberry 保持运行,因为它具有更高的优先级。
NAME READY STATUS RESTARTS AGE
banana 0/1 ContainerCreating 0 2s
blueberry 1/1 Running 0 4h42m
raspberry 0/1 Terminating 0 59m
strawberry 0/1 Terminating 0 5h23m
tomato 0/1 ContainerCreating 0 2s
watermelon 0/1 ContainerCreating 0 2s
这是最终结果:
NAME READY STATUS RESTARTS AGE
banana 1/1 Running 0 3s
blueberry 1/1 Running 0 4h43m
tomato 1/1 Running 0 3s
watermelon 1/1 Running 0 3s
3.节点压力驱逐
除了抢占,Kubernetes 还会不断检查节点资源,如磁盘压力、CPU 或内存不足 (OOM)。
如果节点中的资源(如 CPU 或内存)消耗达到某个阈值,kubelet
将开始驱逐 Pod 以释放资源。将考虑 服务质量 (QoS) 来确定驱逐顺序。
3.1 服务质量等级
在 Kubernetes 中,Pod 被赋予三个 QoS 类别中的一个,这三个类别定义了 Pod 在资源不足的情况下被驱逐的可能性,从可能性较低到可能性较高不等:
- Guaranteed(保证)
- Burstable(稳定)
- BestEffort(尽力)
这些 QoS 等级是如何分配给 Pod 的?这是基于对 CPU和内存的限制和请求。提醒一句:
- Limits:容器可以使用的最大资源量。
- Requests:容器运行所需的最小资源量。
3.1.1 Guaranteed
如果满足以下条件,Pod 将分配有保证的 QoS 等级:
- Pod 中的所有容器都为 CPU 和内存设置了限制和请求。
- Pod 中的所有容器都具有相同的
CPU Limit
和CPU Request
值。 - Pod 中所有容器都具有相同的
memory Limit
和memory Request
值
Guaranteed Pod 在正常情况下不会被驱逐。
3.1.2 Burstable
如果满足以下条件,Pod 将分配有可突发的 QoS 等级:
- 它没有 Guaranteed QoS 等级。
- 已为 Pod 中的容器设置了 Limits 或 Requests 。
Burstable Pod 可以被驱逐,但比下一个类别更不可能。
3.1.3 BestEffort
如果出现以下情况,Pod 将分配有 BestEffort 的 QoS 等级:
- Pod 中的任何容器都没有设置限制和请求。
在发生节点压力过程的情况下,BestEffort Pod 被驱逐的可能性最大。
重要提示:Limits 和 Requests 中可能还有其他资源,如
ephemeral-storage
,但它们不用于 QoS Class 计算。
如前所述,QoS 类将被考虑用于节点压力驱逐。这是内部发生的过程。kubelet 按照以下顺序对要驱逐的 Pod 进行排序:
- 1️⃣ BestEffort 或 Burstable 使用量超过 requests 的 Pod
- 2️⃣ Burstable 使用量低于 requests 的 Pod 或者 Guaranteed Pod
Kubernetes 将优先从第 1 组中驱逐 Pod,然后才会尝试在第 2 组驱逐。
上面的一些要点:
- 如果您在容器中添加非常低的请求,它们的 Pod 可能会被分配到组 1,这意味着它更有可能被驱逐。
- 你无法判断哪个特定的 Pod 将被驱逐,只是 Kubernetes 会尝试在第 2 组之前驱逐第 1 组中的 Pod。
- Guaranteed Pod 通常不会被驱逐:
kubelet
不会为了安排其他 Pod 而驱逐它们。但是如果某些系统服务需要更多资源,kubelet
将在必要时终止Guaranteed Pod。
4.其他类型的驱逐
本文重点介绍抢占和节点压力驱逐,但 Pod 也可以通过其他方式驱逐。例子包括:
4.1 API 发起的驱逐(API-initiated eviction)
可以使用 Kubernetes Eviction API 请求按需驱逐一个节点中的 Pod。
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#create-eviction-pod-v1-core
请记住,Eviction API 不同于 Delete Pod,前者是 CREATE 动作,请求会创建 pods/eviction 子资源,后者是 DELETE 动作请求。
4.2 基于污点的驱逐(Taint-based eviction)
利用 Kubernetes Taints and Tolerations,你可以指导如何将 Pod 分配到节点。但是,如果在现有节点上应用 NoExecute taint,所有不能容忍 NoExecute taint 的 Pod 都会被立即驱逐。
Taints 是节点上的特殊标签,它们可以阻止 Pods 被调度到该节点。Tolerations 是 Pods 上的特殊标签,它们允许 Pods 容忍并调度到具有相应 Taints 的节点上。
4.3 节点耗尽(Node drain)
有时节点变得不可用或者不想再在这些节点上工作时。kubectl cordon
命令会阻止在其上安排新的 Pod,运行 kubectl drain nodename
也可以一次完全清空所有当前节点上 Pod。节点中的所有 Pod 都将被驱逐,遵守其正常终止限期。
5.Prometheus 中的 Pod 驱逐监控
可以使用 Prometheus 通过执行以下操作轻松监控 Pod 驱逐:
kube_pod_status_reason{reason="Evicted"} > 0
这将显示集群中所有被驱逐的 Pod。您还可以将其与 kube_pod_status_phase{phase="Failed"}
搭配使用,以便对 Pod 出现故障后被驱逐的 Pod 发出警报。
6.结论
在 抢占(preemption
)期间,Kubernetes 将尝试通过驱逐优先级较低的 Pod 释放资源,来安排新的 Pod 。使用优先级类别(Priority Classes
),可以控制哪些 Pod 在抢占时更有可能继续运行,因为它们被驱逐的可能性较小。
在 执行 期间,Kubernetes 将检查节点压力并在需要时驱逐 Pod。使用 QoS Classes,可以控制在节点压力的情况下哪些 Pod 更有可能被驱逐。
内存和 CPU 是节点中的重要资源,您需要配置 Pod、容器和节点以使用适量的内存和 CPU。如果您合理地管理这些资源,不仅可以降低成本,还可以确保重要流程无论如何都能继续运行。