技术笔记:K8s中大量Pod是Evicted状态,这是咋回事?

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 技术笔记:K8s中大量Pod是Evicted状态,这是咋回事?

线上被驱逐实例数据


最近在线上发现很多实例处于 Evicted 状态,通过 pod yaml 可以看到实例是因为节点资源不足被驱逐,但是这些实例并没有被自动清理,平台的大部分用户在操作时看到服务下面出现 Evicted 实例时会以为服务有问题或者平台有问题的错觉,影响了用户的体验。而这部分 Evicted 状态的 Pod 在底层关联的容器其实已经被销毁了,对用户的服务也不会产生什么影响,也就是说只有一个 Pod 空壳在 k8s 中保存着,但需要人为手动清理。本文会分析为什么为产生 Evicted 实例、为什么 Evicted 实例没有被自动清理以及如何进行自动清理。


kubernetes 版本:v1.17


$ kubectl get pod | grep -i Evicted


cloud-1023955-84421-49604-5-deploy-c-7748f8fd8-hjqsh 0/1 Evicted 0 73d


cloud-1023955-84421-49604-5-deploy-c-7748f8fd8-mzd8x 0/1 Evicted 0 81d


cloud-1237162-276467-199844-2-deploy-7bdc7c98b6-26r2r 0/1 Evicted 0 18d


复制


Evicted 实例状态:


status:


message: 'Pod The node had condition: 【DiskPressure】. '


phase: Failed


reason: Evicted


startTime: "2021-09-14T10:42:32Z"


复制


实例被驱逐的原因


kubelet 默认会配置节点资源不足时驱逐实例的策略,当节点资源不足时 k8s 会停止该节点上实例并在其他节点启动新实例,在某些情况下也可通过配置 --eviction-hard= 参数为空来禁用驱逐策略,在之前的生产环境中我们也确实这么做了。


节点资源不足导致实例被驱逐


k8s 中产生 Evicted 状态实例主要是因为节点资源不足实例主动被驱逐导致的,kubelet eviction_manager 模块会定期检查节点内存使用率、inode 使用率、磁盘使用率、pid 等资源,根据 kubelet 的配置当使用率达到一定阈值后会先回收可以回收的资源,若回收后资源使用率依然超过阈值则进行驱逐实例操作。


Eviction Signal


Description


memory.available


memory.available := node.status.capacity【memory】 - node.stats.memory.workingSet


nodefs.available


nodefs.available := node.stats.fs.available


nodefs.inodesFree


nodefs.inodesFree := node.stats.fs.inodesFree


imagefs.available


imagefs.available := node.stats.runtime.imagefs.available


imagefs.inodesFree


imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree


pid.available


pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc


kubelet 中 pod 的 stats 数据一部分是通过 cAdvisor 接口获取到的,一部分是通过 CRI runtimes 的接口获取到的。


memory.available:当前节点可用内存,计算方式为 cgroup memory 子系统中 memory.usage_in_bytes 中的值减去 memory.stat 中 total_inactive_file 的值;


nodefs.available:nodefs 包含 kubelet 配置中 --root-dir 指定的文件分区和 /var/lib/kubelet/ 所在的分区磁盘使用率;


nodefs.inodesFree:nodefs.available 分区的 inode 使用率;


imagefs.available:镜像所在分区磁盘使用率;


imagefs.inodesFree:镜像所在分区磁盘 inode 使用率;


pid.available:/proc/sys/kernel/pid_max 中的值为系统最大可用 pid 数;


kubelet 可以通过参数 --eviction-hard 来配置以上几个参数的阈值,该参数默认值为 imagefs.available<15%,memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,当达到阈值时会驱逐节点上的容器。


kubelet 驱逐实例时与资源处理相关的已知问题


1、kubelet 不会实时感知到节点内存数据的变化


kubelet 定期通过 cadvisor 接口采集节点内存使用数据,当节点短时间内内存使用率突增,此时 kubelet 无法感知到也不会有 MemoryPressure 相关事件,但依然会调用 OOMKiller 停止容器。可以通过为 kubelet 配置 --kernel-memcg-notification 参数启用 memcg api,当触发 memory 使用率阈值时 memcg 会主动进行通知;


memcg 主动通知的功能是 cgroup 中已有的,kubelet 会在 /sys/fs/cgroup/memory/cgroup.event_control 文件中写入 memory.available 的阈值,而阈值与 inactive_file 文件的大小有关系,kubelet 也会定期更新阈值,当 memcg 使用率达到配置的阈值后会主动通知 kubelet,kubelet 通过 epoll 机制来接收通知。


2、kubelet memory.available 不会计算 active page


