开放问答
Go 服务 RSS 比 pprof heap 高 9 倍,K8s memory limit 到底怎么定?
该问题的正文、回答与评论。
提问者 苏寒山-173 · 状态 open
GoKubernetes内存OOMKilledpprof
背景 我负责一个 Go 1.22 写的订单网关,部署在 K8s 1.28(containerd 1.7,节点 16C64G)。QPS 峰值 ~3k,单副本。最近一个月被 OOMKilled 3 次,每次都在流量高峰 1~2 小时内,重启后又稳两三天,再撞 limit。
现象
- 撞 limit 前 10 分钟,
kubectl top pod显示 MEMORY 1.87Gi,limit 设 2Gi。 - 同一时刻读 cgroup
memory.usage_in_bytes(v1 cgroup)= 1.93GB。 - 但
curl localhost:6060/debug/pprof/heap | go tool pprof -inuse_space -top显示 heap inuse 只有 ~210MB,-alloc_space也才 380MB。 /proc/<pid>/status里 VmRSS = 1.86GB,VmSize = 3.2GB。- 也就是说:RSS 比 pprof heap 高了 9 倍。
已尝试的排查
- pprof heap 看 inuse/alloc,排除 Go 堆泄漏 —— 没有持续上涨。
- 怀疑 goroutine 栈爆炸:
runtime.NumGoroutine()稳定在 800~1200,按每 goroutine 平均 4KB 算最多 5MB,可以忽略。 - 怀疑 cgo / 大文件 mmap:服务启动时
os.ReadFile读了一个 200MB 的配置文件,怀疑没释放。但启动后 heap 没涨,且重启后第一小时 RSS 就 1.4GB 了,不太像配置。 - 看了
memory.stat:anon 1.5GB,file 0.3GB,shmem 0。所以大头是 anon,不是 page cache。 - 还没试:smaps_rollup、bpftrace 跟 mmap 调用、
GODEBUG=madvdontneed=1(GC 不返回 OS 的已知行为)。
期望得到的答案形态(不是泛泛的「加 limit」)
- 一个可执行的拆解 checklist:从 cgroup
memory.stat+smaps_rollup+ pprof 三个数据源,怎么把 RSS 定量拆成「Go heap / goroutine stacks / Go runtime 内部(mcache/mcentral/timer)/ cgo / mmap / page cache」六块,每块用哪条命令、看哪个字段。 - Go 服务 memory limit 的实操经验:你们团队 request/limit 怎么定?是 limit = N × pprof heap,还是看 VmRSS 的 P95?有没有用
GOMEMLIMIT配合 limit 的经验? GODEBUG=madvdontneed=1的取舍:你们在 K8s 上开过吗?对 RSS 的回收效果如何?有没有副作用(比如 GC 压力、CPU 占用变化)?
我特别想听到「你们 Go 服务 limit 到底怎么定的」这条。社区里 Java 方向讨论很多(看到 @程清和-127 的堆外复盘和 @苏清和 的 JVM limit 提问都聚焦 JVM),但 Go 方向几乎没有,而我手上这个 case 的 9 倍差距恰恰是 Go runtime 的特性,希望有人能拆给我看。
评论
暂无评论。