资源调度和资源隔离是 Yarn 作为一个资源管理系统,最重要和最基础的两个功能。资源调度由 ResourceManager 完成,资源隔离由各个 NodeManager 实现。
ResourceManager 将某个 NodeManager 上资源分配给任务,这也就是资源分配。资源分配后,NodeManager 需按照要求为任务提供相应的资源,甚至保证这些资源应具有独占性,为任务运行提供基础的保证,这就是资源隔离。
Yarn 的资源管理器实际上是一个事件处理器,它需要处理六种来自外部的 SchedulerEvent 类型的事件,六种事件的含义如下:
NODE_REMOVED
表示集群中被移除一个计算节点,资源调度器收到该事件时需要从可分配资源总量中移除相应的资源量
NODE_ADDED
表示集群中增加了一个计算节点,资源调度器收到该事件时需要将新增的资源加到可分配资源中去
APPLICATION_ADDED
表示 ResourceManager 收到一个新的 Application。通常而言,资源管理器需要为每个 application 维护一个独立的数据结构,以便统一管理和资源分配,资源管理器需要将该 Application 添加到相应的数据结构中去
APPLICATION_REMOVED
表示一个 application 运行结束(成功或失败),资源管理器需将该 application 从相应的数据结构中清除
CONTAINER_EXPIRED
当 RM 将一个 container 分配给某个 AM 后,如果该 AM 在一定时间间隔内没有使用该 container,则 RM 会对该 container 进行再分配
NODE_UPDATE
NM 通过心跳机制向 RM 汇报各个 container 的运行情况,会触发一个 NODE_UPDATE 事件,由于此时可能有新的 container 得到释放,因此该事件会触发资源分配
Yarn 对内存和 CPU 采用了不同的资源隔离方案。对于内存资源,为了更灵活的控制内存,Yarn 采用了进程监控的方案控制内存的使用,即每个 NodeManager 会启动一个额外的监控线程控制每个 container 内存资源的使用量,一旦发现它超过规定的资源量,则会将其杀死。采用这种方案的另一个原因是 Java 中创建子进程采用了 fork()+exec() 的方案,子进程启动的瞬间,它使用的内存量与父进程一致,从外面看来,一个进程使用的内存量可能瞬间翻倍,然后又降下来,采用线程监控 的方法可以防止这种情况下导致的 swap 操作
对于 CPU 资源,则采用了 Cgroups 进行资源隔离。
CPU 被划分成虚拟 CPU,这里的虚拟 CPU 是 Yarn 自己引入的概念,初衷是考虑到不同节点的 CPU 性能不同,这时候可以通过为一个性能更好的 CPU 多配置几个虚拟 CPU 的方法来弥补这种差距