kubelet 通过内存使用率驱逐实例时,内存使用率数据包含了 page cache 中 active_file 的数据,在某些场景下会因 page cache 过高导致内存使用率超过阈值会造成实例被驱逐,


由于在内存紧张时 inactive_file 会被内核首先回收,但在内存不足时,active_file 也会被内核进行回收,社区对此机制也有一些疑问,针对内核回收内存的情况比较复杂,社区暂时还未进行回应,详情可以参考 kubelet counts active page cache against memory.available (maybe it shouldn’t?)【1】。


kubelet 计算节点可用内存的方式如下:


#!/bin/bash


#!/usr/bin/env bash


# This script reproduces what the kubelet does


# to calculate memory.available relative to root cgroup.


# current memory usage


memory_capacity_in_kb=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}')


memory_capacity_in_bytes=$((memory_capacity_in_kb 1024))


memory_usage_in_bytes=$(cat /sys/fs/cgroup/memory/memory.usage_in_bytes)


memory_total_inactive_file=$(cat /sys/fs/cgroup/memory/memory.stat | grep total_inactive_file | awk '{print $2}')


memory_working_set=${memory_usage_in_bytes}


if 【 "$memory_working_set" -lt "$memory_total_inactive_file" 】;


then


memory_working_set=0


else


memory_working_set=$((memory_usage_in_bytes - memory_total_inactive_file))


fi


memory_available_in_bytes=$((memory_capacity_in_bytes - memory_working_set))


memory_available_in_kb=$((memory_available_in_bytes / 1024))


memory_available_in_mb=$((memory_available_in_kb / 1024))


echo "memory.capacity_in_bytes $memory_capacity_in_bytes"


echo "memory.usage_in_bytes $memory_usage_in_bytes"


echo "memory.total_inactive_file $memory_total_inactive_file"


echo "memory.working_set $memory_working_set"


echo "memory.available_in_bytes $memory_available_in_bytes"


echo "memory.available_in_kb $memory_available_in_kb"


echo "memory.available_in_mb $memory_available_in_mb"


复制


驱逐实例未被删除原因分析


源码中对于 Statefulset 和 DaemonSet 会自动删除 Evicted 实例,但是对于 Deployment 不会自动删除。阅读了部分官方文档以及 issue,暂未找到官方对 Deployment Evicted 实例未删除原因给出解释。


statefulset:pkg/controller/statefulset/stateful_set_control.go


// Examine each replica with respect to its ordinal


