YARN Resource Localization (资源本地化) 不完全指南

本文主要取材并翻译自 Management of Application Dependencies in YARN 以及 Resource Localization in YARN: Deep Dive。本文假设读者对于 YARN 有基本的了解。

背景

在 YARN 中,应用程序通过在底层操作系统上运行的容器来执行工作。容器通常依赖于执行所需的文件,这些文件可能在启动时就用到,也可能在运行时用到一次或多次。例如,要将一个简单的 Java 程序作为容器启动,就需要一个 jar 文件,并且可能还需要其他 jar 文件作为依赖项。为了避免每次访问(主要是读取)这些文件时出现远程访问或自行管理的情况,YARN 为应用程序提供了本地化这些文件的能力。

在启动容器时,ApplicationMaster(AM)可以指定容器所需的所有文件,并进行本地化。一旦指定,YARN 会自行处理本地化,并隐藏涉及安全复制、管理和删除这些文件的所有复杂性。

本文基于以上参考资料,对 YARN 的资源本地化进行简要介绍。

LocalResources (本地资源)

名词解释

在进行接下来的介绍之前,首先明确一些定义:

  • 本地化(Localization):将远程资源复制 / 下载到本地文件系统的过程。不必每次都远程访问资源,而是将其复制到本地机器上。
  • 本地资源(LocalResource):运行容器所需的文件 / 库。NodeManager 负责在启动容器之前将资源本地化。对于每个本地资源,应用程序可以指定以下内容:
    • URL: 远程下载本地资源的地址
    • Size: 本地资源的字节大小
    • Timestamp: 资源在容器启动前在远程文件系统上的最后修改时间戳
    • LocalResourceType: 指定 NodeManager 本地化的资源类型,包括 FILEARCHIVEPATTERN
    • Pattern: 从归档中提取条目所使用的模式(仅在类型为 PATTERN 时使用)
    • LocalResourceVisibility: 指定 NodeManager 本地化的资源的可见性,包括 PUBLIC(公共)、PRIVATE(私有)和 APPLICATION(应用程序级别)

对于可本地化的文件,容器可以使用任何容器只读的文件。本地资源的典型示例包括:

  • 启动容器所需的库文件,例如一个 jar 文件
  • 启动后配置容器所需的配置文件(远程服务 URL、应用程序默认配置等)
  • 静态字典文件

以下是一些不适合用作本地资源的示例:

  • 共享文件,尤其是外部组件可能会在将来更新,而当前容器希望跟踪这些更改的文件
  • 应用程序需要直接更新的文件
  • 应用程序计划与外部服务共享更新信息所用的文件

其他相关的名词定义如下:

  • ResourceLocalizationService (资源本地化服务):在 NodeManager 中,ResourceLocalizationService 是负责安全地下载和组织容器所需的各种文件资源的服务。它尽量将文件分布在所有可用的磁盘上,强制执行对下载文件的访问控制限制,并对其施加适当的使用限制。
  • DeletionService (删除服务):在 NodeManager 内运行的服务,按照指示删除本地路径。
  • Localizer :实际执行本地化的线程或进程。有两种类型的 Localizer,其中 PublicLocalizer 用于 PUBLIC 资源,ContainerLocalizers 用于 PRIVATE 和 APPLICATION 资源。
  • LocalCache (本地缓存):NodeManager 维护和管理所有下载文件的多个本地缓存。资源基于最初用于复制该文件的远程 URL 进行唯一标识。

本地资源时间戳

NodeManager 在容器启动前跟踪每个本地资源的最后修改时间戳。在下载之前,NodeManager 会检查文件是否在此期间发生了更改。这是为了在本地资源上提供一致的视图,应用程序可以始终使用相同的文件内容运行,而不必担心由于对同一文件的并发写入而导致的数据损坏问题。

一旦文件从其远程位置复制到 NodeManager 的本地磁盘之一,除了 URL(在复制时使用)之外,它失去了与原始文件的任何联系。NodeManager 不会跟踪远程文件在将来的任何修改,因此,如果外部系统必须更新远程资源,则应通过版本控制来完成。为了防止不一致性,如果有容器依赖于已修改的远程资源,YARN 将这些容器置为失败。

请注意,在启动节点上的任何容器时,ApplicationMaster 向 NodeManager 指定资源时间戳。同样,在运行 ApplicationMaster 本身的容器中,客户端必须填充 ApplicationMaster 所需的所有资源的时间戳。例如,在 MapReduce 应用程序中,MapReduce JobClient 确定 MapReduce ApplicationMaster 所需资源的修改时间戳。然后,ApplicationMaster 本身为 MapReduce 任务所需的资源设置时间戳。

本地资源类型

每个本地资源可以是以下类型之一:

  • FILE(文件):常规文件,可以是文本或二进制文件。
  • ARCHIVE(归档):一个归档文件,由 NodeManager 自动解压缩。目前,NodeManager 支持识别.jar.tar.tar.gz.zip 文件。
  • PATTERN(模式):ARCHIVE 和 FILE 类型的混合体。原始文件保留不变,同时(仅在本地化期间)部分文件在本地文件系统上解压缩。原始文件和提取的文件都放在同一个目录中。从归档中提取和不提取哪些内容是由本地资源规范中的模式字段确定的。PATTERN 类型仅支持.jar 文件,其他文件都被视为常规的 ARCHIVE 文件处理。

