容器内存一路涨到 OOM,我没改一行业务代码,靠 eBPF 把"贼"揪出来了

举报
Echo_Wish 发表于 2026/11/05 08:42:02 2026/11/05
【摘要】 容器内存一路涨到 OOM,我没改一行业务代码,靠 eBPF 把"贼"揪出来了

容器内存一路涨到 OOM,我没改一行业务代码,靠 eBPF 把"贼"揪出来了

我是 Echo_Wish。前阵子半夜被叫起来,一个跑在 K8s 里的服务每隔几个小时就被 OOMKilled 一次,重启完内存又从头爬坡,像西西弗斯推石头。

业务方的第一反应是:“能不能给个 heap dump?” 我说,这是 Python 加 C 扩展的混合服务,heap dump 只能看到 Python 那一层,真正涨的地方它看不见。

这就是我今天想聊的:很多时候,你没法直接进应用里排查,那就绕着走,从旁边的系统层面把它盯死。 eBPF 干的就是这个活。

第一步:先别急着上工具,先问"涨的是哪块内存"

我见过太多人一上来就 memleak,结果查了半天发现压根不是泄漏。先看容器的内存到底怎么涨的:

# 找到容器在宿主机上的 PID
PID=$(crictl inspect --output go-template --template '{{.info.pid}}' <container_id>)

# 看 cgroup 层面的内存构成
cat /sys/fs/cgroup/kubepods.slice/.../memory.stat | egrep "^(anon|file|slab|sock) "

# 看进程自己的 RSS 变化
while true; do grep -E "VmRSS|RssAnon" /proc/$PID/status; sleep 10; done

结果很关键:anon 持续涨,file 和 slab 基本不动。这说明是匿名内存在涨,也就是堆和 mmap 出来的那种,page cache 没背锅。方向对了,才轮到 eBPF 登场。

第二步:不碰应用,从系统调用层面"数人头"

应用层我进不去,但应用要内存,总得向内核要。内核这扇门,eBPF 守得住:

bpftrace -e '
tracepoint:syscalls:sys_enter_mmap /pid == '$PID'/ {
    @mmap_bytes[ustack(6)] = sum(args->len);
}
tracepoint:syscalls:sys_enter_munmap /pid == '$PID'/ {
    @munmap_bytes = sum(args->len);
}
interval:s:60 { exit(); }
'

跑一分钟,对比 mmap 和 munmap 的量。我当时看到的是:申请的远多于释放的,而且绝大部分集中在同一条用户态调用栈上。 栈顶落在一个叫 decode_frame 的函数里,属于一个第三方 C 库。

这就是"间接"的好处:我没有去猜应用逻辑,而是让内核告诉我谁在要内存。

第三步:再用 page fault 交叉验证

只看 mmap 有个坑:申请了不代表用了。真正吃掉物理内存的是缺页中断:

bpftrace -e '
software:page-faults:1 /pid == '$PID'/ {
    @faults[ustack(8)] = count();
}
interval:s:30 { exit(); }
'

两份数据的热点栈对上了,我才敢下结论。单一证据别轻易定罪,两条独立线索指向同一个人,才叫实锤。

第四步:最后上 memleak 确认"只借不还"

/usr/share/bcc/tools/memleak -p $PID -T 5 60

输出里 decode_frame 那条栈,未释放字节数一直在涨。回头翻代码,原来是一个异常分支提前 return,漏掉了 free(buf)。线上遇到畸形数据才会走进去,所以泄漏很慢,慢到你以为是"正常增长"。

一个容易被忽略的反面案例

另一个服务我也遇到过 RSS 暴涨,同样的流程跑下来,发现 malloc 和 free 是基本平衡的。罪魁祸首是 glibc 的 arena 碎片:多线程下每个线程各占一个 arena,释放的内存还回不去操作系统。

# 不改代码,一个环境变量就能验证
MALLOC_ARENA_MAX=2

加上之后内存曲线立刻平了。这类问题如果你一上来就认定"泄漏",会白白追几天。

聊点心里话

干了这么多年运维,我越来越觉得:

  1. 排查的本质是缩小范围,不是堆工具。 从 cgroup 到进程、到系统调用、到调用栈,每一步都在排除一大片可能性。
  2. eBPF 最大的价值是"无侵入"。 不重启、不改代码、不加 agent,生产环境上敢动手,这点比炫酷的火焰图重要得多。
  3. 别迷信单一指标。 RSS、mmap、page fault、memleak 互相印证,才不容易冤枉好人。
  4. "泄漏"和"涨"不是一回事。 缓存、碎片、延迟释放都会让曲线好看不到哪去,分清楚再动手。

最后说一句,工具永远是第二位的。真正值钱的是你面对一条诡异曲线时,脑子里那套"先问是什么涨、再问谁要的、最后问谁没还"的排查顺序。把这个顺序练熟了,换成别的工具也一样好使。

我是 Echo_Wish,下次再聊。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。