一 问题现象
服务器的磁盘空间爆满(90%以上),触发某种机制,导致大量pod处于被驱逐状态(Evicted),大部分镜像被删除,所有服务均不可用。
二 问题追踪
发现服务器的存储达到某个状态(假如是超过90%)后会删除自身的一些资源,比较明显的是删除了大量的镜像,因此会出现一些pod的状态是镜像下载失败,包括k8s自身的系统镜像。服务器本身并不存在自动清理根目录,除非是写自动清理的脚本。排查了一圈,并不存在,因此极大概率是k8s自己给删的。我在官方的一篇文档中,找到了这个机制。
三 机制说明
每个node上的kubelet都负责定期采集资源占用数据,并与预设的 threshold值进行比对,如果超过 threshold值,kubelet就会尝试杀掉一些Pod以回收相关资源,对Node进行保护。kubelet关注的资源指标threshold大约有如下几种:
- memory.available
- nodefs.available
- nodefs.inodesFree
- imagefs.available
- imagefs.inodesFree
每种threshold又分为eviction-soft和eviction-hard两组值。soft和hard的区别在于前者在到达threshold值时会给pod一段时间优雅退出,而后者则崇尚“暴力”,直接杀掉pod,没有任何优雅退出的机会。这里还要提一下nodefs和imagefs的区别:
nodefs: 指node自身的存储,存储daemon的运行日志等,一般指root分区/;
imagefs: 指docker daemon用于存储image和容器可写层(writable layer)的磁盘;
在我遇到的问题中,我们的imagefs和nodefs分区是同一个分区,即/分区,占用率很高(96%)。列一下其中一些指标的阈值:
memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%
(至于其他的指标阈值为啥没有,因为我没找到。)
其中比较重要的动作,就是达到nodefs和imagefs的阈值后的回收机制。
简单来说,就是nodefs超过阈值了,k8s会自动干掉本机上的pod和pod对应的容器(这里是k8s的驱逐机制,删除本节点pod,在其他节点启动,在不放守护机制的前提下是不顺滑的,存在一定时间的服务中断);如果imagefs超过阈值了,会删除没有使用到的镜像文件。
那么问题来了:如果nodefs和imagefs是同一个分区,会出现什么问题呢?k8s会首先驱逐本机上的pod到其他节点,然后资源仍然不够,会删除没有被使用到的镜像,直到剩余空间低于设定的阈值,甚至会删除k8s的系统镜像。
四 解决办法
1 修改这几个指标的阈值(不推荐)
2 添加监控,在阈值到达之前提前处理