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,这个阈值的配置可以是一个定值或一个百分比。如:

cpp 复制代码
 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

cpp 复制代码
kubectl get priorityclasses

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

cpp 复制代码
[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状态

cpp 复制代码
kubectl get pods
cpp 复制代码
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事件日志

cpp 复制代码
kubectl describe pods web-nginx-xxx

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

cpp 复制代码
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的驱逐参数,如果没有配置,则使用默认值。检查下驱逐阈值。

cpp 复制代码
# 硬性驱逐条件
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可用内存

cpp 复制代码
kubectl describe node
cpp 复制代码
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则触发告警

相关推荐
云计算小黄同学2 小时前
【最详细】Kubernetes探针介绍、应用与最佳实践
运维·云原生·容器·kubernetes
一周困⁸天.2 小时前
K8S-特殊容器
云原生·容器·kubernetes
飞Link3 小时前
Docker基础知识
运维·docker·容器
凯子坚持 c3 小时前
Docker常见问题(多种类似命令之间的区别)
运维·docker·容器
❀͜͡傀儡师3 小时前
Docker一键部署Flatnas,比Sun-Panel更优雅
运维·docker·容器
永不停歇的蜗牛4 小时前
K8S之Ctr 和 Docker的区别
docker·kubernetes·dubbo
ascarl20104 小时前
k8s修改 Kubelet 配置文件,避免乱驱逐!!!
云原生·kubelet
初学者_xuan4 小时前
K8S-Pod驱逐
云原生·容器·kubernetes
❥ღ Komo·4 小时前
K8S Pod优先级与抢占策略详解
云原生·容器·kubernetes