每个进程都被分配一个且仅分配一个管理 cgroup。cgroups 以层次树结构排序。您可以为单个进程或层次树的整个分支设置资源限制,例如 CPU、内存、磁盘 I/O 或网络带宽使用量。
在 openSUSE Leap 上,systemd 使用 cgroups 将所有进程组织到组中,systemd 将其称为 slices。 systemd 还提供了一个接口来设置 cgroup 属性。
命令 systemd-cgls 显示层次树。
内核 cgroup API 有两种变体——v1 和 v2。此外,可能存在多个 cgroup 层次结构,暴露不同的 API。在众多可能的组合中,有两种实际选择
统一:具有 controllers 的 v2 层次结构
混合:没有 controllers 的 v2 层次结构,以及位于 v1 层次结构上的 controllers(已弃用)
默认模式是统一。存在一种混合模式,为需要它的应用程序提供向后兼容性。
您只能设置一种模式。
将进程组织到不同的 cgroups 可以用来获取每个 cgroup 的资源消耗数据。
会计具有相对较小但非零的开销,其影响取决于工作负载。为一个单元激活会计,也隐式地为同一 slice 中的所有单元以及其父 slice 中的所有单元激活它。
可以使用诸如 MemoryAccounting= 之类的指令在每个单元的基础上设置会计,或者使用指令 DefaultMemoryAccounting= 在 /etc/systemd/system.conf 中全局为所有单元设置会计。有关可能的指令的详尽列表,请参阅 man systemd.resource-control。
请注意,资源消耗隐式地取决于您的工作负载执行的环境(例如,库/内核中数据结构的尺寸,实用程序的 fork 行为,计算效率)。因此,如果环境发生变化,建议(重新)校准您的限制。
可以使用 systemctl set-property 命令设置 cgroups 的限制。语法是
#systemctl set-property [--runtime] NAME PROPERTY1=VALUE [PROPERTY2=VALUE]
配置的值会立即应用。可选地,使用 --runtime 选项,以便新的值在重新启动后不会持久存在。
将 NAME 替换为 systemd 服务、scope 或 slice 名称。
有关完整的属性列表和更多详细信息,请参阅 man systemd.resource-control。
systemd 支持为每个单独的 leaf 单元或聚合在 slices 上配置任务计数限制。上游 systemd 附带的默认值限制了每个单元中的任务数量(内核全局限制的 15%,运行 /usr/sbin/sysctl kernel.pid_max 查看总限制)。每个用户的 slice 限制为内核限制的 33%。但是,这对于 openSUSE Leap 而言有所不同。
实践表明,没有一个默认值适用于所有用例。 openSUSE Leap 附带两个自定义配置,这些配置会覆盖系统单元和用户 slice 的上游默认值,并将它们都设置为 infinity。 /usr/lib/systemd/system.conf.d/__20-defaults-SUSE.conf 包含这些行
[Manager] DefaultTasksMax=infinity
/usr/lib/systemd/system/user-.slice.d/10-defaults.conf 包含这些行
[Slice] TasksMax=infinity
使用 systemctl 验证 DefaultTasksMax 值
>systemctl show --property DefaultTasksMaxDefaultTasksMax=infinity
infinity 表示没有限制。更改默认值不是必需的,但设置某些限制可能有助于防止系统因失控进程而崩溃。
通过创建一个新的覆盖文件 /etc/systemd/system.conf.d/90-system-tasksmax.conf 来更改全局 DefaultTasksMax 值,并写入以下行以设置新的默认系统单元限制为 256 个任务
[Manager] DefaultTasksMax=256
加载新设置,然后验证它是否已更改
>sudosystemctl daemon-reload>systemctl show --property DefaultTasksMaxDefaultTasksMax=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。
>sudosystemctl 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 应用新的限制并创建一个 drop-in 文件以实现持久性,/etc/systemd/system/mariadb.service.d/50-TasksMax.conf,其中仅包含您想要应用于现有单元文件的更改。该值不必为 8192,但应为适合您工作负载的任何限制。
用户的默认限制应较高,因为用户会话需要更多资源。通过创建一个新文件,例如 /etc/systemd/system/user-.slice.d/40-user-taskmask.conf,为任何用户设置您自己的默认值。以下示例设置默认值为 16284
[Slice] TasksMax=16284
请参阅 “参考”手册,第 10 章“systemd 守护程序”,第 10.5.3 节“手动创建 drop-in 文件”,以了解 drop-in 文件需要哪些数字前缀。
然后重新加载 systemd 以加载新值,并验证更改
>sudosystemctl daemon-reload>systemctl show --property TasksMax user-1000.sliceTasksMax=16284
您如何知道使用哪些值?这因您的工作负载、系统资源和其他资源配置而异。当您的 TasksMax 值过低时,您可能会看到错误消息,例如 Failed to fork (Resources temporarily unavailable)、Can't create thread to handle new connection 和 Error: Function call 'fork' failed with error code 11, 'Resource temporarily unavailable'。
有关配置 systemd 中系统资源的更多信息,请参阅 systemd.resource-control (5)。
本节介绍使用 Linux 内核的块 I/O 控制器来优先处理或限制 I/O 操作。这利用了 systemd 提供的手段来配置 cgroups,并讨论了在处理比例 I/O 控制时可能遇到的陷阱。
以下子部分描述了您在设计和配置系统时必须采取的步骤,因为这些方面无法在运行时更改。
您应该使用支持 cgroup writeback 的文件系统(否则无法进行 writeback 计费)。推荐的 openSUSE Leap 文件系统在以下上游版本中添加了支持
Btrfs (v4.3)
Ext4 (v4.3)
XFS (v5.3)
截至 openSUSE Leap 15.3,可以使用上述任何文件系统。
节流策略在堆栈中更高层实现,因此不需要进行任何额外的调整。比例 I/O 控制策略有两种不同的实现:BFQ 控制器和基于成本的模型。我们在此描述 BFQ 控制器。要为特定设备施加其比例实现,我们必须确保 BFQ 是所选调度器。检查当前调度器
>cat /sys/class/block/sda/queue/schedulermq-deadline kyber bfq [none]
将调度器切换到 BFQ
#echo bfq > /sys/class/block/sda/queue/scheduler
您必须指定磁盘设备(而不是分区)。设置此属性的最佳方法是特定于设备的 udev 规则。 openSUSE Leap 附带的 udev 规则已经为旋转磁盘驱动器启用了 BFQ。
您可以永久地将值应用于(长期运行的)服务。
>sudosystemctl set-property fast.service IOWeight=400>sudosystemctl set-property slow.service IOWeight=50>sudosystemctl set-property throttled.service IOReadBandwidthMax="/dev/sda 1M"
或者,您可以将 I/O 控制应用于单个命令,例如
>sudosystemd-run --scope -p IOWeight=400 high_prioritized_command>sudosystemd-run --scope -p IOWeight=50 low_prioritized_command>sudosystemd-run --scope -p IOReadBandwidthMax="/dev/sda 1M" dd if=/dev/sda of=/dev/null bs=1M count=10
以下列表项描述了 I/O 控制行为,以及在不同条件下您应该期望的结果。
I/O 控制最适用于直接 I/O 操作(绕过页面缓存),在实际 I/O 与调用者分离的情况下(通常通过页面缓存进行写回),可能会表现出各种情况。例如,延迟的 I/O 控制,甚至没有观察到的 I/O 控制(考虑小的突发或同时发生且永远不会 “相遇,” 同时提交 I/O 的竞争工作负载)。出于这些原因,结果的吞吐量比率并不严格遵循配置权重的比率。
systemd 会缩放配置的权重(以调整较窄的 BFQ 权重范围),因此结果的吞吐量比率也不同。
写回活动取决于脏页的数量,以及全局 sysctl 旋钮(vm.dirty_background_ratio 和 vm.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,但交换(swap-in)和换出(swap-out)也可以被控制。虽然如果出现这种需求,则表明内存配置不当(或内存限制)。
为了在用户会话中应用 cgroup 资源控制,必须将控制器委托给 systemd 的用户实例。 openSUSE Leap 默认配置的 systemd 不会委托任何控制器。
您可以使用 drop-in 文件来更改委托的控制器集合。例如,/etc/systemd/system/user@.service.d/60-delegate.conf 将控制器添加到所有用户,而 /etc/systemd/system/user@uid.service.d/60-delegate.conf 仅将控制器添加到特定用户。该文件的内容应如下所示
[Service] Delegate=pids memory
必须通知 systemd 实例和受影响的用户实例以重新加载新的配置。
>sudosystemctl daemon-reload>systemctl --user daemon-reexec
或者,受影响的用户可以注销并重新登录,而不是第二行来重启他们的用户实例。
内核文档(软件包 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 parts)。
https://lwn.net/Articles/243795/—Corbet, Jonathan: Controlling memory use in containers (2007)。
https://lwn.net/Articles/236038/—Corbet, Jonathan: Process containers (2007)。