每天都有成千上万的 Pod 从节点中被驱逐。它们无家可归、困惑不已,不得不放弃之前的生活方式。其中一些甚至变成无节点状态。
在 Kubernetes 中 Pod 被驱逐意味着什么?我们常常能看到 Pod 因为资源不足被终止。但为什么会发生这种情况呢?
驱逐(Eviction) 是指终止已分配到某个节点的 Pod。Kubernetes 中最常见的情况是抢占,是指为了将新的 Pod 调度到资源有限的节点上,需要终止另一个 Pod 以释放资源。
此外,Kubernetes 会不断检查资源在必要时驱逐 Pod,这个过程称为节点压力驱逐。
Pod 被驱逐的原因:抢占和节点压力
Kubernetes 中 Pod 驱逐现象发生的最主要原因如下:
- 抢占驱逐
- 节点压力驱逐
抢占驱逐
抢占(Preemption) 是指如果需要调度一个新的 Pod,但没有合适的节点有足够的资源, kube-scheduler
就会检查是否可以通过驱逐(终止)一些优先级较低的 Pod,来确保新的 Pod 可以调度到该节点。
首先,我们来理解下 Kubernetes 调度的工作原理。
调度
Kubernetes 调度是指 Pod 被分配到节点的过程。
默认情况下,一个名为 kube-scheduler
的 Kubernetes 实体负责调度,它在控制面中运行。Pod 在找到合适的节点前会一直处于 Pending 状态。
将 Pod 分配到节点的过程包含过滤和评分两个步骤:
过滤
在过滤步骤中,kube-scheduler
将选出所有适合放置当前 Pod 的节点。该步骤会考虑污点、容忍度等特性。过滤完成后,将会列出适合该 Pod 的节点列表。
评分
在评分步骤中,kube-scheduler
将使用上一步筛选出的候选节点列表,并为每个节点打分。这样就可以按照合适程度对候选节点进行排序。如果存在两个节点得分相同,kube-scheduler
会随机排序。
但是,如果没有合适的节点可以运行 Pod,该怎么办?这时,Kubernetes 就会开始抢占,试图驱逐优先级较低的 Pod,并将此节点分配给新 Pod。
优先级驱逐
但如何防止特定 Pod 在抢占过程中被驱逐?可能有一些重要的 Pod 不应被终止。
这正是 Kubernetes 提供优先级类的原因。
Pod 优先级是一种 Kubernetes 对象,允许我们将优先级值映射到特定的 Pod。系统会将更重视优先级较高的 Pod,该 Pod 被驱逐的可能性更低。
您可以使用以下命令查询当前 Pod 的优先级:
kubectl get priorityclasses
kubectl get pc
NAME VALUE GLOBAL-DEFAULT AGE
system-cluster-critical 2000000000 false 2d
system-node-critical 2000001000 false 2dCode language: Shell Session (shell)
Pod 优先级示例
这里我们用 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
Code language: JavaScript (javascript)
且存在两个优先级类: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"Code language: Shell Session (shell)
- 蓝莓被设置为 trueberry 优先级类(值为 1,000,000)
- 树莓和草莓则被设置为 falseberry 优先级类(值为 5,000)
这意味着抢占发生时,树莓和草莓更可能被驱逐,给更高优先级的 Pod 腾出空间。
可在 Pod 定义中添加以下内容来分配优先级:
priorityClassName: trueberryCode language: Shell Session (shell)
现在我们尝试添加三个新水果,但有一个小的改动。新添加的水果都设置了 trueberry
,具有更高优先级。
由于这三个新水果对内存或 CPU 的需求超出了节点的能力,kubelet
会驱逐所有优先级低于新水果的 Pod。由于蓝莓优先级更高,它仍保持运行状态。
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 2sCode language: Shell Session (shell)
这是最终的 Pod 运行结果:
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 3sCode language: Shell Session (shell)
对于 Berry Club 来说,这真是艰难的时期...
节点压力驱逐
除了抢占外,Kubernetes 还会不断检查节点资源,如磁盘压力、CPU 或内存溢出(OOM)。
如果节点上的某种资源(如 CPU 或内存)的使用达到一定阈值,kubelet
就会开始驱逐 Pod 以释放资源。系统也会根据服务质量(QoS)确定驱逐顺序。
服务质量类驱逐
在 Kubernetes 中,如果 Pod 被赋予了以下三种 QoS 之一,这意味着它们在资源短缺情况下被驱逐的可能性等级,以下按照被驱逐的可能性从低到高排序:
- 保证型(Guaranteed)
- 突发型(Burstable)
- 尽力就好型(BestEffort)
如何将这些 QoS 分配给 Pod 取决于 CPU 和内存的限制和请求值。
- 限制值:容器可以使用的资源的最大量。
- 请求值:容器运行所需的最小资源量。
保证型
如果满足以下条件,Pod 将被赋予保证型 QoS:
- Pod 中的所有容器都设置了 CPU 和内存的限制和请求值。
- Pod 中所有容器的 CPU 限制和 CPU 请求的值相同。
- Pod 中所有容器的内存限制和内存请求的值相同。
在正常情况下,保证型 Pod 不会被驱逐。
突发型
如果满足以下条件,Pod 将被赋予突发型 QoS:
- 不符合保证型 QoS。
- Pod 中的某个容器设置了限制值或请求值。
突发型 Pod 可能会被驱逐,但相较于比尽力就好型,其被驱逐的可能性更低。
尽力就好型
如果满足以下条件,Pod 将被赋予尽力就好型 QoS:
- Pod 中没有为任何容器设置限制值和请求值。
在节点压力发生时,尽力就好型 Pod 最有可能被驱逐。
重要提示:限制值和请求值中可能还有其他可用资源,如 ephemeral-storage,但它们不用于 QoS 类计算。
如前所述,QoS 类将被考虑用于节点压力驱逐。以下是内部发生的过程:
kubelet 按以下顺序对要驱逐的 Pod 进行排名:
尽力就好型
Pod 或使用量超过请求的突发型
Pod- 使用量低于请求的
突发型
Pod 或保证型
Pod
Kubernetes 将尝试先驱逐第 1 组中的 Pod,然后再驱逐第 2 组。
可以关注以下要点:
- 如果您在容器中设置的请求值很低,容器的 Pod 很可能被分配到第 1 组,这意味着它们被驱逐的可能性更大。
- 您无法确定哪个特定的 Pod 将被驱逐,只知道 Kubernetes 将尝试先驱逐符合第 1 组条件的 Pod,然后再驱逐第 2 组中的。
保证型
Pod 通常不会被驱逐:kubelet
不会为了调度其他 Pod 而驱逐保证型 Pod。但如果某些系统服务需要更多资源,kubelet
将在必要时终止保证型
Pod,并始终给予最低优先级。
其他类型的驱逐
本节重点关注抢占和节点压力驱逐,但 Pod 也可能会以其他方式被驱逐。例如:
API 驱动的驱逐
您可以使用 Kubernetes 的驱逐 API 请求对某个节点上的 Pod 进行即时驱逐。
基于污点的驱逐
借助 Kubernetes 污点和容忍,您可以指定如何将 Pod 分配到节点。但是,如果您对一个现有节点应用了 NoExecute
污点,所有未设置容忍该污点 Pod 都将立即被驱逐。
节点排空
有时节点可能会无法使用,或者您不想再继续使用这些节点。kubectl cordon
命令可以防止新的 Pod 被调度到该节点,但您也可以一次性清空该节点上的所有当前 Pod。执行 kubectl drain nodename
后,该节点上的所有 Pod 都将被驱逐,并遵守其优雅终止期。
使用 Prometheus 监控Kubernetes Pod 驱逐监控
您可以在云解决方案中使用 Prometheus 轻松监控 Pod 驱逐情况:
kube_pod_status_reason{reason="Evicted"} > 0Code language: JavaScript (javascript)
执行这一命令后,将显示集群中所有被驱逐的 Pod。您还可以将其与 kube_pod_status_phase{phase="Failed"}
配对,可以在 Pod 因失败而被驱逐时收到告警。
总结
驱逐只是 Kubernetes 的一个功能,可让您控制有限的资源,即 Pod 将使用的节点资源。
在抢占期间,Kubernetes 会尝试通过驱逐优先级较低的 Pod 来释放资源,以调度新的 Pod。您可以使用优先级类驱逐,控制哪些 Pod 在抢占后更有可能继续运行,因为设置特定优先级的 Pod 被驱逐的可能性更小。
在执行过程中,Kubernetes 也将检查节点压力,并在需要时驱逐 Pod。您也可以在节点压力情况下,使用 QoS 类驱逐,控制哪些 Pod 更可能被驱逐。
内存和 CPU 是节点中重要的资源,您需要给 Pod、容器和节点配置恰当的使用量。如果您相应地管理这些资源,不仅可以节省成本,还可以确保无论发生什么情况,重要的流程都能保持运行。
原文链接: