强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

systemd 教程 / Slice 与资源控制(cgroups)

Slice 与资源控制(cgroups)

概述

systemd 通过 Linux cgroups(控制组)实现对服务的资源限制和隔离。Slice(切片)是 systemd 管理 cgroups 层次结构的机制,可以对一组服务统一设置资源上限。在生产环境中,合理配置资源限制是保障系统稳定性的关键。


cgroups v2 概述

cgroups 简介

cgroups(Control Groups)是 Linux 内核提供的资源管理机制,可以限制、统计和隔离进程组的资源使用(CPU、内存、IO 等)。

cgroups v1 vs v2

对比项cgroups v1cgroups v2
层次结构多棵树(每个控制器独立)单一统一层次
挂载点/sys/fs/cgroup/*//sys/fs/cgroup/
控制器各自独立管理统一管理
委托支持有限完整支持
PSI 支持不支持支持(Pressure Stall Information)
推荐版本旧系统新系统推荐

检查当前 cgroups 版本

# 检查 cgroups 版本
stat -fc %T /sys/fs/cgroup/
# 输出 cgroup2fs 表示 v2

# 查看已挂载的 cgroups
mount | grep cgroup

# 查看内核支持
cat /proc/filesystems | grep cgroup

systemd Slice 层次结构

默认层次

/ (root slice)
├── system.slice    — 系统服务
├── user.slice      — 用户会话和用户服务
└── machine.slice   — 虚拟机和容器

查看 cgroups 树

# 查看 cgroups 树
systemd-cgls

# 输出示例:
# Control group /:
# -.slice
# ├─user.slice
# │ └─user-1000.slice
# │   └─session-1.scope
# │     └─1234 /bin/bash
# ├─system.slice
# │ ├─nginx.service
# │ │ └─5678 nginx: worker process
# │ ├─mysql.service
# │ │ └─9012 /usr/sbin/mysqld
# │ └─sshd.service
# │   └─3456 sshd: user [priv]
# └─machine.slice

自定义 Slice

# /etc/systemd/system/custom-apps.slice
[Unit]
Description=Custom Applications Slice

[Slice]
# 在此设置该 slice 的资源限制
CPUQuota=200%
MemoryMax=2G

CPU 资源限制

CPUQuota — CPU 时间配额

限制服务可使用的最大 CPU 时间百分比:

# /etc/systemd/system/myapp.service
[Service]
Type=simple
ExecStart=/opt/myapp/bin/server
# 限制最多使用 1.5 个 CPU 核心
CPUQuota=150%
配置值含义
50%最多使用半个 CPU 核心
100%最多使用 1 个 CPU 核心
200%最多使用 2 个 CPU 核心
400%最多使用 4 个 CPU 核心(4 核系统)

CPUWeight — CPU 权重

当 CPU 资源竞争时,按权重分配(默认值 100):

[Service]
# 高优先级服务
CPUWeight=200
场景CPUWeight效果
高优先级200获得 2 倍于默认的 CPU 时间
默认100标准分配
低优先级50获得一半于默认的 CPU 时间
后台任务10仅在空闲时使用 CPU

CPUSet — CPU 亲和性

将服务绑定到指定的 CPU 核心:

[Service]
# 绑定到 CPU 0 和 CPU 1
AllowedCPUs=0 1

💡 提示:对于延迟敏感的服务,使用 AllowedCPUs 绑定核心可以减少 CPU 缓存抖动。


内存资源限制

MemoryMax — 最大内存

超过限制时,OOM killer 会终止进程:

[Service]
ExecStart=/usr/sbin/mysqld
# 最大内存 4GB
MemoryMax=4G

MemoryHigh — 内存软限制

超过限制时,进程会被节流(throttle),但不会被杀死:

[Service]
ExecStart=/usr/sbin/mysqld
# 软限制 3GB
MemoryHigh=3G
# 硬限制 4GB
MemoryMax=4G

MemoryMin — 内存最低保障

在内存竞争时,保证至少获得指定内存:

[Service]
# 保证至少 512MB
MemoryMin=512M

内存限制层次

MemoryMin (最低保障) ─── MemoryLow (低水位) ─── MemoryHigh (软限制) ─── MemoryMax (硬限制)
参数作用超出后果
MemoryMin最低保障其他服务被回收
MemoryLow低水位可被回收
MemoryHigh软限制进程被节流
MemoryMax硬限制触发 OOM killer
MemorySwapMaxswap 限制swap 空间用完后可能 OOM

⚠️ 注意MemoryMax 设置过低会导致服务被 OOM killer 终止。建议先设置 MemoryHigh,再设置合理的 MemoryMax

查看内存使用

# 查看服务的内存使用
systemctl status mysql.service
# 或
systemd-cgtop

# 查看详细内存统计
cat /sys/fs/cgroup/system.slice/mysql.service/memory.current
cat /sys/fs/cgroup/system.slice/mysql.service/memory.stat

IO 资源限制

IOWeight — IO 权重

控制块设备 IO 的优先级(默认 100):

[Service]
# 后台备份服务,降低 IO 优先级
IOWeight=50

IOReadBandwidthMax — 读取带宽限制

[Service]
# 限制读取带宽为 50MB/s
IOReadBandwidthMax=/dev/sda 50M
# 限制写入带宽为 30MB/s
IOWriteBandwidthMax=/dev/sda 30M

IOReadIOPSMax — IOPS 限制

[Service]
# 限制读取 IOPS
IOReadIOPSMax=/dev/sda 1000
# 限制写入 IOPS
IOWriteIOPSMax=/dev/sda 500

💡 提示:对于需要限制磁盘 IO 的服务(如备份、日志归档),IO 限制可以有效避免影响其他关键服务。


任务数限制

TasksMax

限制服务可创建的最大进程/线程数:

[Service]
# 最多 512 个任务
TasksMax=512

⚠️ 注意:如果服务创建的线程或子进程过多,可能耗尽系统 PID 资源。TasksMax 可以防止 fork 炸弹。

查看系统级限制

# 查看默认 TasksMax
systemctl show -p DefaultTasksMax

# 查看服务当前任务数
systemctl show mysql.service -p TasksCurrent
systemctl show mysql.service -p TasksMax

systemd-cgtop 实时监控

systemd-cgtop 类似 top,实时显示各 cgroup 的资源使用:

# 基本使用
systemd-cgtop

# 输出示例:
# Control Group                    Tasks   %CPU   Memory  Input/s Output/s
# /                                   150   25.3    2.1G    1.2M    3.4M
# /system.slice                        45   12.1    1.5G    800K    2.1M
# /system.slice/mysql.service          15    8.2    800M    500K    1.2M
# /system.slice/nginx.service           8    2.1    120M    200K    800K
# /user.slice                          20    3.5    400M    100K    300K

# 按内存排序
systemd-cgtop -m

# 只显示一次(不刷新)
systemd-cgtop -n 1 --no-pager

指定列

# 只显示 CPU 和内存
systemd-cgtop -O

# 按路径排序
systemd-cgtop -p

资源限制实战

案例 1:限制 MySQL 内存使用

# /etc/systemd/system/mysql.service.d/memory-limit.conf
[Service]
# 内存软限制 6GB
MemoryHigh=6G
# 内存硬限制 8GB
MemoryMax=8G
# 禁止使用 swap
MemorySwapMax=0
# CPU 限制
CPUQuota=300%
# 任务数限制
TasksMax=512
# 应用配置
sudo systemctl daemon-reload
sudo systemctl restart mysql

# 验证
systemctl show mysql.service -p MemoryMax,MemoryHigh

案例 2:限制 Redis 内存

# /etc/systemd/system/redis.service.d/limits.conf
[Service]
MemoryMax=2G
MemoryHigh=1536M
CPUQuota=100%
TasksMax=256

案例 3:后台备份任务

# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh

# 低 CPU 优先级
CPUWeight=20
# 内存限制
MemoryMax=1G
# IO 限速
IOWeight=20
IOReadBandwidthMax=/dev/sda 20M
IOWriteBandwidthMax=/dev/sda 20M
# 任务数限制
TasksMax=64

# 使用低优先级的 nice 值
Nice=19
IOSchedulingClass=idle

案例 4:使用 Slice 统一管理

# /etc/systemd/system/production.slice
[Unit]
Description=Production Services Slice

[Slice]
CPUQuota=800%
MemoryMax=16G
MemoryHigh=12G
TasksMax=4096
# /etc/systemd/system/development.slice
[Unit]
Description=Development Services Slice

[Slice]
CPUQuota=200%
MemoryMax=4G
MemoryHigh=3G
TasksMax=512
# 将 MySQL 放入 production.slice
# /etc/systemd/system/mysql.service.d/slice.conf
[Service]
Slice=production.slice

cgroup 委托(Delegation)

委托允许非特权用户管理自己的 cgroup 子树:

# /etc/systemd/system/container.slice
[Unit]
Description=Container Slice

[Slice]
Delegate=yes
# 委托所有控制器
Delegate=cpu memory io

用户服务委托

# ~/.config/systemd/user/myapp.slice
[Unit]
Description=My App Slice

[Slice]
Delegate=cpu memory
MemoryMax=2G
CPUQuota=200%

💡 提示:对于容器运行时(如 Podman、Docker),委托 cgroup 是必要的,这样容器可以管理自己的子 cgroup。


Drop-in 文件技巧

使用 drop-in 文件覆盖默认资源限制,而不是修改原始单元文件:

# 创建 drop-in 文件
sudo systemctl edit mysql.service

在编辑器中添加:

[Service]
MemoryMax=8G
MemoryHigh=6G
CPUQuota=300%
# 查看生成的 drop-in 文件
systemctl cat mysql.service

# 重载并重启
sudo systemctl daemon-reload
sudo systemctl restart mysql

常用命令汇总

# 查看 cgroups 树
systemd-cgls

# 实时监控资源使用
systemd-cgtop

# 查看服务的资源限制
systemctl show mysql.service -p MemoryMax,MemoryHigh,CPUQuota,CPUWeight

# 查看服务当前资源使用
systemctl status mysql.service

# 查看所有 slice
systemctl list-units --type=slice

# 设置运行时资源限制(不修改文件)
sudo systemctl set-property mysql.service MemoryMax=8G

扩展阅读