Slurm 中的控制组

目录

控制组概述

控制组是内核提供的一种机制,用于以分层方式组织进程,并以可控和可配置的方式沿层次结构分配系统资源。Slurm 可以利用 cgroups 来限制作业、步骤和任务的不同资源,并获取这些资源的会计信息。

cgroup 为不同的资源提供不同的控制器(以前称为“子系统”)。Slurm 插件可以使用这些控制器中的多个,例如:memory, cpu, devices, freezer, cpuset, cpuacct。每个启用的控制器都能将资源限制到一组进程。如果系统上没有某个控制器,那么 Slurm 就无法通过 cgroup 限制相关资源。

“cgroup”代表“控制组”,永远不大写。单数形式用于表示整个功能,也用作限定词,如“cgroup 控制器”。当明确指多个单独的控制组时,使用复数形式“cgroups”。

Slurm 支持两种 cgroup 模式,传统模式(cgroup v1)和统一模式(cgroup v2)。不支持在一个系统中混合使用版本 1 和版本 2 的控制器的混合模式。

注意:cgroup/v1 插件已被弃用,将不再支持未来的 Slurm 版本。较新的 GNU/Linux 发行版正在或已经放弃对 cgroup v1 的支持,甚至可能不提供所需的 cgroup v1 接口的内核支持。Systemd 也弃用了 cgroup v1。从 Slurm 版本 25.05 开始,不会向 cgroup v1 添加新功能。对关键错误的支持将持续到其最终移除。

有关 cgroup 的更全面描述,请参阅 kernel.org 文档:

Slurm cgroup 插件设计

有关 Slurm 内部 Cgroup 插件的更多信息,请阅读:

Slurm 中 cgroup 的使用

Slurm 提供了多个插件的 cgroup 版本。

  • proctrack/cgroup(用于进程跟踪和管理)
  • task/cgroup(用于在步骤和任务级别限制资源)
  • jobacct_gather/cgroup(用于收集统计信息)

cgroups 也可以用于资源专用化(将守护进程限制在特定的核心或内存上)。

Slurm Cgroup 配置概述

Slurm cgroups 有几个配置选项集:

  • slurm.conf 提供启用 cgroup 插件的选项。每个插件可以独立于其他插件启用或禁用。
  • cgroup.conf 提供适用于所有 cgroup 插件的通用选项,以及仅适用于特定插件的附加选项。
  • 系统级资源专用化通过节点配置参数启用。

当前可用的 Cgroup 插件

proctrack/cgroup 插件

proctrack/cgroup 插件是其他 proctrack 插件(如 proctrack/linux)用于进程跟踪和挂起/恢复功能的替代方案。

proctrack/cgroup 使用 freezer 控制器来跟踪作业的所有 pid。它基本上将 pid 存储在 cgroup 树中的特定层次结构中,并在指示时负责向这些 pid 发送信号。例如,如果用户决定取消作业,Slurm 将通过调用 proctrack 插件并要求其向作业发送 SIGTERM 来在内部执行此命令。由于 proctrack 在 cgroup 中维护了所有与 Slurm 相关的 pid 的层次结构,因此它很容易知道需要向哪些 pid 发送信号。
Proctrack 还可以响应查询以获取作业或步骤的所有 pid 列表。
或者,当使用 proctrack/linux 时,pid 由 cgroup 存储在单个文件(cgroup.procs)中,插件通过读取该文件来获取层次结构一部分的所有 pid。例如,当使用 proctrack/cgroup 时,单个步骤有自己的 cgroup.procs 文件,因此获取步骤的 pid 是瞬时的。在 proctrack/linux 中,我们需要递归读取 /proc 以获取父 pid 的所有子代。

要启用此插件,请在 slurm.conf 中配置以下选项:

ProctrackType=proctrack/cgroup

此插件在 cgroup.conf 中没有特定选项,但通用选项适用。有关详细信息,请参阅 cgroup.conf 手册页。

task/cgroup 插件

task/cgroup 插件允许将资源限制到作业、步骤或任务。这是唯一可以确保分配边界不被违反的插件。 只有 jobacctgather/linux 提供了一种非常简单的机制来将内存限制到作业,但它不可靠(存在一个时间窗口,作业可以超出其限制),并且仅适用于非常少见的没有 cgroup 的系统。

task/cgroup 提供以下功能:

  • 将作业和步骤限制在其分配的 cpuset 中。
  • 将作业和步骤限制在特定的内存资源中。
  • 将作业、步骤和任务限制在其分配的 gres,包括 gpus。

task/cgroup 插件使用 cpuset、memory 和 devices 子系统。

要启用此插件,请在 slurm.conf 中的 TaskPlugin 配置参数中添加 task/cgroup

TaskPlugin=task/cgroup

此插件在 cgroup.conf 中有许多特定选项。通用选项也适用。有关详细信息,请参阅 cgroup.conf 手册页。

此插件可以与其他任务插件堆叠,例如与 task/affinity。这将允许它将资源限制到作业并获得亲和力插件的优势(顺序无关紧要):

TaskPlugin=task/cgroup,task/affinity

jobacct_gather/cgroup 插件

jobacct_gather/cgroup 插件是用于收集作业、步骤和任务的会计统计信息的 jobacct_gather/linux 插件的替代方案。
jobacct_gather/cgroup 使用 cpuacct 和 memory cgroup 控制器。

此插件收集的 cpu 和内存统计信息与 jobacct_gather/linux 收集的 cpu 和内存统计信息不代表相同的资源。虽然 cgroup 插件只是读取包含整个 pid 子树信息的 cgroup.stats 文件和类似文件,但 linux 插件从 /proc/pid/stat 获取每个 pid 的信息,然后进行计算,因此效率稍低(尽管在实践中不明显)比 cgroup 插件。

要启用此插件,请在 slurm.conf 中配置以下选项:

JobacctGatherType=jobacct_gather/cgroup

此插件在 cgroup.conf 中没有特定选项,但通用选项适用。有关详细信息,请参阅 cgroup.conf 手册页。

使用 cgroup 进行资源专用化

资源专用化可用于在每个计算节点上保留一部分核心或特定数量的内存,供 Slurm 计算节点守护进程 slurmd 独占使用。

如果使用 cgroup/v1,保留的资源也将被 slurmstepd 进程使用。如果使用 cgroup/v2,slurmstepd 不受此资源专用化的限制。相反,slurmstepd 被限制在分配给作业的资源中,因为它被视为作业的一部分,其消耗完全取决于作业的拓扑。例如,一个 MPI 作业可以使用 PMI 初始化许多进程,使 slurmstepd 消耗更多内存。

系统级资源专用化通过特殊的节点配置参数启用。阅读 slurm.confcore_spec.html 中的核心专用化以获取更多信息。

Slurm cgroup 插件

cgroup v1 和 v2 插件在组织其层次结构和响应不同设计约束方面有很大不同。设计由内核维护者负责。

cgroup/v1 和 cgroup/v2 的主要区别