本地资源可见性

根据指定的本地资源可见性(LocalResourceVisibility),本地资源可以分为三种类型,取决于它们在原始存储 / 文件系统上的可见性 / 可访问性。

  • PUBLIC(公共):所有标记为 PUBLIC 的本地资源(远程 URL)都可以由任何用户的容器访问。通常,公共资源是可以由远程文件系统上的任何人访问的资源,并且根据相同的访问控制列表(ACL)将其复制到公共本地缓存中。如果将来属于同一应用程序(或任何用户的任何应用程序)的容器请求相同的本地资源,则从本地缓存中提供该资源,如果该资源尚未从本地缓存中清除,则不会再次复制 / 下载。公共缓存中的所有文件的所有者是 yarn-user(NodeManager 运行的用户),具有全局可读权限,以便可以由在该节点上运行的所有用户的容器共享。
  • PRIVATE(私有):标记为私有的本地资源在节点上由同一用户的所有应用程序共享。这些本地资源被复制到特定用户(启动容器 / 应用程序的用户)的私有缓存中。这些文件对于属于不同应用程序但由同一用户启动的所有容器都是可访问的。本地文件系统上的这些文件归用户所有,其他用户无法访问。与公共本地缓存类似,即使对于应用程序提交用户,也没有写入权限,即用户无法在本地化后修改这些文件。这是为了避免某个容器对这些文件的意外写入对其他容器造成损害,所有容器都期望它们处于与最初指定时相同的状态(保持与原始时间戳和 / 或版本号一致)。
  • APPLICATION(应用程序):标记为 APPLICATION 范围的所有资源仅在节点上的同一应用程序的容器之间共享。它们被复制到由启动容器 / 应用程序的用户拥有的特定应用程序本地缓存中。所有这些文件都由用户所有,并具有只读权限。

请注意,在启动容器时,ApplicationMaster 向 NodeManager 指定此资源的可见性,NodeManager 本身不做任何决策或分类资源。同样,在运行 ApplicationMaster 本身的容器中,客户端必须为 ApplicationMaster 所需的所有资源指定可见性。

本地资源生命周期

不同类型的本地资源具有不同的生命周期:

  • PUBLIC(公共)本地资源在容器或应用程序完成后不会被删除。它们仅在每个本地目录的磁盘容量出现压力时被删除。本地文件的阈值由配置 yarn.nodemanager.localizer.cache.target-size-mb 所规定。
  • PRIVATE(私有)本地资源的生命周期与公共资源相同。将来希望为不同用户使用单独的阈值。
  • APPLICATION(应用程序)范围的本地资源在应用程序完成后立即被删除。

值得注意的是,在任何给定的应用程序中,可能会有多个 ApplicationAttempts,每个尝试可能会在给定的 NodeManager 上启动多个容器。当属于 ApplicationAttempt 的第一个容器启动时,ResourceLocalizationService 将根据容器的启动上下文请求为该应用程序本地化文件。如果将来的容器请求更多此类资源,则它们都将被本地化。如果一个 ApplicationAttempt 完成 / 失败,另一个 ApplicationAttempt 开始,ResourceLocalizationService 不会对先前本地化的资源进行任何操作。当应用程序最终完成时,ResourceManager 将此信息传达给 NodeManagers,NodeManagers 会清除应用程序的本地缓存。总之,APPLICATION 级别的 LocalResources 其实是应用程序范围的,而不是 ApplicationAttempt 范围的。

本地化过程

PUBLIC 资源本地化过程

公共(PUBLIC)资源的本地化由一组称为 PublicLocalizers 的线程池负责。

  • PublicLocalizers 在 NodeManager 本身的地址空间内运行。
  • PublicLocalizer 线程的数量由配置 yarn.nodemanager.localizer.fetch.thread-count 指定,控制在下载公共资源时的最大并行度。
  • 在本地化公共资源时,本地化程序通过检查远程文件系统上资源的权限来验证所有请求的资源是否确实为公共资源。任何不符合条件的 LocalResource 都会被拒绝本地化。
  • 每个 PublicLocalizer 使用 ContainerLaunchContext 中传递的凭据,从远程文件系统安全地复制资源。

PRIVATE/APPLICATION 资源本地化过程

