K8S-Pod驱逐

一、Pod驱逐

1、为什么要有驱逐

pod.spec.containers[].resources中会存在cpu或memory的request和limit。即该pod请求的最小资源和Node结点可以给的最大资源。

当一个容器的cpu使用率超过limit时会被进行流控,而当内存超过limit时则会被oom_kill。

完全依赖于oom_kill并不是一个很好的方案,一来对于cpu要求高的容器没有作用,二来单纯将pod杀死,并不能根本上解决困局,比如pod占用node绝大部分内存,假如pod被kill后再次调度到这个node上,oom的情况还会复现。所以kubelet增加了一套驱逐机制。 eviction中要设置触发驱逐的阈值Eviction Thresholds,这个阈值的配置可以是一个定值或一个百分比。如:

复制代码
 memory.available<10%
 memory.available<1Gi

2、驱逐机制

Soft Eviction Thresholds(软驱逐机制)

当node的内存/磁盘空间达到一定的阈值后,我要观察一段时间,如果改善到低于阈值就不进行驱逐,若这段时间一直高于阈值就进行驱逐。

Hard Eviction Thresholds( 强制驱逐机制)

简单的多,一旦达到阈值,立刻把pod从本地kill。

3、Pod eviction(Pod 驱逐)

当资源使用情况触发了驱逐条件时,kubelet会启动一个任务去轮流停止运行中的pod,直到资源使用状况恢复到阈值以下。以硬驱逐为例,整体流程是:

  • 每隔一段时间从cadvisor中获取资源使用情况,发现触发了阈值;

  • 从运行中的pod里找到QoS策略最开放的一个,比如策略为bestEffort的一个pod(即便这个pod没有吃多少内存,大部分内存是另一个策略为burstable,但内存使用率也很高的pod),kubelet停止该pod对应的所有容器,然后将pod状态更新为Failed。如果该pod长时间没有被成功kill掉,kubelet会再找一个pod进行驱逐。

  • 检查内存用量是否恢复到阈值以下,如果没有,则重复第二步(这里就要干掉那个罪魁祸首了)。一直到内存使用情况恢复到阈值以下为止。

在 Kubernetes 中,当资源不足需要驱逐 Pod 时,系统会根据 Pod 的优先级(由 PriorityClass 的 value 决定)和 Pod 的 Quality of Service (QoS) 类别等进行决策。而调度优先级主要由 Priority 值确定。

3.1、resource 的 requests 和 limits

Requests(请求):Requests 是指容器在运行时所需的资源的最小数量。它们用于告诉 Kubernetes 调度器在选择节点时要为 Pod 预留多少资源。如果没有足够的请求资源可用,Pod 可能无法被调度到节点上。

Limits(限制):Limits 是指容器在运行时所允许使用的资源的最大数量。它们用于限制容器的资源使用,以防止容器占用过多的资源导致其他容器或节点受到影响。如果容器尝试使用超过其限制的资源量,Kubernetes 将会限制其资源使用,并可能触发容器的重新启动。

3.2、QoS 类别
  • BestEffort:没有设置 resource requests 和 limits 的 Pod。

  • Burstable:设置了 requests 或者 limits,但不完全相同。

  • Guaranteed:requests 和 limits 都设置了,并且两者值相等。

驱逐顺序:BestEffort(lowest) -> Burstable -> Guaranteed(highest)。不影响调度的优先级。

3.3、PriorityClass 和 Priority

PriorityClass(优先级类):PriorityClass 是一种用于调度和优先级管理的对象。它允许您为 Pod 分配优先级。PriorityClass 定义了一个优先级类别,其中包含一个整数值 value 表示优先级的相对值。较高的 value 值表示较高的优先级。通过将 Pod 与特定的 PriorityClass 关联,可以影响 Pod 的调度和驱逐顺序。

Priority(优先级):Priority 是一个整数值,直接应用于 Pod 对象。它表示 Pod 的绝对优先级。较高的 Priority 值表示较高的优先级。同样可以影响 Pod 的调度和驱逐顺序。

查看 PriorityClass

复制代码
kubectl get priorityclasses

查看系统组件 controller-manager 使用的 PriorityClass

复制代码
[root@k8s-master ~]# kubectl describe pod -n kube-system kube-controller-manager-k8s-master  | grep -i priority
Priority:             2000001000
Priority Class Name:  system-node-critical

