背景

现象: 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知识,夯实自身基础,从遇到问题—>排查问题—>查阅文档—>记录解决问题过程及使用到的知识 做起!! 这篇文章也可以作为面经到时讲给面试官听。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