最近发现一则比较奇怪的业务性能抖动问题
简单来说,就是离线作业频繁的触发oom,导致在线业务指标陡增。理论上来说,离线作业触发自身硬限,不应该导致其他容器出问题才是
先来说一下我们的 memory cgroup 层级结构,可以简单理解为如下:
/cgroups/v2/kubepods/
/cgroups/v2/kubepods/online/a -> a为在线服务
/cgroups/v2/kubepods/offline/job1 -> job1 为离线服务
通常来说,为了解决混部的内存争抢问题,我们会对整个 offline 大框设置一个内存上限,防止离线作业使用内存太多,导致在线服务出现内存压力。这看起来似乎没什么问题。
不过最近一个问题,发现离线 job1 频繁 oom,引发了在线的性能抖动
最后定位的结论如下:
- /cgroups/v2/kubepods/offline/cgroup.stat 下看到有大量的 nr_dying_descendants
- offline 大框频繁出发 oom
- oom 的回收过程有个效率不太好的地方,导致 oom 回收效率底下 -> 导致内核各种锁争抢严重 -> 进程sys高 -> cpu排队严重 -> 在线业务性能抖动
根因分析:nr_dying_descendants 的原因是由于容器被销毁时,容器内还有未完全回收干净的 page cache,所以内核不会立即释放这个 cgroup(用户态操作系统从 /cgroups 文件系统下已经看不到了,但是内核态的数据结构还在)
cat /cgroups/v2/kubepods/online/cgroup.stat
nr_descendants 10
nr_dying_descendants 4172
这就有一个问题,如果有大量的dying cgroups,内核在oom处理过程中:
- 如果是cgroup oom,会优先尝试从 dying cgroups 回收内存,但是最多只回收5个 -> 这个地方有效率问题
- 如果是整机回收,不处理dying cgroups内存
dying cgroups 的回收,在 shrink_node() 函数里面:
static bool shrink_node(pg_data_t *pgdat, struct scan_control *sc) { do { if (!global_reclaim(sc)) { do { __get_reclaim_lowest_priority_mem(memcg, &priority); if (!mem_cgroup_online(memcg) && memcg->kswapd_failures < MEM_DYING_CGROUP_MAX_RECLAIM_LOOPS) { shrink_node_memcg(tmp_pgdat, memcg, sc, &lru_pages); } } while ((memcg = mem_cgroup_iter(sc->target_mem_cgroup, memcg, NULL))); } } }
其中 mem_cgroup_online() 是用来判断容器是不是 dying 状态的