k8s生产环境业务某个pod一天内多次异常重启问题排查分析
QoS指的是Quality of Service,中译就是服务质量,也就是k8s用来标记各个pod对于资源使用情况的质量,QoS会直接影响当节点资源耗尽的时候k8s对pod进行evict的决策。官方的描述在这里.:k8s会以pod的描述文件里的资源限制,对pod进行分级.此处的资源限制指的就是k8s资源控制器yaml文件中的Request和Limit.如下表格所示。QoS条件Guaranteed(
文章目录
背景
现象: k8s生产环境的集群中有个业务pod一天内重启了N次,其余pod未出现该情况,需要排查下原因。查看该pod日志,jvm没有抛出过任何错误,服务就直接重启了。显然是进程被直接杀了,初步判断是pod达到内存上限被K8s OOMKill了.但具体分析排查如下所示
提示:以下是本篇文章正文内容,下面案例可供参考
一、排查过程
1、在k8s master节点查看该pod 的相关描述
[root@k8s-master]# kubectl describe pod -n xxx xxx-sharedata-59nghdx
#其余信息看下方图片所示
Last State:Terminated
Exit Code: 137
通过容器退出状态码得知,Exit Code: 137 的含义是立即终止 (SIGKILL),容器被操作系统通过 SIGKILL 信号终止。
一般情况下是因为pod达到内存上限被k8s杀了。因此得出结论是: 首先生产环境暂时先扩大下pod的内存限制,让服务先平稳运行。然后再排查为什么pod里会有这么多的堆外内存占用。
但是通过进一步分析得知: 已经无法再扩大pod的内存限制,因为宿主机的内存已经占到了98%以上了。 然后结合pod的内存监控,发现pod被杀前的内存占用只到4G左右,没有达到上限的6G,pod就被kill掉了,所以问题就出来了。
2、问题引出
具体问题:
为什么pod没有达到内存上限就被kill了呢? 带着疑问,我开始查看k8s官方文档及王松寻找答案,也发现了以下3处端倪
a、如果是pod内存达到上限被kill,pod的描述里会写Exit Code: 137,但是Reason不是Error,而是OOMKilled
b、宿主机内存已经吃满,会触发k8s的保护机制,开始evict一些pod来释放资源
c、但是为什么整个集群里,只有这个pod被反复evict,其他服务没有影响?
二、问题定位与解决
1、问题定位
最终结合k8s官方文档及网上搜寻的结果,确定了该问题是因为k8s的QoS机制(服务质量),该机制会在宿主机资源耗尽时,按照该机制的优先级,kill掉pod来释放资源
2、什么是k8s的QoS?
QoS指的是Quality of Service,中译就是服务质量,也就是k8s用来标记各个pod对于资源使用情况的质量,QoS会直接影响当节点资源耗尽的时候k8s对pod进行evict的决策。
官方的描述在这里.:k8s会以pod的描述文件里的资源限制,对pod进行分级.此处的资源限制指的就是k8s资源控制器yaml文件中的Request和Limit.如下表格所示。
QoS | 条件 |
---|---|
Guaranteed(可靠) | pod里所有的容器必须设置cpu和mem的request和limit且设置的cpu和内存的 request和limit必须相等 |
Burstable(基本可靠) | pod不满足Guaranteed的条件,且至少有一个容器设置了cpu或mem的request和limit |
BestEffort(不可靠) | pod里所有容器都没有设置任何资源的request和limit |
2.1、 pod的request和limit到底是如何应对到这三个QoS等级上的,可以参考下方表格
2.2、 三种QoS等级可能会产生的一些服务现象如下所示
通过阅读文档整理得出以下关系
2.3、QoS在容器中的应用总结(重要)
从上面的QoS机制描述可以看到,这个pod的资源限制是这样的: 当节点资源耗尽的时候,k8s会按照BestEffort-->Burstable-->Guaranteed这样的优先级去选择杀死pod去释放资源。
3、QoS相同的情况下,按照什么优先级去Evict?
因为在实际的生产环境中,集群内所有pod的配置,它的request和limit都是不一样的,甚至有的都不给pod配置资源限制
现在思绪回到我的这个问题上,因为在我维护的生产环境中,pod都配置QoS服务质量属于Burstable(可靠)这类。所以为什么其他pod没有被驱逐,只有这个pod被驱逐呢?
当QoS相同的情况,肯定还是会有驱逐的优先级,通过查看k8s官方文档,获得了以下知识点:
Node资源耗尽时候的驱逐机制
重要知识点: 请牢记
If the kubelet can't reclaim memory before a node experiences OOM, the oom_killer calculates an oom_score based on the percentage of memory it's using on the node, and then adds the oom_score_adj to get an effective oom_score for 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.
如果kubelet无法在节点经历OOM之前回收内存,OOM_killer会根据节点上使用的内存百分比计算OOM_score,然后将OOM_score_adj相加,以获得每个容器的有效OOM_scored。然后它杀死得分最高的容器。这意味着,相对于调度请求而言,低QoS pod中消耗大量内存的容器将首先被杀死。
综上所述:
简单来说就是pod evict的标准来自oom_score,每个pod都会被计算出来一个oom_score,而oom_score的计算方式是:
**(pod_used_mem/all_mem) + 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)
QoS相同的情况下驱逐总结:
从上述这个表格可以看出: 当QoS相同的情况下,首先是BestEffort-->Burstable-->Guaranteed这样的一个整体的优先级;
*当都是Burstable的时候,pod实际占用内存/pod的request内存比例最高的,会被优先驱逐*
4、问题解决
4.1、至此已经可以基本上定位出Pod被反复重启的原因了
1、k8s节点宿主机内存占用满了,触发了Node-pressure Eviction;
2、按照Node-pressure Eviction的优先级,k8s选择了oom_score最高的pod去evict;
3、由于所有pod都是Burstable,并且设置的request memery都是一样的512M,因此内存占用最多的pod计算出来的oom_score就是最高的所有pod中,这个pod服务的内存占用一直都是最高的,所以每次计算出来,最后都是杀死这个pod。
4.2、解决方法
•宿主机内存扩容,不然杀死pod这样的事情无法避免,无非就是杀哪个的问题
•提高关键服务的pod的QoS质量,把request和limit设置为完全一致,让pod的QoS置为Guaranteed,尽可能降低关键pod被杀的几率
•通过pod监控分析并降低占用可能内存高但属于边角料pod的QoS质量,当出现问题优先kill这种边角料pod
总结
以上就是我实际维护环境中遇到的一个问题,在排查问题的过程中,也学习到了更多的k8s知识,夯实自身基础,从遇到问题—>排查问题—>查阅文档—>记录解决问题过程及使用到的知识 做起!! 这篇文章也可以作为面经到时讲给面试官听。
更多推荐
所有评论(0)