Kubernetes pod oom 问题 排查记录

背景

近期维护的 Kubernetes 组件 pod 在某些集群上经常遇到 oom 问题。 导致 container 频繁重启. 该组件在集群中的主要作用是根据 pvc & sc 的配置 动态创建 pv。由于 oom 会导致 container 自动重启,而 pending 状态的 pvc 会自动重试。所以在功能上并没有给用户的集群造成特别大的影响。只是每次 oom 的时候集群内都有相关的报警,给用户造成困扰。

复现

由于我们无法在客户集群上做太多变更,并且客户集群只有内网入口,这边操作十分困难。于是我们尝试在自己的测试集群上复现该问题。 该组件的作用是根据 pvc 创建 pv 并进行绑定,理所当然想到是因为创建了过多的 pvc 导致了组件使用了过多的内存。这边一开始尝试顺序创建了 500 个 pvc, 等待 pv 的生成。发现一切如常。这些 pv 被顺利的创建了出来,并且组件也没有重启。于是我们改变压测脚本,使其并发创建。结果复现了现象

  1. 大量的 pvc 处于 Pending 状态
    Kubernetes pod oom 问题 排查记录
  2. pod 中的 container 内存使用量达到 limit(100M)
    Kubernetes pod oom 问题 排查记录

原因

golang 程序内存分析

现在已经明确是 container 内存使用过多的问题,我们首先借助 pprof 工具来分析 container 中 golang 程序的内存使用情况,找出内存瓶颈并优化。

  • 我们在组件的代码中埋入pprof,并通过 8899 端口将探测点暴露出来。
import _ "net/http/pprof"
go func() {
    log.Println(http.ListenAndServe("localhost:8899", nil))
}()
  • 将 pprof 暴露的端口映射到本地端口上(方便在本地进行追踪)

kubectl port-forward xxxxxxx 8899:8899 -nkube-system

  • 开始监听 pod 内 golang 进程,同时开始进行压测,观察进程的内存使用状况
    Kubernetes pod oom 问题 排查记录

经过初步的分析,发现 golang 程序使用的内存远远未达到 container limit 所设置的上线

pod 内新建进程内存分析

既然 golang 进程本身并没有使用过多的内存,那么就可以判断是 pod 内的其他进程导致的 container oom, 该组件由于业务原因需要使用 exec.Command() 调用 pod 内的二进制程序执行一些操作。会创建新的子进程进行调用。

  • 使用 kubectl exec -it 命令进入容器内部,使用 ps -auxef 命令查看调用关系以及子进程使用的内存
    Kubernetes pod oom 问题 排查记录

Kubernetes pod oom 问题 排查记录

  • 观察上图发现了几个问题

    • findmnt 命令由于使用了一系列管道,进一步创建了子进程
    • mount 命令以及其子命令占用了大量的内存

使用 strace 追踪 golang 以及其子进程调用

  • strace -f -F -o output.txt -T -tt -e trace=all -p xxxxx
    Kubernetes pod oom 问题 排查记录

通过观察发现了一个新的问题:

  • pod 内所有的进程都有被 killed 的情况, 但是 pod 本身并没有被 killed 掉(strace程序依旧在运行)

修复方案

  1. 变更 代码里面使用 管道命令的操作,使用 grep -e 命令代替
  2. 对 mount 命令进行限流,
  3. 目前这个操作只由一个 pod 进行处理,需要对请求进行分流。下发请求到各个 Node 上来缓解压力

问题

以上方案经过验证的确可以解决问题,但是依旧留下了两个问题

  1. 为什么单个 mount 命令可以使用几十兆内存
  2. 进程不是由于 pod 被 kill 导致退出(strace还在,但是用户进程却被killed掉了)是由于什么限制导致的进程被kill掉退出呢?
上一篇:ACK 云盘定时快照使用文档


下一篇:如何在golang代码里面解析容器镜像