私有(PRIVATE)/ 应用程序(APPLICATION)资源的本地化不在 NodeManager 内部进行,因此不是集中式的。该过程有一些步骤,概述如下:

  • 这些资源的本地化在一个名为 ContainerLocalizer 的单独进程中进行。
  • 每个 ContainerLocalizer 进程由 NodeManager 中的一个名为 LocalizerRunner 的线程管理。每个容器如果有尚未下载的资源,将触发一个 LocalizerRunner。
  • LocalResourcesTracker 是一个按用户或应用程序跟踪所有 LocalResources 的对象。
  • 当容器首次请求私有(PRIVATE)/ 应用程序(APPLICATION)LocalResource 时,如果在 LocalResourcesTracker 中找不到(或者找到但处于 INITIALIZED 状态),则将其添加到待处理资源列表中。
    • 根据需要下载的新内容,可能会创建一个 LocalizerRunner。
    • LocalResources 被添加到其 LocalizerRunner 的待处理资源列表中。
  • NodeManager 在安全模式下的一个要求是以应用程序提交用户(而不是 yarn-user 特权用户)的身份下载 / 复制这些资源。因此,LocalizerRunner 启动一个 LinuxContainerExecutor(作为应用程序提交用户运行的进程),然后执行 ContainerLocalizer 来下载这些资源。
    • 一旦启动,ContainerLocalizer 开始与 NodeManager 进程进行心跳通信。
    • 每次心跳时,LocalizerRunner 要么向 ContainerLocalizer 分配一个资源,要么要求其退出。ContainerLocalizer 向 LocalizerRunner 报告下载的状态。
    • 如果无法下载某个资源,则会从 LocalResourcesTracker 中移除该特定资源,容器最终标记为失败。发生这种情况时,LocalizerRunners 停止运行的 ContainerLocalizers 并退出。
    • 如果下载成功,则 LocalizerRunner 会再次给 ContainerLocalizer 分配另一个资源,直到所有待处理资源都成功下载。
  • 目前,每个 ContainerLocalizer 不支持多个私有(PRIVATE)/ 应用程序(APPLICATION)资源的并行下载,YARN-574 将会解决这个问题。

本地化目标位置

在每个 NodeManager 机器上,LocalResources 最终会本地化到以下目标目录中,位于每个本地目录下:

  • PUBLIC: <local-dir>/filecache,资源权限为 755
  • PRIVATE: <local-dir>/usercache/${user}/filecache,资源权限为 710
  • APPLICATION: <local-dir>/usercache/${user}/appcache/<app-id>/filecache,权限为 710

其中:

  • <local-dir> 是 NodeManager 的本地目录,由 yarn.nodemanager.local-dirs 配置。
  • <user> 是启动容器的用户。
  • <app-id> 是应用程序的唯一标识。

相关配置

管理员可以通过在启动 NodeManager 时设置或更改 yarn-site.xml 中的特定配置参数来控制与资源本地化相关的各种事项:

  • yarn.nodemanager.local-dirs:一个逗号分隔的本地目录列表,可以配置用于在本地化过程中复制文件的目录。允许使用多个目录,利用多个磁盘进行本地化,这有助于故障转移(一个或多个磁盘出现问题不会影响所有容器)和负载均衡(没有单个磁盘被写入限制)。因此,应该尽可能在不同的本地磁盘上配置各个目录。
  • yarn.nodemanager.local-cache.max-files-per-directory:限制每个本地化目录中本地化文件的最大数量(分别针对 PUBLIC / PRIVATE / APPLICATION 资源)。其默认值为 8192,通常不应赋予一个很大的值(应配置一个远小于底层文件系统的每个目录最大文件限制的值)。
  • yarn.nodemanager.localizer.address:ResourceLocalizationService 侦听各种 Localizer 的网络地址。
  • yarn.nodemanager.localizer.client.thread-count:限制 ResourceLocalizationService 中用于处理 Localizers 的本地化请求的 RPC 线程数。默认值为 5,这意味着默认情况下,在任何时刻只有 5 个 Localizers 会被处理,而其他的则在 RPC 队列中等待。
  • yarn.nodemanager.localizer.fetch.thread-count:配置用于本地化 PUBLIC 资源的线程数。请记住,PUBLIC 资源的本地化发生在 NodeManager 地址空间内,因此此属性限制了在 NodeManager 内为本地化 PUBLIC 资源生成的线程数。默认值为 4。
  • yarn.nodemanager.delete.thread-count:控制 DeletionService 用于删除文件的线程数。DeletionService 在整个 NodeManager 中用于删除日志文件和本地缓存文件。默认值为 4。
  • yarn.nodemanager.localizer.cache.target-size-mb:决定用于本地化资源的最大磁盘空间(目前没有针对 PUBLIC / PRIVATE / APPLICATION 缓存的单独限制,可参见 YARN-882)。一旦缓存的总磁盘大小超过此值,删除服务将尝试删除未被任何运行容器使用的文件。此限制适用于所有磁盘的总计,而不是每个磁盘。
  • yarn.nodemanager.localizer.cache.cleanup.interval-ms:在此时间间隔后,如果总缓存大小超过配置的最大大小,资源本地化服务将尝试删除未使用的资源。每次容器请求资源时,容器将被添加到资源的引用列表中。只有容器退出时,作为容器资源清理的一部分,它才会从资源的引用列表中移除。因此,当引用计数降至 0 时,资源成为删除的候选。资源将按照 LRU(最近最少使用)的方式进行删除,直到当前缓存大小降至目标大小以下。