跳到内容跳到页面导航:上一页 [access key p]/下一页 [access key n]
documentation.suse.com / SUSE Linux Enterprise Server 文档 / 系统分析和微调指南 / 资源管理 / 内核控制组
适用范围 SUSE Linux Enterprise Server 15 SP4

9 内核控制组

内核控制组 (cgroup) 是一种内核功能,可用于指派和限制进程的硬件与系统资源。还能以层次树状结构组织进程。

9.1 概述

为每个进程刚好指派一个管理 cgroup。cgroup 在层次树状结构中按序列出。您可针对单个进程或针对层次树状结构中的整条分支设置资源限制,例如 CPU、内存、磁盘 I/O 或网络带宽用量。

SUSE Linux Enterprise Server 上,systemd 使用 cgroup 以分组(systemd 称之为切片)形式组织所有进程。systemd 还提供一个界面用于设置 cgroup 属性。

命令 systemd-cgls 显示层次树状结构。

内核 cgroup API 有两个版本:v1 和 v2。此外,可能有多个 cgroup 层次结构公开不同的 API。从众多可能的组合来看,有两种切实可行的选择:

  • 混合:不包含控制器的 v2 层次结构;控制器位于 v1 层次结构中

  • 统一:包含控制器的 v2 层次结构

当前的默认模式是混合模式(从 SLE SP3 开始)。这可以与需要混合模式的应用程序保持向后兼容。以下功能仅适用于统一 v2 层次结构:

  • 内存控制器:回收保护(又名为 memory.low)、memory.high、PSI(压力失速信息)

  • io 控制器:写回控制,新的控制策略

  • 将控制器委托给非特权用户(无根容器)

  • systemd 中的冻结器支持

  • 更方便地处理单个层次结构

您只能设置一种模式。

要启用统一控制组层次结构,请将 systemd.unified_cgroup_hierarchy=1 作为内核命令行参数追加到 GRUB 2 引导加载器。(有关配置 GRUB 2 的更多细节,请参见第 18 章 “引导加载程序 GRUB 2。)

9.2 资源统计

可以通过将进程组织到不同的 cgroup 来获取每个 cgroup 的资源消耗数据。

统计进程相对较小,但开销并非为零,它所造成的影响取决于工作负载。对一个单元激活统计也会对同一切片中的所有单元、该切片的所有父切片,以及包含在这些父切片中的单元隐式激活统计。

可以使用诸如 MemoryAccounting= 的指令为每个单元设置统计,或者在 /etc/systemd/system.conf 中使用 DefaultMemoryAccounting= 指令为所有单元全局设置统计。有关可用指令的详尽列表,请参见 man systemd.resource-control

9.3 设置资源限制

注意
注意:隐式资源消耗

请注意,资源消耗隐式取决于工作负载的执行环境(例如,库/内核中数据结构的大小、实用程序的派生行为、计算效率)。因此,如果环境发生变化,建议您(重新)校准您的限制。

可以使用 systemctl set-property 命令设置 cgroup 的限制。语法是:

# systemctl set-property [--runtime] NAME PROPERTY1=VALUE [PROPERTY2=VALUE]

配置的值会立即应用。或者,也使用 --runtime 选项,使新值在重引导后不会保留。

请将 NAME 替换为 systemd 服务、范围或切片名称。

有关完整的属性列表和更多细节,请参见 man systemd.resource-control

9.4 使用 TasksMax 防止派生炸弹

systemd 支持为每个叶单元配置任务计数限制,或配置各个切片的聚合限制。上游 systemd 随附了用于限制每个单元中的任务数量的默认值(内核全局限制的 15%,运行 /usr/sbin/sysctl kernel.pid_max 可查看总限制)。每个用户的切片限制为内核限制的 33%。但是,对于 SLE,情况并非如此。

9.4.1 查找当前的默认 TasksMax

显然,在实践中,单一默认值并不能适用于所有用例。SUSE Linux Enterprise Server 随附了两个自定义配置用于覆盖系统单元和用户片的上游默认值,并将两者都设置为 infinity/usr/lib/systemd/system.conf.d/__25-defaults-SLE.conf 包含以下行:

[Manager]
DefaultTasksMax=infinity

/usr/lib/systemd/system/user-.slice.d/25-defaults-SLE.conf 包含以下行:

[Slice]
TasksMax=infinity

使用 systemctl 校验 DefaultTasksMax 值:

> systemctl show --property DefaultTasksMax
DefaultTasksMax=infinity

infinity 表示没有限制。并不要求一定要更改默认值,但设置一些限制可能有助于防止失控进程导致系统崩溃。

9.4.2 覆盖 DefaultTasksMax

通过创建新的覆盖文件 /etc/systemd/system.conf.d/90-system-tasksmax.conf 更改全局 DefaultTasksMax 值,并写入下面几行以便为每个系统单元设置新的默认任务数限制(256 个):