当集群中没有默认的 PriorityClass,也没有手动指定 Priority,那优先级的值就为 0。优先级的值越小,驱逐顺序越靠前,调度顺序越靠后。

还有一种情况是:尽管 PriorityClass 的 value 值大,但是 BestEffort 类型的 qos class 会比Burstable 或 Guaranteed 类别更容易被驱逐。当然还会有其他因素也会影响 pod 的驱逐顺序,但是影响力不如上面两种大,例如:Pod资源使用量越接近 limits,和 pod 运行时长越短等,那么这些 Pod 会被优先考虑驱逐。

当集群内有比较重要的服务时,可以把 Qos Class 设置为 Guaranteed,也就是都指定了 requests 和 limits 并且二者值相等,会有长时间运行稳定性的优势。且 Priority 的值尽可能设置大些,会有优先占用集群资源资源的优势。

在K8s 1.6之后还引入了Taint的两个新特性,TaintNodesByCondition与TaintBasedEvictions用来改善出现异常时对Pod的调度与驱逐问题

TaintNodesByCondition

特性如下(为节点添加NoSchedule的污点)

  • Node节点会不断的检查Node的状态,并且设置对应的Condition

  • 不断地根据Condition的变更设置对应的Taint

  • 不断地根据Taint驱逐Node上的Pod

主要污点如下:

node.kubernetes.io/not-ready 节点未就绪,节点Ready为False

node.kubernetes.io/unreachable 节点不可达

node.kubernetes.io/out-of-disk 磁盘空间已满

node.kubernetes.io/network-unavailable 网络不可用

node.kubernetes.io/unschedulable 节点不可调度

node.cloudprovider.kubernetes.io/uninitialized 如果 kubelet 从 外部 云服务商启动的,该污点用来标识某个节点当前为不可用状态,当云控制器 cloud-controller-manager 初始化这个节点后,kubelet 会将此污点移除

TaintBasedEvictions

特性添加的是NoExecute的污点,例如内存与磁盘有压力时,如果Pod没有设置容忍这些污点,则会被驱逐,以保证Node不会崩溃

主要污点如下:

node.kubernetes.io/memory-pressure 内存不足

node.kubernetes.io/disk-pressure 磁盘不足

1.13版本之后TaintNodesByCondition 与 TaintBasedEvictions 都是默认开启

二、Pod驱逐实战案例

1、k8s pod内存驱逐问题解决

**背景:**突然收到 web 无法访问告警,然后发现前段应用pod状态为Evicted,证明pod是被驱逐了

排查过程:

1.1、查看pod状态:
复制代码
kubectl get pods
复制代码
kubectl get pods -A | grep 0/1
 web-nginx-865674789f-c7bv4  0/1   Evicted       0   25h   <none>  192.168.3.10  <none>
 web-nginx-865674789f-ggb27  0/1   Evicted       0   25h   <none>  192.168.3.10  <none>
 web-nginx-865674789f-fwp94  0/1   Evicted       0   25h   <none>  192.168.3.10  <none>
 web-nginx-865674789f-djj46  0/1   Evicted       0   25m   <none>  192.168.3.10  <none>
1.2、查看pod事件日志:
复制代码
kubectl describe pods web-nginx-xxx

从日志上可以看出来是内存不足导致了驱逐

复制代码
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Warning  Evicted    2m (x1 over 2m)    kubelet            The node was low on resource: [MemoryPressure].
1.3、排查节点内存监控

发现利用率在50%,没理由会导致内存不足

1.4、思考内存驱逐的原理

1.4.1 K8S通过kubelet来配置pod的驱逐参数,如果没有配置,则使用默认值。检查下驱逐阈值。

复制代码
# 硬性驱逐条件
evictionHard:
  memory.available: "200Mi"
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
  imagefs.available: "15%"
# 软性驱逐条件
evictionSoft:
  memory.available: "300Mi"
  nodefs.available: "15%"
  imagefs.available: "20%"
# 软性驱逐条件的宽限期
evictionSoftGracePeriod:
  memory.available: "1m"
  nodefs.available: "1m"
  imagefs.available: "1m"
# 驱逐Pod前的最大宽限期
evictionMaxPodGracePeriod: 60
#驱逐开始前等待资源压力状态稳定的时间
evictionPressureTransitionPeriod: "5m"
​

