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(最近最少使用)的方式进行删除,直到当前缓存大小降至目标大小以下。