Linux 设置内存用量限制
实验室有一位大神,他写的程序每次都会吃掉几百 G 的内存然后把服务器搞崩。作为 SysAdmin,肯定不能让服务器动不动就崩溃,因此我决定采取一些方法限制内存用量。
使用 earlyoom
earlyoom 会在可用内存和 SWAP 均不足 10% 时关闭内存占用最大的进程。
安装
-
从 APT 安装 earlyoom(版本可能较旧):
sudo apt install earlyoom -
手动安装:
git clone https://github.com/rfjakob/earlyoom.git cd earlyoom make sudo make install
配置
-
编辑配置文件:
sudoedit /etc/default/earlyoomEARLYOOM_ARGS="-m 5 -r 60 --avoid '(^|/)(init|Xorg|ssh)$' --prefer '(^|/)(java|chromium)$'"-m PERCENT[,KILL_PERCENT]:设置可用内存的百分比阈值。当可用内存低于PERCENT(默认 10)时发送 SIGTERM 信号,低于KILL_PERCENT(默认PERCENT/2)时发送 SIGKILL 信号-s PERCENT[,KILL_PERCENT]:设置空闲 SWAP 的PERCENT和KILL_PERCENT。可用内存和 SWAP 的阈值都达到之后 earlyoom 才会介入-M SIZE[,KILL_SIZE]:设置可用内存的绝对值阈值(KiB)-S SIZE[,KILL_SIZE]:设置 SWAP 的绝对值阈值(KiB)-n:启用 d-bus 通知-N /PATH/TO/SCRIPT:oom kill 后要执行的脚本-g:杀死进程组内的所有进程-r INTERVAL:内存报告间隔(sec)(默认 1)--dryrun:dry run,不实际杀死进程--ignore-root-user:不杀死 root 用户的进程--prefer REGEX:优先杀死匹配正则表达式的进程--avoid REGEX:避免杀死匹配正则表达式的进程--ignore REGEX:忽略匹配正则表达式的进程
-
重启 earlyoom:
sudo systemctl restart earlyoom -
查看服务状态:
sudo systemctl status earlyoom -
查看服务日志:
sudo journalctl -u earlyoom | grep sending
测试
-
模拟内存泄露:
tail /dev/zero -
启动
earlyoom:earlyoom
使用 cgroup
对于基于 systemd 的发行版,建议使用 systemd slice 管理 cgroup:
-
编辑
.slice文件:sudoedit /etc/systemd/system/user.slice # 对所有用户添加共享 cgroup 限制 sudoedit /etc/systemd/system/user-.slice.d/50-defaults.conf # 对所有用户添加独立 cgroup 限制 sudoedit /etc/systemd/system/user-1000.slice # 对指定 UID 用户添加 cgroup 限制不要命名为
/etc/systemd/system/user-.slice.d/10-defaults.conf,会覆盖/usr/lib/systemd/system/user-.slice.d/10-defaults.conf。[Unit] Description=User Slice of UID %j Documentation=man:user@.service(5) StopWhenUnneeded=yes [Slice] # 限制 CPU 使用率 CPUQuota=200% # 限制内存用量 MemoryHigh=3G MemoryMax=4G # 限制任务数量 TasksMax=100 # IO 权重设置 IOWeight=100CPU 使用率是以单核计算的。对于多核 CPU,最大使用率 = 核数 * 100%
-
重载 systemd 配置:
sudo systemctl daemon-reload -
查看 slice 状态
systemctl list-units --type=slice # 查看所有 slice systemctl status user.slice # 查看特定 slice 状态 systemctl show user.slice # 查看 slice 的 cgroup 信息
模拟高 CPU、内存占用进程:
hog.go
package main
import (
"context"
"fmt"
"os/signal"
"runtime"
"sync"
"syscall"
"time"
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
var wg sync.WaitGroup
// Allocate all available CPUs
numCPU := runtime.NumCPU()
runtime.GOMAXPROCS(numCPU)
// Start a busy goroutine for each CPU
for i := 0; i < numCPU; i++ {
go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("Started a CPU hog")
for ctx.Err() == nil { /* Busy loop to keep CPU busy */ }
}()
}
// Start allocating memory every second
const memChunkMB = 10
var chunks [][]byte
go func() {
wg.Add(1)
defer wg.Done()
for ctx.Err() == nil {
chunk := make([]byte, memChunkMB*1024*1024)
// Prevent the memory from being optimized away
for i := 0; i < len(chunk); i++ {
chunk[i] = byte(i % 256)
}
chunks = append(chunks, chunk)
fmt.Printf("Allocated %d MB of memory\n", memChunkMB)
time.Sleep(1 * time.Second)
}
}()
<-ctx.Done()
fmt.Println("Received termination signal. Initiating shutdown...")
wg.Wait()
fmt.Println("Shutdown complete.")
}
参考:Set a default resource limit for all users with systemd cgroups | Unix & Linux Stack Exchange
参见:

浙公网安备 33010602011771号