硬性驱逐和软性驱逐的区别:

  • 硬性驱逐是当资源达到或超过设定的硬性驱逐阈值时,Kubelet立即执行驱逐操作。硬性驱逐的特点是直接且无延迟。

  • 软性驱逐是在资源使用达到设定的软性驱逐阈值后,给Pod一个宽限期(Grace Period)。如果在宽限期结束后资源使用仍然没有降低,Kubelet才会驱逐Pod。

1.4.2 查看node可用内存

复制代码
kubectl describe node
复制代码
Allocatable:
  cpu:                15400m
  ephemeral-storage:  1043358208Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             63242364Ki  #可分配60G内存
  pods:               253

可分配内存为60G,而服务器内存为100G,

和现场同学(一线工程师)确认,问题出现前由于内存占用很高,做过一次在线扩容。

**故障复盘:**故障原因为前期内存资源不足后,虚拟机采用在线扩容内存的方式,服务器没有重启,并且K8S的kubelet服务也没有重启,获取到的内存配置仍然是60G,所以当主机内存达到60G的时候出现pod由于内存不足产生驱逐。

至于监控,node-exporter可以动态获取主机物理资源,所以过于依赖监控却忽略了检查kubelet。

优化方案:对node内存和kubelet可分配内存做对比,如果相差大于1G则触发告警

配置案例

好的,这里提供一些 Kubernetes Pod 驱逐(Eviction)参数设置的典型案例。这些参数主要配置在 Kubelet 上,用于控制节点资源压力时的驱逐行为。

核心概念:驱逐信号(Eviction Signals)与阈值(Thresholds)

Kubelet 监控多种资源信号,当这些信号达到设定的阈值时,就会触发驱逐。

驱逐信号 描述
memory.available 节点可用内存
nodefs.available 节点根磁盘(/)可用空间
nodefs.inodesFree 节点根磁盘(/)可用 inode
imagefs.available 容器运行时存储镜像和可写层的磁盘可用空间
imagefs.inodesFree 容器运行时磁盘的可用 inode
pid.available 节点可用进程 ID (PID)

案例一:基础驱逐配置

这是一个最常见的配置案例,用于防止节点因内存或磁盘耗尽而完全宕机。

kubelet 启动参数(通常在 /var/lib/kubelet/config.yaml 或 systemd 的 kubelet.service 中配置):

复制代码
# /var/lib/kubelet/config.yaml 片段
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"   # 可用内存低于500Mi时开始驱逐
  nodefs.available: "10%"     # 根磁盘可用空间低于10%时开始驱逐
  nodefs.inodesFree: "5%"     # 根磁盘inode低于5%时开始驱逐
  imagefs.available: "15%"    # 容器镜像磁盘可用空间低于15%时开始驱逐
evictionSoft:
  memory.available: "700Mi"
  nodefs.available: "15%"
evictionSoftGracePeriod:
  memory.available: "1m30s"   # 软阈值持续1分30秒后才触发驱逐
  nodefs.available: "2m"      # 软阈值持续2分钟后才触发驱逐
evictionMaxPodGracePeriod: 60 # 驱逐Pod时,允许Pod体面终止的最大宽限期(秒)
evictionPressureTransitionPeriod: 30s # 脱离压力状态后,需要持续多久才报告状态正常

对应 systemd 参数(如果使用命令行参数):

复制代码
/usr/bin/kubelet \
  --eviction-hard=memory.available<500Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<15% \
  --eviction-soft=memory.available<700Mi,nodefs.available<15% \
  --eviction-soft-grace-period=memory.available=1m30s,nodefs.available=2m \
  --eviction-max-pod-grace-period=60 \
  --eviction-pressure-transition-period=30s \
  ...其他参数

案例二:为系统守护进程预留资源

这是强烈推荐的生产环境配置。通过为系统进程和 kubelet 本身预留资源,可以防止它们因资源竞争而被饿死,从而保证节点的稳定性。

kubelet 启动参数:

复制代码
# /var/lib/kubelet/config.yaml 片段
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# 1. 设置驱逐阈值
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "10%"
# 2. 为核心系统预留资源!非常重要!
systemReserved:
  memory: "1Gi"
  cpu: "500m"
  ephemeral-storage: "5Gi"
kubeReserved:
  memory: "1Gi"
  cpu: "250m"
  ephemeral-storage: "1Gi"