[Manager]
DefaultTasksMax=256

装载新设置,然后校验设置是否已更改:

> sudo systemctl daemon-reload
> systemctl show --property DefaultTasksMax
DefaultTasksMax=256

根据您的需要调整此默认值。可根据需要在单个服务上设置不同的限制。本示例适用于 MariaDB。首先检查当前活动值:

> systemctl status mariadb.service
  ● mariadb.service - MariaDB database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset>
   Active: active (running) since Tue 2020-05-26 14:15:03 PDT; 27min ago
     Docs: man:mysqld(8)
           https://mariadb.com/kb/en/library/systemd/
 Main PID: 11845 (mysqld)
   Status: "Taking your SQL requests now..."
    Tasks: 30 (limit: 256)
   CGroup: /system.slice/mariadb.service
           └─11845 /usr/sbin/mysqld --defaults-file=/etc/my.cnf --user=mysql

Tasks 行显示 MariaDB 中当前有 30 个任务正在运行,而默认上限为 256,这对于数据库而言并不足够。以下示例演示如何将 MariaDB 的限制提高至 8192。

> sudo systemctl set-property mariadb.service TasksMax=8192
> systemctl status mariadb.service
● mariadb.service - MariaDB database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disab>
  Drop-In: /etc/systemd/system/mariadb.service.d
           └─50-TasksMax.conf
   Active: active (running) since Tue 2020-06-02 17:57:48 PDT; 7min ago
     Docs: man:mysqld(8)
           https://mariadb.com/kb/en/library/systemd/
  Process: 3446 ExecStartPre=/usr/lib/mysql/mysql-systemd-helper upgrade (code=exited, sta>
  Process: 3440 ExecStartPre=/usr/lib/mysql/mysql-systemd-helper install (code=exited, sta>
 Main PID: 3452 (mysqld)
   Status: "Taking your SQL requests now..."
    Tasks: 30 (limit: 8192)
   CGroup: /system.slice/mariadb.service
           └─3452 /usr/sbin/mysqld --defaults-file=/etc/my.cnf --user=mysql

systemctl set-property 应用新限制,并创建一个可持久保存的插入式文件 /etc/systemd/system/mariadb.service.d/50-TasksMax.conf,其中仅包含您要应用于现有单元文件的更改。值不一定必须是 8192,但应该是适合您工作负载的限制。

9.4.3 针对用户的默认 TasksMax 限制

针对用户的默认限制应相当高,因为用户会话需要更多的资源。通过创建新文件(例如 /etc/systemd/system/user-.slice.d/40-user-taskmask.conf),针对用户设置您自己的默认值。以下示例设置的默认值为 16284:

[Slice]
TasksMax=16284
注意
注意:数字前缀参考

请参见 https://documentation.suse.com/sles/15-SP3/html/SLES-all/cha-systemd.html#sec-boot-systemd-custom-drop-in 来了解插入式文件所需的数字前缀。

然后重新装载 systemd 以装载新值,并校验更改:

> sudo systemctl daemon-reload
> systemctl show --property TasksMax user-1000.slice
TasksMax=16284

如何知道要使用哪些值?根据您的工作负载、系统资源和其他资源配置而定。如果 TasksMax 值太小,您会看到诸如无法派生(资源暂时不可用)无法创建用于处理新连接的线程,以及错误:函数调用“fork”失败,错误代码 11“资源暂时不可用”等错误消息。

有关在 systemd 中配置系统资源的详细信息,请参见 systemd.resource-control (5)

9.5 使用 cgroup 进行 I/O 控制

本节介绍如何使用 Linux 内核的块 I/O 控制器来指定 I/O 操作的优先级或限制此类操作。本节将利用 systemd 提供的方法来配置 cgroup,并讨论在处理按比例 I/O 控制时可能存在的陷阱。

9.5.1 先决条件

以下小节介绍了在设计和配置系统时必须提前采取的措施,因为在运行时期间无法更改这方面的设置。

9.5.1.1 文件系统

应该使用 cgroup 写回感知的文件系统(否则无法计算写回费用)。建议的 SLES 文件系统新增了对下述上游版本的支持:

  • Btrfs (v4.3)

  • Ext4 (v4.3)

  • XFS (v5.3)

SUSE Linux Enterprise Server 15 SP3 开始,可以使用任何命名文件系统。

9.5.1.2 统一 cgroup 层次结构

要正确统计写回 I/O,必须具有相同的 I/O 和内存控制器 cgroup 层次结构,并使用 cgroup v2 I/O 控制器。总而言之,这意味着必须使用统一 cgroup 层次结构。必须通过在 SLES 中传递内核命令行选项 systemd.unified_cgroup_hierarchy=1 来显式请求此模式。

9.5.1.3 块 I/O 调度程序

限制策略在堆栈中的更高级别实施,因此不需要进行任何额外的调整。按比例 I/O 控制策略有两种不同的实施方式:BFQ 控制器和基于成本的模型。本节介绍 BFQ 控制器。要对特定的设备按比例实施该策略,我们必须确保 BFQ 是所选的调度程序。检查当前调度程序:

> cat /sys/class/block/sda/queue/scheduler
mq-deadline kyber bfq [none]

将调度程序切换到 BFQ:

 # echo bfq > /sys/class/block/sda/queue/scheduler

必须指定磁盘设备(而不是分区)。设置此属性的最佳方式是创建特定于设备的 udev 规则(请注意,SLES 随附的 udev 规则已经为旋转式磁盘驱动器启用了 BFQ)。

9.5.1.4 Cgroup 层次结构布局

一般情况下,所有任务都驻留在根 cgroup 中,它们会相互竞争。将任务分发到 cgroup 树中时,竞争只在同级 cgroup 之间发生。这一点适用于按比例 I/O 控制;限制会按层次结构聚合所有后代的吞吐量(参见下图)。

r
`-  a      IOWeight=100
    `- [c] IOWeight=300
    `-  d  IOWeight=100
`- [b]     IOWeight=200

I/O 仅源自 cgroup c 和 b。即使 c 的权重更高,也会以更低的优先级来处理它,因为它与 b 之间存在级别竞争。

9.5.2 配置控制数量

可将这些值永久应用于(长时间运行的)服务。

> sudo systemctl set-property fast.service IOWeight=400
> sudo systemctl set-property slow.service IOWeight=50
> sudo systemctl set-property throttled.service IOReadBandwidthMax="/dev/sda 1M"

或者,可将 I/O 控制应用于单个命令,例如:

> sudo systemd-run --scope -p IOWeight=400 high_prioritized_command
> sudo systemd-run --scope -p IOWeight=50 low_prioritized_command
> sudo systemd-run --scope -p IOReadBandwidthMax="/dev/sda 1M" dd if=/dev/sda of=/dev/null bs=1M count=10

9.5.3 I/O 控制行为和设置预期

以下列表项目描述了 I/O 控制行为,以及各种条件下的预期结果。

  • I/O 控制最适合用于直接 I/O 操作(绕过页面缓存),实际 I/O 与调用方解耦的情况(通常会通过页面缓存写回)可能以各种方式表现出来。例如,I/O 控制发生延迟,甚至观察不到 I/O 控制(例如,在极少出现突发或竞争性工作负载碰巧永不“相遇”、同时提交 I/O 以及带宽饱和的情况下)。出于这些原因,最终的 I/O 吞吐量比率并不严格遵守配置权重的比率。

  • systemd 执行配置权重的缩放(以根据较窄的 BFQ 权重范围进行调整),因此最终的吞吐量比率也不相同。

  • 除了全局 sysctl 句柄之外,写回活动还取决于未写入页的数量(vm.dirty_background_ratiovm.dirty_ratio)。当未写入页限制分布在 cgroup 之间时,各个 cgroup 的内存限制就会发挥作用,从而可能影响相关 cgroup 的 I/O 强度。

  • 并非所有储存都是相等的。I/O 控制发生在 I/O 调度程序层,这会对堆叠在不执行实际调度的储存上的设备的设置产生影响。以跨越多个物理设备、MD RAID 甚至 Btrfs RAID 的设备映射器逻辑卷为例。对此类设置进行 I/O 控制可能颇有难度。

  • 没有单独的设置用于对读取和写入进行按比例 I/O 控制。

  • 按比例 I/O 控制只是能够彼此交互的策略之一(但负责任的资源设计可能会避免这种交互)。

  • I/O 设备带宽不是 I/O 路径中唯一的共享资源。这涉及到全局文件系统结构,当 I/O 控制旨在保证特定的带宽时,就要考虑到此因素;但这种控制不能保证带宽,甚至可能导致优先级反转(优先的 cgroup 会等待较慢 cgroup 的事务)。

  • 到目前为止,我们一直都在讨论文件系统数据的显式 I/O,但换入和换出也可以控制。不过,如果出现这种需求,系统通常会指出内存供应不当(或内存限制)。

9.6 更多信息

  • 内核文档(软件包 kernel-source):/usr/src/linux/Documentation/admin-guide/cgroup-v1 中的文件,以及文件 /usr/src/linux/Documentation/admin-guide/cgroup-v2.rst

  • man systemd.resource-control

  • https://lwn.net/Articles/604609/ — Brown, Neil:Control Groups Series(控制组系列,发布于 2014 年,包含 7 个部分)。

  • https://lwn.net/Articles/243795/ — Corbet, Jonathan:Controlling memory use in containers(控制容器中的内存使用,发布于 2007 年)。

  • https://lwn.net/Articles/236038/ — Corbet, Jonathan:Process containers(进程容器,发布于 2007 年)。