为docker设计的早期OOM killer
当程序写歪了或者故意的,在某一刻开始分配巨量内存并fork自己,它就会光速消耗完内存,内存耗尽进入交换时会将IO和CPU占满,最终拖垮系统让人ssh连不上tty打不开,没办法软重启。
我们构建机器上的node/npm/vite/composer在安装包/构建时就有这样的行为,经过很长时间的调查,我们最终还是没能确定问题在哪,怎么修复。
于是就有了这个简单的早期oom killer
- 将自己所有的内存页从file backed变成anonymous,防止IO队列满时无法访问内存
- mlock所有页,防止被交换出内存
- 设置nice和ionice
- 每1s扫描一次所有docker容器的cgroup,如果容器内存占用(memory.current)超过设定值,向cgroup.kill写1
无法处理的情况:
- 恶意进程通过各种手段直接将CPU吃满而不提高内存占用
- 多个docker容器一同触发问题,每个docker容器的内存占用低于阈值
问题:
- 扫描的路径是docker.service所在的scope,docker会将它的构建容器放在同级scope里,这导致了如果同级有需要占用超过阈值内存的服务,会被它杀掉
- 暂不确认如果拒绝服务已经发生,IO队列满的情况下,VFS延迟会不会爆炸,会不会导致读取cgroupfs卡住或者向cgroup.kill写1无法生效
可能的解决方案:
- 写一个内核模块来监视cgroup
- 使用eBPF(同样不知道能不能绕过IO)
先递归带submodule clone
需要musl gcc wrapper
make
sudo /path/to/boomwatcher /path/to/scope number
例如,对于系统dockerd,限制4G内存:
sudo ./boomwatcher /sys/fs/cgroup/system.slice 4294967296
提供了钉钉通知的简单实现:设置自定义机器人,关键词为boomwatcher
,设置环境变量NOFITY_TOKEN
为access_token。
提供了一个赛博电灯泡,请不要放进嘴里
memoryeater是模拟恶意程序的,不要在没有准备的情况下运行
memoryeater是模拟恶意程序的,不要在没有准备的情况下运行
memoryeater是模拟恶意程序的,不要在没有准备的情况下运行
可以构建Dockerfile.memoryeater来模拟构建内存爆炸的情况,在构建前请保证构建机器上所有工作已保存,sync已执行,有备份并可以物理重启