# 3. 强制执行预留策略
enforceNodeAllocatable:
  - pods
  - system-reserved
  - kube-reserved
  • systemReserved: 为操作系统守护进程(如 sshd、udev)预留的资源。

  • kubeReserved: 为 Kubernetes 系统守护进程(如 kubelet、容器运行时)预留的资源。

  • enforceNodeAllocatable: 指定 kubelet 需要强制执行哪些预留策略。pods 必须包含在内。

工作原理: 节点的总资源 = Allocatable(可分配给 Pod 的资源) + SystemReserved + KubeReserved 驱逐阈值是基于 Allocatable 来计算的吗?不是,驱逐阈值是基于节点的总容量(Capacity) 来监控的。但预留机制确保了系统核心组件有资源可用,即使 Pod 已经耗尽了 Allocatable 部分。


案例三:仅使用软驱逐阈值实现平滑处理

软阈值搭配宽限期,可以让 Pod 有机会在被迫驱逐前完成一些清理工作(如通知中心、完成当前请求),适用于对中断敏感的应用。

kubelet 启动参数:

复制代码
evictionSoft:
  memory.available: "5%"
  nodefs.available: "10%"
  imagefs.available: "10%"
evictionSoftGracePeriod:
  memory.available: "2m"   # 内存压力持续2分钟后才驱逐
  nodefs.available: "3m"  # 磁盘压力持续3分钟后才驱逐
  imagefs.available: "3m"
evictionMaxPodGracePeriod: 90 # 给予Pod更长的体面终止时间

案例四:高级镜像垃圾收集策略

驱逐经常与镜像磁盘空间不足有关,配置积极的镜像垃圾收集可以预防驱逐。

kubelet 启动参数:

复制代码
# 镜像垃圾收集策略
imageGCHighThresholdPercent: 85  # 当磁盘使用率达到85%时,触发镜像GC
imageGCLowThresholdPercent:  80  # GC会一直清理,直到使用率降到80%
imageMinimumGCAge: 2m0s          # 镜像必须至少存在2分钟才会被GC清理,避免误删正在使用的中间层

最佳实践与总结

  1. 始终设置 evictionHard:这是防止节点完全宕机的安全网。

  2. 必须配置 systemReservedkubeReserved:这是生产环境的黄金准则,能极大提高节点稳定性。预留值大小需根据节点规格和实际系统开销调整。

  3. 理解软阈值的用途:用于需要"优雅驱逐"的场景,给应用和运维人员预留反应时间。

  4. 监控驱逐事件 :使用 kubectl get events -w --field-selector reason=Evicted 或监控平台来关注集群中的驱逐事件,它们是指标应用资源规划不合理或节点压力过大的重要信号。

  5. 合理设置 Pod 的 requestslimits :这是防止 Pod 被驱逐的第一道防线。Kubelet 在决定驱逐哪个 Pod 时,会优先驱逐那些实际使用量远超其 requestsBurstable Pod。

  6. 使用 Pod 优先级(PriorityClass) :对于关键应用,可以创建高优先级的 PriorityClass,并配置到 Pod 上。这样即使节点压力巨大,低优先级的 Pod 也会在高优先级的 Pod 之前被驱逐。

通过组合使用这些参数,你可以精细地控制 Kubernetes 节点的驱逐行为,在保证应用可用性的同时,最大限度地维护节点本身的健康与稳定。

相关推荐
❥ღ Komo·2 小时前
K8S Pod优先级与抢占策略详解
云原生·容器·kubernetes
❀͜͡傀儡师2 小时前
docker一键部署kafka
docker·容器·kafka
IT运维爱好者2 小时前
【国产】华为欧拉操作系统openEuler-LTS-22.03安装Docker-Compose保姆级教程
docker·容器·openeuler·欧拉
晨欣2 小时前
Dify Custom Tool 调用超时问题排查与解决方案(claude-4.5-opus-high)
docker·容器·dify
betazhou2 小时前
docker容器单机创建3个节点的MySQLMGR集群
运维·mysql·docker·容器·集群·mgr
Live in Shanxi.3 小时前
Prometheus监控服务器及K8s集群资源
服务器·kubernetes·prometheus
weixin_46683 小时前
K8S-Deployment
云原生·容器·kubernetes
总有刁民想爱朕ha3 小时前
银河麒麟v10服务器版Docker部署MySQL 8教程
mysql·docker·容器·银河麒麟v10
没有bug.的程序员3 小时前
云原生 + JIT:冷启动与预热优化
java·jvm·云原生·日志·gc·jit