记录一次K8s pod被杀的排查过程
问题描述
今天下午运维反馈说我们这一个pod一天重启了8次,需要排查下原因。一看Kiban日志,jvm没有抛出过任何错误,服务就直接重启了。显然是进程被直接杀了,初步判断是pod达到内存上限被K8s oomkill了。
因为我们xmx和xsx设置的都是3G,而pod的内存上限设置的是6G,所以出现这种情况还挺诡异的。
排查过程
初步定位
先找运维拉了一下pod的描述,关键信息在这里
Containers:
container-prod--:
Container ID: --
Image: --
Image ID: docker-pullable://--
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Fri, 05 Jan 2024 11:40:01 +0800
Last State: Terminated
Reason: Error
Exit Code: 137
Started: Fri, 05 Jan 2024 11:27:38 +0800
Finished: Fri, 05 Jan 2024 11:39:58 +0800
Ready: True
Restart Count: 8
Limits:
cpu: 8
memory: 6Gi
Requests:
cpu: 100m
memory: 512Mi
- 可以看到Last State:Terminated,Exit Code: 137。这个错误码表示的是pod进程被SIGKILL给杀掉了。一般情况下是因为pod达到内存上限被k8s杀了。
因此得出结论是生产环境暂时先扩大下pod的内存限制,让服务稳住。然后再排查为啥pod里会有这么多的堆外内存占用。
进一步分析
但是运维反馈说无法再扩大pod的内存限制,因为宿主机的内存已经占到了99%了。
然后结合pod的内存监控,发现pod被杀前的内存占用只到4G左右,没有达到上限的6G,pod就被kill掉了。
于是问题就来了,为啥pod没有达到内存上限就被kill了呢。
带着疑问,我开始在google里寻找答案,也发现了一些端倪:
- 如果是pod内存达到上限被kill,pod的描述里会写Exit Code: 137,但是Reason不是Error,而是OOMKilled
- 宿主机内存已经吃满,会触发k8s的保护机制,开始evict一些pod来释放资源
- 但是为什么整个集群里,只有这个pod被反复evict,其他服务没有影响?
谜题解开
最终还是google给出了答案:
Why my pod gets OOMKill (exit code 137) without reaching threshold of requested memory
链接里的作者遇到了和我一样的情况,pod还没吃到内存上限就被杀了,而且也是:
Last State: Terminated
Reason: Error
Exit Code: 137
作者最终定位的原因是因为k8s的QoS机制,在宿主机资源耗尽的时候,会按照QoS机制的优先级,去杀掉pod来释放资源。
什么是k8s的QoS?
QoS,指的是Quality of Service,也就是k8s用来标记各个pod对于资源使用情况的质量,QoS会直接影响当节点资源耗尽的时候k8s对pod进行evict的决策。官方的描述在这里.
k8s会以pod的描述文件里的资源限制,对pod进行分级:
| QoS | 条件 |
|---|---|
| Guaranteed | 1. pod里所有的容器都必须设置cpu和内存的request和limit,2. pod里所有容器设置的cpu和内存的request和容器设置的limit必须相等(容器自身相等,不同容器可以不等) |
| Burstable | 1. pod并不满足Guaranteed的条件,2. 至少有一个容器设置了cpu或者内存的request或者limit |
| BestEffort | pod里的所有容器,都没有设置任何资源的request和limit |
当节点资源耗尽的时候,k8s会按照BestEffort->Burstable->Guaranteed这样的优先级去选择杀死pod去释放资源。
从上面运维给我们的pod描述可以看到,这个pod的资源限制是这样的:
Limits:
cpu: 8
memory: 6Gi
Requests:
cpu: 100m
memory: 512Mi
显然符合的是Burstable的标准,所以宿主机内存耗尽的情况下,如果其他服务都是Guaranteed,那自然会一直杀死这个pod来释放资源,哪怕pod本身并没有达到6G的内存上限。
QoS相同的情况下,按照什么优先级去Evict?
但是和运维沟通了一下,我们集群内所有pod的配置,limit和request都是不一样的,也就是说,大家都是Burstable。所以为什么其他pod没有被evict,只有这个pod被反复evict呢?
QoS相同的情况,肯定还是会有evict的优先级的,只是需要我们再去寻找下官方文档。
关于Node资源耗尽时候的Evict机制,官方文档有很详细的描述。
其中最关键的一段是这个:
If the kubelet can't reclaim memory before a node experiences OOM, the
oom_killercalculates anoom_scorebased on the percentage of memory it's using on the node, and then adds theoom_score_adjto get an effectiveoom_scorefor each container. It then kills the container with the highest score.This means that containers in low QoS pods that consume a large amount of memory relative to their scheduling requests are killed first.
简单来说就是pod evict的标准来自oom_score,每个pod都会被计算出来一个oom_score,而oom_score的计算方式是:pod使用的内存占总内存的比例加上pod的oom_score_adj值。
oom_score_adj的值是k8s基于QoS计算出来的一个偏移值,计算方法:
| QoS | oom_score_adj |
|---|---|
| Guaranteed | -997 |
| BestEffort | 1000 |
| Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
从这个表格可以看出:
- 首先是BestEffort->Burstable->Guaranteed这样的一个整体的优先级
- 然后都是Burstable的时候,pod实际占用内存/pod的request内存比例最高的,会被优先Evict
总结
至此已经可以基本上定位出Pod被反复重启的原因了:
- k8s节点宿主机内存占用满了,触发了Node-pressure Eviction
- 按照Node-pressure Eviction的优先级,k8s选择了oom_score最高的pod去evict
- 由于所有pod都是Burstable,并且设置的request memery都是一样的512M,因此内存占用最多的pod计算出来的oom_score就是最高的
- 所有pod中,这个服务的内存占用一直都是最高的,所以每次计算出来,最后都是杀死这个pod
那么如何解决呢?
- 宿主机内存扩容,不然杀死pod这样的事情无法避免,无非就是杀哪个的问题
- 对于关键服务的pod,要把request和limit设置为完全一致,让pod的QoS置为Guaranteed,尽可能降低pod被杀的几率
记录一次K8s pod被杀的排查过程的更多相关文章
- k8s pod故障分类与排查
一.Pod故障状态基本有几种Pod状态 处于PendingPod状态 处于WaitingPod状态 处于ContainerCreatingPod状态 ImagePullBackOffPod状态 Cra ...
- k8s pod的4种网络模式最佳实战(externalIPs )
[k8s]k8s pod的4种网络模式最佳实战(externalIPs ) hostPort相当于docker run -p 8081:8080,不用创建svc,因此端口只在容器运行的vm ...
- k8s pod访问不通外网问题排查
环境概况 自建k8s集群,主机操作系统ubuntu16.04,k8s版本v1.14, 集群网络方案calico-3.3.6. worker节点数50+,均为GPU物理服务器,服务器类型异构,如Nvid ...
- k8s pod节点调度及k8s资源优化
一.k8s pod 在节点间调度控制 k8s起pod时,会通过调度器scheduler选择某个节点完成调度,选择在某个节点上完成pod创建.当需要在指定pod运行在某个节点上时,可以通过以下几种方式: ...
- kubectl cp 从k8s pod 中 拷贝 文件到本地
请查看官方的说明 kubectl cp --help 官方说使用cp , pod里需要有tar命令 从k8s pod 中 拷贝 文件到本地 这是我使用的命令 kubectl exec redis-6c ...
- K8S线上集群排查,实测排查Node节点NotReady异常状态
一,文章简述 大家好,本篇是个人的第 2 篇文章.是关于在之前项目中,k8s 线上集群中 Node 节点状态变成 NotReady 状态,导致整个 Node 节点中容器停止服务后的问题排查. 文章中所 ...
- 记录linux tty的一次软锁排查2
在复现tty的死锁问题的时候,文洋兄使用了如下的方式: #include <fcntl.h> #include <unistd.h> #include <stdio.h& ...
- Linux(2)---记录一次线上服务 CPU 100%的排查过程
Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...
- 超详细从零记录Hadoop2.7.3完全分布式集群部署过程
超详细从零记录Ubuntu16.04.1 3台服务器上Hadoop2.7.3完全分布式集群部署过程.包含,Ubuntu服务器创建.远程工具连接配置.Ubuntu服务器配置.Hadoop文件配置.Had ...
- Kubernetes Pod故障归类与排查方法
Pod概念 Pod是kubernetes集群中最小的部署和管理的基本单元,协同寻址,协同调度. Pod是一个或多个容器的集合,是一个或一组服务(进程)的抽象集合. Pod中可以共享网络和存储(可以简单 ...
随机推荐
- Solution -「ARC 103B」Robot Arms
Description Link. 给定 \(n\) 组坐标.构造长度为 \(m\) 的序列 \(\{c_n\}\) 和 \(n\) 组包含 LRUD 的路径,满足对于每一组坐标: \(c_i\) 表 ...
- What is Lambda?
根据我的观察,Lambda是一种比较灵活的形式,需要多看几个案例才能明白它. Lambda是一种简化代码的技术手段,主要用于简化匿名实现类,允许把函数作为一个方法的参数传递进方法中.它本身并不会创造出 ...
- fprintf
fprintf 是一个标准C库函数,用于将格式化的输出写入到指定文件流中.它的函数原型如下: int fprintf(FILE *stream, const char *format, ...); 参 ...
- 带宽优化新思路:RoCE网卡聚合实现X2增长
本文分享自华为云社区<2个RoCE网卡Bond聚合,实现带宽X2>,作者: tsjsdbd . 我们知道操作系统里面,可以将2个实际的物理网卡,合体形成一个"逻辑网卡" ...
- Django框架——Django与Ajax、分页器
文章目录 1 Django与Ajax 一 什么是Ajax 优点: 二 基于jquery的Ajax实现 Ajax-->服务器-->Ajax执行流程图 三 案例 一 通过Ajax,实现前端输入 ...
- ddddocr1.4.8失效的解决方法
1. 问题描述 from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.max ...
- mooc第四单元《管理团队》单元测试
第四单元<管理团队>单元测试 返回 本次得分为:6.00/10.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 判断(2分 ...
- http 500 错误
214 [Thu Sep 09 16:03:54.919334 2021] [:error] [pid 32045] [client 124.238.184.230:23796] script '/v ...
- MQ系列15:MQ实现批量消息处理
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...
- ACTF 2023 部分WP
来自密码手的哀嚎: 玩不了一点,太难了. CRYPTO MDH Description Malin's Diffile-Hellman Key Exchange. task.sage from has ...