已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
背景
15 年嵌入式出身,现在带一个 Go 1.22 的数据清洗服务(K8s 1.28.6 / containerd 1.7.13 / cgroup v2 / 内核 5.15,节点 16C64G)。服务每天凌晨跑批,从 NFS 拉一份 1.8~2.2GB 的 CSV 用 bufio.Scanner 流式解析,单副本,limit=1Gi、request=1Gi(GQA)。
现象
连续三天凌晨 OOMKilled,k8s 事件里都是 oom_kill=1。我 cat /sys/fs/cgroup/memory.stat(OOM 前 30 秒的快照):
anon 512MB、file 448MB、file_mapped 12MB、shmem 8MBmemory.current 968MB、memory.max 1073741824/heap 显示 Go heap in-use 仅 380MB,runtime.MemStats.TotalAlloc 没有异常增长也就是说:Go 堆根本没满,是 448MB 的 file-backed page cache 把 cgroup 顶穿了。strace 看到全是 read(fd=7, ...),fd 指向 NFS 挂载的 CSV。
已尝试
O_DIRECT 重开文件 → NFS 不支持,报 EINVAL;本地盘能跑但吞吐掉到 1/4,业务不接受。echo 3 > /proc/sys/vm/drop_caches → 只影响全节点,别的 Pod 一起抖,不敢在生产做。memory.high 做软压力 → K8s 1.28 的 Pod spec 没有 memory.high 字段,只能塞 memory.max,查了一圈没有原生入口。期望得到的答案形态
memory.current 参与 OOM 判定?有没有办法让内核在 OOM 时优先回收 file 而不是杀进程(比如 memory.oom.group 之外的开关)?limit = Go heap 峰值 × 1.2 + 文件 IO 缓冲,但 448MB 的 page cache 怎么估?有没有按 file 字段动态留余量的落地案例(Prometheus 指标 + HPA/VPA 之类的)?posix_fadvise(POSIX_FADV_DONTNEED) 边读边释放、madvise(MADV_DONTNEED)、或 cgroup 层面的 memory.swap.max=0 + vm.vfs_cache_pressure 调大)?各自的成本与收益怎么权衡?我倾向不抬 limit(抬了 P99 就抖),也不想在节点上做全局长。想听社区里有同样场景的同学怎么落地的。