YARN 底层是如何实现 CPU 和内存资源的物理隔离的?
YARN(Yet Another Resource Negotiator)在底层实现 CPU 和内存资源的物理隔离,核心是依赖于 Linux 操作系统的内核特性:Cgroups(Control Groups)。
如果不启用 Cgroups,YARN 默认只能做到“逻辑隔离”(基于轮询监控的软限制),而无法做到真正的物理隔离。为了实现真正的物理隔离,YARN 必须配合 LinuxContainerExecutor(LCE)来调用底层系统的 Cgroups。
以下是 YARN 底层实现 CPU 和内存物理隔离的详细机制:
一、 核心基础:Linux Cgroups 与 LCE
要理解底层隔离,首先需要知道两个关键组件:
- Cgroups (Control Groups):Linux 内核提供的一种机制,可以限制、记录、隔离进程组(process groups)所使用的物理资源(CPU、内存、I/O 等)。
- LinuxContainerExecutor (LCE):YARN NodeManager 中的一个执行器。YARN 默认使用
DefaultContainerExecutor,它无法做到真正的资源隔离。只有配置为 LCE 时,NodeManager 才会通过一个底层的 C 语言编写的特权程序(container-executor)去操作系统层面创建 Cgroups 目录,并将 Container 的进程 PID 放入对应的 Cgroup 中。
二、 内存资源的隔离实现
内存的隔离在 YARN 中分为“逻辑监控”和“物理隔离”两层防御。
1. 物理隔离(基于 Cgroups)
当开启 Cgroups 的内存隔离后,底层实现如下:
- 资源分配:NodeManager 启动一个 Container 时,LCE 会在 Linux 的
/sys/fs/cgroup/memory/hadoop-yarn/目录下为该 Container 创建一个专属的子目录。 - 参数设置:YARN 会将该 Container 申请的内存大小(例如 1GB)写入到该目录下的
memory.limit_in_bytes文件中。 - 物理限制:一旦该 Container 内的进程(包括其产生的子进程)使用的物理内存(RSS)超过了
limit_in_bytes的值,Linux 内核的 OOM (Out Of Memory) Killer 会直接在操作系统底层将该进程杀掉。 - 表现:YARN 会捕获到进程非正常退出,通常退出码为
137(128 + 9 SIGKILL),并在此 Container 日志中记录超用内存被杀。
2. 逻辑监控(基于 NodeManager 轮询 - 默认机制)
即使开启了 Cgroups,YARN 依然保留了默认的轮询监控机制作为补充:
- NodeManager 中有一个
ContainersMonitorImpl线程。 - 它会定期(默认 3 秒)读取 Linux 的
/proc/<pid>/stat和/proc/<pid>/statm文件,计算出一棵进程树(Process Tree)的总物理内存(RSS)和虚拟内存(Vmem)。 - 如果发现超限,NodeManager 会主动向该进程树发送
SIGTERM然后发送SIGKILL杀死容器。 - 区别:这种方式有滞后性(3秒窗口期),容易导致短时间内内存暴增冲垮 Node 节点,因此生产环境强依赖 Cgroups 进行物理兜底。
三、 CPU 资源的隔离实现
CPU 资源的隔离比内存更复杂,因为 CPU 是可压缩资源(超出限制通常是降速而不是杀进程)。YARN 底层通过 Cgroups 的 CPU 子系统 实现,主要分为两种模式:弹性共享(Soft Limit) 和 严格限制(Hard Limit)。
1. 弹性共享隔离(基于 CPU Shares - 默认 Cgroups 行为)
- 底层机制:通过 Cgroups 的
cpu.shares参数实现。 - 实现过程:如果一个 Container 申请了 1 个 vCore,YARN 会根据节点的总 CPU 能力计算出一个相对权重值,写入该 Container 对应 Cgroups 目录的
cpu.shares文件中(默认 1 vCore = 1024 shares)。 - 物理效果:
- 当节点 CPU 繁忙时,Linux 内核的 CFS(完全公平调度器)会严格按照
cpu.shares的比例来分配 CPU 时间片,保证 Container 获得其应有的 CPU 资源。 - 弹性:当节点上有其他空闲 CPU 时,该 Container 可以突破自身的配额,占用空闲的 CPU 资源。这提高了集群的整体资源利用率。
- 当节点 CPU 繁忙时,Linux 内核的 CFS(完全公平调度器)会严格按照
2. 严格限制隔离(基于 CPU CFS Quota - 需特殊配置)
在多租户或者对 SLA 要求极高的场景下,用户不希望 Container 抢占空闲 CPU(防止相互干扰),YARN 提供了严格模式(yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage 设置为 true)。
- 底层机制:通过 Cgroups 的
cpu.cfs_period_us和cpu.cfs_quota_us参数实现。 - 实现过程:
cpu.cfs_period_us:定义一个调度周期(通常是 100000 微秒,即 100 毫秒)。cpu.cfs_quota_us:定义在一个周期内,该 Container 最多能使用的 CPU 时间。- 如果 Container 申请了 2 个 vCore,YARN 会将
cpu.cfs_quota_us设置为2 * cpu.cfs_period_us。
- 物理效果:无论 Node 节点是否空闲,Container 使用的 CPU 一旦达到
quota限制,Linux 内核就会挂起该进程的执行,直到下一个周期开始。绝对不允许超用。
3. CPU 绑核(NUMA 架构优化)
在更高阶的隔离中,YARN 支持基于 cpuset Cgroups 子系统的绑核策略。
- 它可以将特定的 Container 进程强制绑定到特定的物理 CPU 核心上运行。
- 这样可以避免 CPU Context Switch(上下文切换)的开销,并且在 NUMA 架构下,保证进程优先使用离特定 CPU 最近的物理内存,实现最高级别的物理隔离。
四、 总结与工作流示例
当你在 YARN 提交一个任务,要求 2 vCores, 2GB 内存 时,底层发生的物理隔离动作如下:
- NodeManager 接收到启动请求。
- 调用
LinuxContainerExecutor的 C 语言二进制程序。 - C 程序以
root权限在操作系统创建:/sys/fs/cgroup/memory/hadoop-yarn/container_xxx//sys/fs/cgroup/cpu/hadoop-yarn/container_xxx/
- 写入参数:
- 将
2 * 1024 * 1024 * 1024(2GB) 写入memory.limit_in_bytes。 - 将
2 * 1024写入cpu.shares(假设非严格模式)。
- 将
- C 程序将任务进程的 PID 写入上述两个 Cgroups 的
tasks或cgroup.procs文件中。 - 切换为提交任务的用户(如
alice),启动 JVM / 实际计算进程。 - 从此,该进程的 CPU 和内存完全受到 Linux 内核级别的物理管控。