Akemi

资源限制ulimit/systemd-cgroupv1/v2

2025/04/25

ulimit

ulimit实际上是一种已经快被淘汰的资源限制工具,因为仅用于一些传统业务的机器的调优,现在都上云了

机制:

  • 进程级限制:ulimit 是传统的 Unix/Linux 资源限制工具,主要针对单个进程或用户会话(如 Shell 启动的进程)设置资源上限,例如文件描述符数量、CPU 时间、内存等。
  • 局限性:其限制是“继承式”的,父进程的限制会传递给子进程,但无法对一组进程进行统一管理。
  • 内核接口:通过 setrlimit 系统调用实现,属于内核的 RLIMIT_* 机制

局限性:

  • 功能单一:ulimit 仅支持少数资源类型(如 nproc 进程数、nofile 文件描述符)
  • 在容器化(Docker、Kubernetes)和微服务场景中,资源隔离需精确到组级别,cgroups 是事实标准,而 ulimit 无法满足需求。
场景 ulimit systemd/cgroups
限制单个进程的文件描述符数 ulimit -n 1024 LimitNOFILE=
限制容器的总内存使用 不适用 docker run --memory=500m
动态调整服务的 CPU 配额 不适用 systemctl set-property
全局用户进程数限制 /etc/security/limits.conf 结合 cgroups 和 pam 模块

配置ulimit

ulimit持久化配置文件/etc/security/limits.conf
或子配置文件/etc/security/limits.d/

1
2
3
4
5
6
7
8
9
10
11
12
# 查看当前所有ulimit
ulimit -a

# 持久化配置
vim /etc/security/limits.conf
#<domain> <type> <item> <value>

# 限制所有用户的打开文件数为 8192
* hard nofile 8192

# 限制用户 "appuser" 的最大进程数为 1024
appuser hard nproc 1024

cgroup v1

cgroups(Control Groups)是 Linux 内核提供的一种机制,用于对进程或进程组进行资源限制、优先级分配、资源统计和任务控制。它是现代 Linux 系统资源管理的核心组件(尤其在容器化技术中广泛应用)。

核心功能

(1) 资源限制

限制进程组使用的资源总量

  • CPU 时间片(cpu)
  • 内存使用量(memory)
  • 磁盘 I/O 带宽(blkio)
  • 网络带宽(net_cls、net_prio)
  • 进程数(pids)

(2) 优先级控制

  • 分配不同进程组的资源使用权重(如 CPU 优先级)。

(3) 资源统计

  • 监控进程组的资源使用情况(如内存占用、CPU 时间)。

(4) 任务控制

  • 冻结、恢复或终止一组进程(freezer)。

核心组件

控制器

每种资源:CPU/MM/IO等对应一个控制器

层级结构-树状结构

1
2
3
根目录:       /sys/fs/cgroup
层级目录如CPU:/sys/fs/cgroup/cpu
控制组目录 /sys/fs/cgroup/cpu/mygroup

控制组/任务

任务即进程,是cgroup资源管理的直接对象

控制组是进程的集合,用于关联控制器并应用资源管理策略

也就是控制组是任务的集合

cgroup文件系统

/sys/fs/cgroup

核心文件包括:

配置文件

  • cgroup.procs:列出cgroup中的进程PID。
  • cgroup.controllers:显示可用的控制器。
  • memory.max:设置内存使用上限。
  • cpu.max:配置CPU时间分配

监控文件

  • cpu.stat:统计CPU使用情况
  • memory.usage_in_bytes:显示当前内存使用量。

操作示例

  • 限制CPU使用:echo "50000 100000" > /sys/fs/cgroup/cpu/mygroup/cpu.max
  • 添加进程:echo 1234 > /sys/fs/cgroup/memory/mygroup/cgroup.procs

工作流程

  1. 创建层级:挂载关联CPU和内存控制器的层级。

    1
    2
    3
    4
    bash
    mount -t cgroup -o cpu,memory none /sys/fs/cgroup/wangsheng

    这就是一个控制器,叫wangsheng
  2. 创建控制组:在层级下创建子cgroup。

    1
    2
    3
    4
    5
    bash
    mkdir /sys/fs/cgroup/wangsheng/ws

    这是一个工作组ws
    隶属于wangsheng控制器的
  3. 配置资源限制:设置CPU配额

    1
    2
    3
    4
    bash
    echo "20000 100000" > /sys/fs/cgroup/myhierarchy/cgroup/wangsheng/ws

    添加一个规则
  4. 添加进程:将PID为1234的进程加入cgroup。

    1
    2
    3
    4
    5
    bash
    echo 1234 > /sys/fs/cgroup/cgroup/wangsheng/ws/cgroup.procs

    现在1234这个进程就属于wangsheng控制器下的ws控制组了
    该组下的进程,都遵循上面的资源限制

cgroupv2

v1的局限性

如果你已经看懂了cgroup的工作流程
那么就很容易知道,在多个控制器的工作组下,一定会有cgroup.procs文件中
含有相同的PID,就会增加配置的复杂性

特性 cgroup v1 cgroup v2
层级结构 多层级树结构,每个控制器(如 CPU、Memory)独立挂载 单一层级树结构,所有控制器统一挂载到 /sys/fs/cgroup
控制粒度 控制器可独立配置,但可能导致规则冲突 统一控制模型,避免策略冲突
进程归属 一个进程可属于多个不同控制器的 cgroup 一个进程只能属于一个 cgroup(所有控制器共享同一层级)

v2的优越性

主要是层级结构的不同

也就是,现在同一个控制组中,可以直接定义规则,而不是通过控制器进行分类