v1 和 v2 之间的三个主要区别是:

  • v2 中的统一模式

    cgroup/v1 中,每个控制器都有一个单独的层次结构,这意味着必须为每个启用的控制器复制和管理作业结构。例如,对于同一个作业,如果使用 memoryfreezer 控制器,我们需要在两个控制器的目录中创建相同的 slurm/uid/job_id/step_id/ 层次结构。例如:

    /sys/fs/cgroup/memory/slurm/uid_1000/job_1/step_0/
    /sys/fs/cgroup/freezer/slurm/uid_1000/job_1/step_0/

    cgroup/v2 中,我们有一个 统一 层次结构,其中控制器在同一级别启用,并以不同的文件形式呈现给用户。

    /sys/fs/cgroup/system.slice/slurmstepd.scope/job_1/step_0/

  • v2 中的自上而下约束

    资源是自上而下分配的,只有当资源从父级分配给它时,cgroup 才能进一步分配资源。启用的控制器列在 cgroup.controllers 文件中,子树中启用的控制器列在 cgroup.subtree_control 中。

  • v2 中的无内部进程约束

    cgroup/v1 中,层次结构是自由的,这意味着可以在树中创建任何目录并将 pid 放入其中。在 cgroup/v2 中,有一个内核限制,阻止将 pid 添加到非叶目录中。

  • systemd 对 cgroup/v2 的依赖 - 分离 slurmd 和 stepds

    这不是内核限制,而是 systemd 的决定,它对决定使用 Delegate=yes 的服务施加了重要限制。systemd,作为 pid 1,决定成为 cgroup 层次结构 /sys/fs/cgroup 的完全所有者,试图实施 单一写入者 设计。这意味着与 cgroup 相关的一切都必须由 systemd 控制。如果决定手动修改 cgroup 树,创建目录并移动 pid,可能会在某个时候 systemd 决定在整个树上启用或禁用控制器,或者移动 pid。已经经历过

    systemd reload
    systemd reset-failed
    在树的任何级别和目录中移除控制器,如果没有任何“systemd 单元”使用它,并且系统上没有任何“Delegate=Yes”启动的“systemd 单元”。这是因为 systemd 想要清理 cgroup 树并将其与其内部单元数据库匹配。事实上,查看 systemd 的代码可以看到,带有“Delegate=yes”标志的单元相关的 cgroup 目录被忽略,而任何其他 cgroup 目录都被修改。这使得必须在带有“Delegate=yes”的单元下启动 slurmd 和 slurmstepd 进程。这意味着我们需要使用 systemd 启动、停止和重启 slurmd。如果我们这样做,由于我们可能之前修改了 slurmd 所属的树(例如,添加作业目录),systemd 将无法重启 slurmd,因为前面提到的 自上而下约束。它将无法将新的 slurmd pid 放入现在是非叶的根 cgroup 中。这迫使我们将 slurmstepd 的 cgroup 层次结构与 slurmd 的分开,并且由于我们需要通知 systemd 并将 slurmstepd 放入一个新单元中,我们将对 systemd 进行 dbus 调用以为 slurmstepd 创建一个新范围。有关更多信息,请参阅 systemd ControlGroupInterface

以下差异不应影响其他插件与 cgroup 插件的交互方式,而是仅显示内部功能差异。

  • cgroup/v2 中,通过写入 cgroup.controllers 启用控制器,而在 cgroup/v1 中,必须使用文件系统类型 "-t cgroup" 和相应的选项(例如 "-o freezer")挂载新的挂载点。
  • cgroup/v2 中,freezer 控制器固有地存在于 cgroup.freeze 接口中。在 cgroup/v1 中,它是一个需要挂载的特定和独立的控制器。
  • devices 控制器在 cgroup/v2 中不存在,而是必须在内核中插入一个新的 eBPF 程序。
  • cgroup/v2 中,memory.stat 文件已更改,现在我们将 anon+swapcached+anon_thp 相加以匹配 v1 中的 RSS 概念。
  • cgroup/v2 中,cpu.stat 提供以毫秒为单位的指标,而 cgroup/v1 中的 puacct.stat 提供以 USER_HZ 为单位的指标。

控制器接口的主要区别

cgroup/v1 cgroup/v2
memory.limit_in_bytes memory.max
memory.soft_limit_in_bytes memory.high
memory.memsw_limit_in_bytes memory.swap.max
memory.swappiness
freezer.state cgroup.freeze
cpuset.cpus cpuset.cpus.effective 和 cpuset.cpus
cpuset.mems cpuset.mems.effective 和 cpuset.mems
cpuacct.stat cpu.stat
device.* ebpf 程序

其他一般性

  • 使用 cgroup/v1 时,某些配置可以排除交换 cgroup 会计。这种会计是内存控制器提供的功能之一。如果从内核或启动参数中禁用此功能,尝试启用交换限制将产生错误。如果需要此功能,请在内核命令行中添加以下参数:
    cgroup_enable=memory swapaccount=1
    这通常可以放在 /etc/default/grub 中的 GRUB_CMDLINE_LINUX 变量中。更新文件后,必须运行 update-grub 命令。此功能也可以在内核配置中通过参数禁用:
    CONFIG_MEMCG_SWAP=
  • 在某些 Linux 发行版中,可以使用 systemd 参数 JoinControllers,但实际上已被弃用。此参数允许多个控制器在 cgroup/v1 中挂载在一个层次结构中,或多或少地尝试模拟 cgroup/v2 中“统一”模式的行为。然而,Slurm 在此配置下无法正常工作,因此请确保您的 system.conf 未使用 JoinControllers,并且在使用 cgroup/v1 传统模式时,所有 cgroup 控制器都在单独的目录下。

最后修改于 2025 年 5 月 5 日