for i := range replicas {


// delete and recreate failed pods


if isFailed(replicas【i】) {


ssc.recorder.Eventf(set, v1.EventTypeWarning, "RecreatingFailedPod",


"StatefulSet %s/%s is recreating failed Pod %s",


set.Namespace,


set.Name,


replicas【i】.Name)


if err := ssc.podControl.DeleteStatefulPod(set, replicas【i】); err != nil {


return &status, err


}


if getPodRevision(replicas【i】) //代码效果参考:http://www.jhylw.com.cn/432535895.html

== currentRevision.Name {

status.CurrentReplicas--


}


if getPodRevision(replicas【i】) == updateRevision.Name {


status.UpdatedReplicas--


}


......


复制


daemonset:pkg/controller/daemon/daemon_controller.go


func (dsc DaemonSetsController) podsShouldBeOnNode(


......


) (nodesNeedingDaemonPods, podsToDelete 【】string) {


......


switch {


......


case shouldContinueRunning:


......


for _, pod := range daemonPods {


if pod.DeletionTimestamp != nil {


continue


}


if pod.Status.Phase == v1.PodFailed {


// This is a critical place where DS is often fighting with kubelet that rejects pods.


// We need to avoid hot looping and backoff.


backoffKey := failedPodsBackoffKey(ds, node.Name)


......


复制


解决方案


1、团队里面有了一套 k8s 集群事件采集的链路,我们通过消费 k8s 中 pod 的相关事件来进行处理,消费事件时过滤 pod 中与 Evicted 实例相关的事件然后处理即可。


Evicted 实例判断逻辑:


const (


podEvictedStatus = "Evicted"


)


// 判断如果为 Evicted 状态的实例且 Pod 中容器数为 0 时直接删除 pod


if strings.ToLower(status) == strings.ToLower(podEvictedStatus) && len(pod.Status.ContainerStatuses) == 0 {


}


复制


2、社区有人提供通过在 kube-controller-manager 中配置 podgc controller –terminated-pod-gc-threshold 参数来自动清理:


Podgc controller flags:


--terminated-pod-gc-threshold int32


Number of terminated pods that can exist before the terminated pod garbage collector starts deleting terminated pods. If


<= 0, the terminated pod garbage collector is disabled. (default 12500)


复制


该参数配置的是保留的异常实例数,默认值为 12500,但 podgc controller 回收 pod 时使用强杀模式不支持实例的优雅退出,因此暂不考虑使用。


3、其他处理方式可以参考社区中提供的 Kubelet does not delete evicted pods【2】。


总结


由于在之前的公司中对于稳定性的高度重视,线上节点并未开启驱逐实例的功能,因此也不会存在 Evicted 状态的实例,当节点资源严重不足时会有告警人工介入处理,以及还会有二次调度、故障自愈等一些辅助处理措施。本次针对 Evicted 相关实例的分析,发现 k8s 与操作系统之间存在了很多联系,如果要彻底搞清楚某些机制需要对操作系统的一些原理有一定的了解。


参考:


引用链接


【1】kubelet counts active page cache against memory.available (maybe it shouldn’t?):


【2】Kubelet does not delete evicted pods:

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
3天前
|
Kubernetes 监控 安全
Kubernetes实战:集群管理与自动化技术详解
【6月更文挑战第27天】Kubernetes实战聚焦集群自动化管理,涵盖核心概念如主从架构、API Server及工作节点,强调自动扩缩容、RBAC安全控制与日志监控。通过IaC工具如Helm实现配置自动化,结合Prometheus等进行持续监控,强调安全策略与资源优化,展现K8s在现代应用管理中的威力。
|
1月前
|
运维 Kubernetes 监控
Kubernetes详解(十九)——Kubernetes Pod控制器
Kubernetes详解(十九)——Kubernetes Pod控制器
47 3
|
12天前
|
运维 Kubernetes Cloud Native
云原生时代的技术革命:Kubernetes与容器编排
【6月更文挑战第17天】在数字化转型的浪潮中,云原生技术正成为推动企业IT架构现代化的核心力量。本文将深入探讨Kubernetes作为云原生生态中的佼佼者,如何引领容器编排的技术革命,并分析其在现代应用部署、管理和扩展中的关键作用。通过实例和案例分析,我们将揭示Kubernetes如何助力企业实现更高效、灵活和可靠的云原生应用管理。
|
19天前
|
运维 Kubernetes 持续交付
Docker与Kubernetes:容器化技术的黄金搭档
【6月更文挑战第10天】Docker和Kubernetes是容器化技术的黄金组合,Docker提供轻量级、可移植的容器引擎,简化应用部署,而Kubernetes作为容器编排系统,实现自动化部署、扩展和管理,确保高可用性和可扩展性。两者的协作使得容器化应用更高效、可靠,共同推动软件开发和运维的创新。
|
11天前
|
Kubernetes API 调度
Pod无法调度到可用的节点上(K8s)
完成k8s单节点部署后,创建了一个pod进行测试,后续该pod出现以下报错: Warning FailedScheduling 3h7m (x3 over 3h18m) default-scheduler 0/1 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
50 0
|
29天前
|
Kubernetes 算法 调度
k8s群集调度之 pod亲和 node亲和 标签指定
k8s群集调度之 pod亲和 node亲和 标签指定
|
1月前
|
Kubernetes 物联网 区块链
未来技术的脉动:区块链、物联网和虚拟现实的新纪元Kubernetes 集群性能优化实践
【5月更文挑战第27天】 随着科技的飞速发展,新兴技术如区块链、物联网(IoT)和虚拟现实(VR)正在重塑我们的世界。这些技术不仅在逐步成熟,而且在各个行业中找到了创新的应用。区块链技术以其不可篡改和去中心化的特性,为金融交易、供应链管理和身份验证提供了新的解决方案。物联网通过智能设备和系统的互联互通,优化了资源管理并提升了生活品质。而虚拟现实技术则在娱乐、教育和医疗等领域创造了沉浸式体验。本文将深入探讨这些技术的发展趋势和多样化应用场景,展望它们如何共同塑造未来社会的面貌。
|
28天前
|
Kubernetes 微服务 容器
Aspire项目发布到远程k8s集群
Aspire项目发布到远程k8s集群
379 2
Aspire项目发布到远程k8s集群
|
17天前
|
Kubernetes Cloud Native 微服务
微服务实践之使用 kube-vip 搭建高可用 Kubernetes 集群
微服务实践之使用 kube-vip 搭建高可用 Kubernetes 集群
203 3
|
2天前
|
Kubernetes 网络协议 Docker
k8s 开船记-故障公告:自建 k8s 集群在阿里云上大翻船
k8s 开船记-故障公告:自建 k8s 集群在阿里云上大翻船