1
2
3
4
5
6
# 内存和 CPU 限制(v2)
echo 500M > /sys/fs/cgroup/myapp/memory.max
echo "50000 100000" > /sys/fs/cgroup/myapp/cpu.max # 50% CPU
echo 2000 > /sys/fs/cgroup/myapp/cpu.weight # 权重值

在下面还可以定义新的子控制组

限制容器内存

  • v1:需分别配置 memory.limit_in_bytes 和 memory.swappiness
  • v2:直接设置 memory.max 和 memory.swap.max,且支持更精细的内存回收策略。

场景 2:CPU 配额分配

  • v1:通过 cpu.shares(相对权重)或 cpu.cfs_quota_us(绝对配额)分配。
  • v2:统一使用 cpu.max 文件

cgroup与systemd

cgroups 和 systemd 在现代 Linux 系统中密切相关。systemd 是 cgroups 的主要管理者和使用者,它利用 cgroups 实现服务资源隔离、进程跟踪和资源限制

slice切片

相当于逻辑组group的概念 

systemd 预定义了三个核心slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
system.slice 所有系统服务的默认分组
user.slice 所有用户会话和用户级服务
init.scope systemd 自身进程所在的 cgroup(PID 1

machine.slice (非顶级) 用于虚拟化或容器环境(如虚拟机、容器引擎)中资源的统一管理

查看slices层级
systemd-cgls
Control group /:
-.slice
├─user.slice (#1225)
│ → user.invocation_id: d8b0161e25574bd0b1945c80211fc22a
│ └─user-0.slice (#317167)
│ → trusted.invocation_id: c43adc6a551a4b5a9b55ba6118726d12
│ ├─session-107.scope (#317426)
├─init.scope (#22)
│ └─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 31
└─system.slice (#59)
├─irqbalance.service (#3152)
│ → user.invocation_id: 7b81fa3ef1aa448498559131fd131f51
│ → trusted.invocation_id: 7b81fa3ef1aa448498559131fd131f51
│ └─867 /usr/sbin/irqbalance
│ ├─init.scope (#256788)
│ │ └─1088268 /sbin/init
│ ├─system.slice (#257142)
│ │ ├─containerd.service … (#258040)

# 查看cgroup资源使用
systemd-cgtop
Control Group Tasks %CPU Memory Input/s Output/s
/ 1395 10.0 5.9G 0B 21.7K
system.slice 1201 6.1 6.7G 0B 19.2K
system.slice/docker…8ad1d666d904588067db8ad0897192ca.scope 344 2.7 1.0G - -
system.slice/docker…d904588067db8ad0897192ca.scope/kubelet 179 1.5 741.9M - -
system.slice/docker…a1cabc5944982da13b8c3693c3a45498.scope 119 1.4 253.4M - -
system.slice/docker…da13b8c3693c3a45498.scope/system.slice 84 1.3 204.8M - -
system.slice/docker…88067db8ad0897192ca.scope/system.slice 164 1.3 305.2M - -
user.slice 6 1.1 5.1G - -

创建自定义slice

子slice命名规则

子slices名称需要带有父slice的名称,通过连字符分隔,

system.slice 的子 slice → system-mycustom.slice

且子slice不能超过父slice的限额

创建文件

**存放目录(自定义)/etc/systemd/system/ ****

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建slice
vim /etc/systemd/system/myapp.slice
[Slice]

systemctl daemon-reload
# 查看slice状态
systemctl status myapp.slice
○ myapp.slice - Slice /myapp
Loaded: loaded (/etc/systemd/system/myapp.slice; static)
Active: inactive (dead)

# 将进程关联到自定义 slice
# 测试 使用systemd-run临时运行进程
systemd-run --slice=myapp.slice --unit=myapp.service /path/to/your/command

# 修改现有服务的配置文件,添加配置
systemctl edit nginx.service
添加
[Service]
Slice=myapp.slice

sudo systemctl restart nginx.service

定义规则

可定义多种块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]块
所有 systemd 单元(包括 slice)的通用配置块,用于定义单元的元信息、依赖关系和启动顺序
不赘述

[Slice]块(必选)
slice 单元的核心配置块,用于定义资源控制
CPUQuota 限制 CPU 使用百分比 CPUQuota=50%
MemoryMax 限制最大内存使用量 MemoryMax=2G
IOWeight 设置块设备 IO 权重 IOWeight=500
TasksMax 限制 slice 内最大进程/线程数 TasksMax=100

[Install]块
定义单元的安装信息(依赖关系)
WantedBy 指定依赖当前 slice 的单元或目标 WantedBy=multi-user.target
RequiredBy 强制要求当前 slice 的单元或目标 RequiredBy=mariadb.service

开启审计

1
2
3
4
5
6
7
8
9
[Slice]
MemoryAccounting=true # Slice 启用统计
MemoryMax=1G # 内存限制

# myapp.service
[Service]
ExecStart=/usr/bin/myapp
Slice=myapp.slice
MemoryAccounting=true # Service 启用审计
CATALOG
  1. 1. ulimit
    1. 1.1. 配置ulimit
  2. 2. cgroup v1
    1. 2.1. 核心功能
    2. 2.2. 核心组件
      1. 2.2.1. 控制器
      2. 2.2.2. 层级结构-树状结构
      3. 2.2.3. 控制组/任务
      4. 2.2.4. cgroup文件系统
    3. 2.3. 工作流程
  3. 3. cgroupv2
    1. 3.1. v1的局限性
    2. 3.2. v2的优越性
  4. 4. cgroup与systemd
    1. 4.1. slice切片
    2. 4.2. 创建自定义slice
      1. 4.2.1. 创建文件
      2. 4.2.2. 定义规则
      3. 4.2.3. 开启审计