YARN 调度器负载模拟器 (SLS)

概述

概述

YARN 调度器是一个备受关注的领域,有不同的实现,例如,Fifo、Capacity 和 Fair 调度器。同时,还进行了多项优化,以提高调度器在不同场景和工作负载下的性能。每个调度器算法都有自己的一组功能,并通过公平性、容量保证、资源可用性等多种因素来驱动调度决策。在生产集群中部署之前,对调度器算法进行非常好的评估非常重要。不幸的是,目前对调度器算法进行评估并非易事。在真实集群中进行评估总是耗时且成本高昂,而且也很难找到足够大的集群。因此,一个可以预测调度器算法对某些特定工作负载的性能的模拟器将非常有用。

YARN 调度程序负载模拟器 (SLS) 就是这样一个工具,它可以在一台机器上模拟大规模 YARN 集群和应用程序负载。此模拟器对于促进 YARN 发展非常宝贵,因为它为研究人员和开发人员提供了一个工具,用于构建新调度程序功能的原型,并以合理的置信度预测其行为和性能,从而有助于快速创新。o 该模拟器将使用真正的 YARN ResourceManager,通过在同一 JVM 内处理和调度 NM/AM 心跳事件来模拟 NodeManagersApplicationMasters,从而消除网络因素。为了持续跟踪调度程序的行为和性能,一个调度程序包装器将包装真正的调度程序。

集群的大小和应用程序负载可以从配置文件中加载,这些配置文件直接通过采用 Apache Rumen 从作业历史文件生成。

该模拟器将在执行期间生成实时指标,包括

  • 整个集群和每个队列的资源使用情况,可用于配置集群和队列的容量。

  • 详细的应用程序执行跟踪(相对于模拟时间记录),可用于分析/验证调度程序行为(各个作业的周转时间、吞吐量、公平性、容量保证等)。

  • 调度程序算法的几个关键指标,例如每个调度程序操作(分配、处理等)的时间成本,Hadoop 开发人员可利用这些指标来查找代码点和可伸缩性限制。

目标

  • 使用真实的作业跟踪,在没有真实集群的情况下大规模使用调度程序。

  • 能够模拟真实的工作负载。

架构

下图说明了模拟器的实现架构。

The architecture of the simulator

模拟器采用工作负载跟踪或合成负载分布作为输入,并生成集群和应用程序信息。对于每个 NM 和 AM,模拟器都会构建一个模拟器来模拟其运行。所有 NM/AM 模拟器都在线程池中运行。模拟器会重复使用 YARN 资源管理器,并从调度程序构建一个包装器。调度程序包装器可以跟踪调度程序行为并生成几个日志,这些日志是模拟器的输出,可以进一步进行分析。

用例

  • 工程

    • 在负载下验证调度程序算法的正确性
    • 查找代码热点/关键路径的经济/实用方法。
    • 验证更改和新功能的影响。
    • 确定调度程序可伸缩性限制的驱动因素。
  • 质量保证

    • 针对“大型”集群和多个工作负载配置文件验证调度程序行为。
  • 解决方案/销售。

    • 针对预定义/典型工作负载的规模模型。
    • 使用真实客户数据(作业跟踪)的集群规模工具。
    • 确定特定工作负载下的最低 SLA。

用法

本节将展示如何使用模拟器。此处令$HADOOP_ROOT表示 Hadoop 安装目录。如果您自己构建 Hadoop,则$HADOOP_ROOThadoop-dist/target/hadoop-$VERSION。模拟器位于$HADOOP_ROOT/share/hadoop/tools/slssls文件夹包含四个目录:binhtmlsample-confsample-data

  • bin:包含模拟器的运行脚本。

  • html:用户还可以在离线模式下重现那些实时跟踪图表。只需将realtimetrack.json上传到$HADOOP_ROOT/share/hadoop/tools/sls/html/showSimulationTrace.html。由于浏览器安全问题,需要将文件realtimetrack.jsonshowSimulationTrace.html放在同一目录中。

  • sample-conf:指定模拟器配置。

  • sample-data:提供一个示例 rumen 跟踪,可用于生成模拟器的输入。

以下各节将逐步介绍如何使用模拟器。在开始之前,确保命令hadoop包含在您的$PATH环境参数中。

步骤 1:配置 Hadoop 和模拟器

在开始之前,确保 Hadoop 和模拟器已配置好。Hadoop 和模拟器的所有配置文件都应放在目录$HADOOP_ROOT/etc/hadoop中,其中ResourceManager和 YARN 调度程序加载其配置。目录$HADOOP_ROOT/share/hadoop/tools/sls/sample-conf/提供多个示例配置,可用于启动演示。

对于 Hadoop 和 YARN 调度程序的配置,用户可以参考 Yarn 网站 (https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/).

对于模拟器,它从文件$HADOOP_ROOT/etc/hadoop/sls-runner.xml加载配置信息。

此处我们说明sls-runner.xml中的每个配置参数。请注意,$HADOOP_ROOT/share/hadoop/tools/sls/sample-conf/sls-runner.xml包含这些配置参数的所有默认值。

  • yarn.sls.runner.pool.size

    模拟器使用线程池来模拟NMAM的运行,此参数指定池中的线程数。

  • yarn.sls.nm.memory.mb

    每个NMSimulator的总内存。

  • yarn.sls.nm.vcores

    每个NMSimulator的总 vCore。

  • yarn.sls.nm.heartbeat.interval.ms

    每个NMSimulator的心跳间隔。

  • yarn.sls.am.heartbeat.interval.ms

    每个AMSimulator的心跳间隔。

  • yarn.sls.am.type.mapreduce

    MapReduce 等应用程序的 AMSimulator 实现。用户可以为其他类型的应用程序指定实现。

  • yarn.sls.container.memory.mb

    每个容器模拟器所需的内存。

  • yarn.sls.container.vcores

    每个容器模拟器所需的核心数。

  • yarn.sls.runner.metrics.switch

    模拟器引入了 指标 来衡量关键组件和操作的行为。此字段指定我们是否打开 (ON) 或关闭 (OFF) 指标运行。

  • yarn.sls.metrics.web.address.port

    模拟器用于提供实时跟踪的端口。默认值为 10001。

  • org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler

    Fifo 调度器的调度器指标实现。

  • org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler

    Fair 调度器的调度器指标实现。

  • org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler

    Capacity 调度器的调度器指标实现。

步骤 2:运行模拟器

模拟器支持两种类型的输入文件:rumen 跟踪和它自己的输入跟踪。启动模拟器的脚本是 slsrun.sh

$ cd $HADOOP_ROOT/share/hadoop/tools/sls
$ bin/slsrun.sh
  Usage: slsrun.sh <OPTIONS>
             --tracetype=<SYNTH | SLS | RUMEN>
             --tracelocation=<FILE1,FILE2,...>
             (deprecated --input-rumen=<FILE1,FILE2,...>  | --input-sls=<FILE1,FILE2,...>)
             --output-dir=<SLS_SIMULATION_OUTPUT_DIRECTORY>
             [--nodes=<SLS_NODES_FILE>]
             [--track-jobs=<JOBID1,JOBID2,...>]
             [--print-simulation]
  • --input-rumen:输入 rumen 跟踪文件。用户可以输入多个文件,用逗号分隔。一个示例跟踪提供在 $HADOOP_ROOT/share/hadoop/tools/sls/sample-data/2jobs2min-rumen-jh.json 中。这等效于 --tracetype=RUMEN --tracelocation=<path_to_trace>

  • --input-sls:模拟器自己的文件格式。模拟器还提供了一个将 rumen 跟踪转换为 sls 跟踪的工具 (rumen2sls.sh)。有关 sls 输入 json 文件的示例,请参阅附录。这等效于 --tracetype=SLS --tracelocation=<path_to_trace>

  • --tracetype:这是配置跟踪生成的新方法,取值 RUMEN、SLS 或 SYNTH,以触发三种类型的负载生成

  • --tracelocation:与上述 tracetype 匹配的输入文件路径。

  • --output-dir:生成运行日志和指标的输出目录。

  • --nodes:集群拓扑。默认情况下,模拟器将使用从输入 json 文件中获取的拓扑。用户可以通过设置此参数指定新的拓扑。有关拓扑文件格式,请参阅附录。

  • --track-jobs:模拟器运行期间将跟踪的特定作业,用逗号分隔。

  • --print-simulation:是否在模拟器运行之前打印模拟信息,包括节点数、应用程序数、任务数以及每个应用程序的信息。

    与 rumen 格式相比,sls 格式简单得多,用户可以轻松生成各种工作负载。模拟器还提供了一个将 rumen 跟踪转换为 sls 跟踪的工具。

    $ bin/rumen2sls.sh
      --rumen-file=<RUMEN_FILE>
      --output-dir=<SLS_OUTPUT_DIRECTORY>
        [--output-prefix=<SLS_FILE_PREFIX>]
    
  • --rumen-file:rumen 格式文件。一个示例跟踪位于目录 sample-data 中。

  • --output-dir:生成模拟跟踪的输出目录。此输出目录中将生成两个文件,包括一个包含所有作业和任务信息的跟踪文件,以及另一个显示拓扑信息的跟踪文件。

  • --output-prefix:生成文件的名称前缀。默认值为“sls”,两个生成的文件为 sls-jobs.jsonsls-nodes.json

指标

YARN 调度程序负载模拟器已集成 指标来衡量关键组件和操作的行为,包括运行应用程序和容器、集群可用资源、调度程序操作时间成本等。如果开关 yarn.sls.runner.metrics.switch 设置为 ON指标 将运行并在用户指定的 --output-dir 目录中输出其日志。用户可以在模拟器运行期间跟踪这些信息,也可以在运行后分析这些日志来评估调度程序性能。

实时跟踪

模拟器提供了一个用于实时跟踪其运行的界面。用户可以访问 http://host:port/simulate 来跟踪整个运行,访问 http://host:port/track 来跟踪特定作业或队列。此处的 host 是我们运行模拟器的位置,port 是由 yarn.sls.metrics.web.address.port 配置的值(默认值为 10001)。

下面,我们将说明网页中显示的每个图表。

第一个图描述正在运行的应用程序和容器的数量。

Number of running applications/containers

第二个图描述集群中已分配和可用的资源(内存)。

Cluster Resource (Memory)

第三个图描述为每个队列分配的资源。此处我们有三个队列:sls_queue_1、sls_queue_2 和 sls_queue_3。前两个队列配置了 25% 的份额,而最后一个队列有 50% 的份额。

Queue Allocated Resource (Memory)

第四个图描述每个调度程序操作的时间成本。

Scheduler Opertion Timecost

最后,我们衡量模拟器使用的内存。

JVM Memory

模拟器还提供了一个用于跟踪某些特定作业和队列的界面。访问 http://<Host>:<Port>/track 来获取这些信息。

此处第一个图说明队列 SLS_Queue_1 的资源使用信息。

Tracking Queue sls_queue_3

第二个图说明作业 job_1369942127770_0653 的资源使用信息。

Tracking Job job_1369942127770_0653

离线分析

模拟器完成后,所有日志都将保存在 $HADOOP_ROOT/share/hadoop/tools/sls/bin/slsrun.sh--output-dir 指定的输出目录中。

  • 文件 realtimetrack.json:每 1 秒记录所有实时跟踪日志。

  • 文件 jobruntime.csv:记录模拟器中所有作业的开始和结束时间。

  • 文件夹 metrics:由 Metrics 生成的日志。

用户还可以在离线模式下重现这些实时跟踪图表。只需将 realtimetrack.json 上传到 $HADOOP_ROOT/share/hadoop/tools/sls/html/showSimulationTrace.html。由于浏览器安全问题,需要将文件 realtimetrack.jsonshowSimulationTrace.html 放置在同一目录中。

合成负载生成器

合成负载生成器通过提供基于分布的负载生成来补充 SLS 原生和 RUMEN 跟踪的广泛性质。负载生成器被组织为 JobStoryProducer(与 rumen 兼容,因此也与 gridmix 兼容,以便以后集成)。我们对随机数生成器进行种子设置,以便结果随机但确定性——因此可重现。我们围绕 /workloads/job_class 层次结构组织正在生成的作业,这允许轻松地对具有类似行为的作业进行分组并对其进行分类(例如,具有长时间运行容器的作业,或仅映射计算的作业等)。用户可以控制许多重要参数的平均值和标准差,例如映射器/归约器的数量、映射器/归约器的持续时间、容器的大小(内存/CPU)、保留的机会等。当我们在少数选项中进行选择时,我们使用加权随机抽样(whenever we pick among a small number of options),或者当我们在广泛的值范围内进行选择时,我们使用对数正态分布(以避免负值)——请参阅对数正态分布的附录。

SLS 的 SYNTH 模式非常方便,无需大量输入文件即可生成非常大的负载。这允许以一种高效且紧凑的方式轻松地探索广泛的用例(例如,想象一下模拟 100k 个作业,并且在不同的运行中只需调整平均映射器数量或平均任务持续时间)。

SLS 中的资源类型

本节介绍如何在 SLS 中使用资源类型。

配置资源管理器

这与为真实集群配置资源类型相同。将 yarn-site.xml 中的项目 yarn.resource-types 配置为以下示例所示。

 <property>
   <name>yarn.resource-types</name>
   <value>resource-type1, resource-type2</value>
 </property>

配置节点管理器

通过将相关项目添加到 sls-runner.xml 中来指定每个节点中的资源大小,如下例所示。这些值适用于 SLS 中的每个节点。除内存和 vcore 之外的资源的默认值为 0。

 <property>
   <name>yarn.sls.nm.resource-type1</name>
   <value>10</value>
 </property>
 <property>
   <name>yarn.sls.nm.resource-type2</name>
   <value>10</value>
 </property>

在 SLS JSON 输入中指定资源

SLS JSON 输入格式支持资源类型,但其他两种格式(SYNTH 和 RUMEN)不支持。若要在 SLS JSON 输入格式中使其正常工作,可以为任务容器和 AM 容器指定资源大小。以下是一个示例。

{
  "job.start.ms" : 0,
  "am.memory-mb": 2048,
  "am.vcores": 2,
  "am.resource-type1": 2,
  "am.resource-type2": 2,
  "job.tasks" : [ {
    "container.duration.ms":  5000
    "container.memory-mb": 1024,
    "container.vcores": 1,
    "container.resource-type1": 1,
    "container.resource-type2": 1
  }
}

附录

资源

YARN-1021 是将 YARN Scheduler Load Simulator 引入 Hadoop YARN 项目的主要 JIRA。 YARN-6363 是将 Synthetic Load Generator 引入 SLS 的主要 JIRA。

SLS JSON 输入文件格式

此处提供包含 2 个作业的 sls json 文件示例格式。第一个作业有 3 个映射任务,第二个作业有 2 个映射任务。

{
  "num.nodes": 3,  // total number of nodes in the cluster
  "num.racks": 1   // total number of racks in the cluster, it divides num.nodes into the racks evenly, optional, the default value is 1
}
{
  "am.type" : "mapreduce", // type of AM, optional, the default value is "mapreduce"
  "job.start.ms" : 0,      // job start time
  "job.end.ms" : 95375,    // job finish time, optional, the default value is 0
  "job.queue.name" : "sls_queue_1", // the queue job will be submitted to
  "job.id" : "job_1",      // the job id used to track the job, optional. The default value, an zero-based integer increasing with number of jobs, is used if this is not specified or job.count > 1
  "job.user" : "default",  // user, optional, the default value is "default"
  "job.count" : 1,         // number of jobs, optional, the default value is 1
  "job.tasks" : [ {
    "count": 1,    // number of tasks, optional, the default value is 1
    "container.host" : "/default-rack/node1",  // host the container asks for
    "container.start.ms" : 6664,  // container start time, optional
    "container.end.ms" : 23707,   // container finish time, optional
    "container.duration.ms":  50000, // duration of the container, optional if start and end time is specified
    "container.priority" : 20,    // priority of the container, optional, the default value is 20
    "container.type" : "map"      // type of the container, could be "map" or "reduce", optional, the default value is "map"
  }, {
    "container.host" : "/default-rack/node3",
    "container.start.ms" : 6665,
    "container.end.ms" : 21593,
    "container.priority" : 20,
    "container.type" : "map"
  }, {
    "container.host" : "/default-rack/node2",
    "container.start.ms" : 68770,
    "container.end.ms" : 86613,
    "container.priority" : 20,
    "container.type" : "map"
  } ]
}
{
  "am.type" : "mapreduce",
  "job.start.ms" : 105204,
  "job.end.ms" : 197256,
  "job.queue.name" : "sls_queue_2",
  "job.id" : "job_2",
  "job.user" : "default",
  "job.tasks" : [ {
    "container.host" : "/default-rack/node1",
    "container.start.ms" : 111822,
    "container.end.ms" : 133985,
    "container.priority" : 20,
    "container.type" : "map"
  }, {
    "container.host" : "/default-rack/node2",
    "container.start.ms" : 111788,
    "container.end.ms" : 131377,
    "container.priority" : 20,
    "container.type" : "map"
  } ]
}

SYNTH JSON 输入文件格式

此处提供合成生成器 json 文件的示例格式。我们使用(非 JSON 规范)内联注释来解释每个参数的用途。

{
  "description" : "tiny jobs workload",    //description of the meaning of this collection of workloads
  "num_nodes" : 10,  //total nodes in the simulated cluster
  "nodes_per_rack" : 4, //number of nodes in each simulated rack
  "num_jobs" : 10, // total number of jobs being simulated
  "rand_seed" : 2, //the random seed used for deterministic randomized runs

  // a list of “workloads”, each of which has job classes, and temporal properties
  "workloads" : [
    {
      "workload_name" : "tiny-test", // name of the workload
      "workload_weight": 0.5,  // used for weighted random selection of which workload to sample from
      "queue_name" : "sls_queue_1", //queue the job will be submitted to

    //different classes of jobs for this workload
       "job_classes" : [
        {
          "class_name" : "class_1", //name of the class
          "class_weight" : 1.0, //used for weighted random selection of class within workload

          //nextr group controls average and standard deviation of a LogNormal distribution that
          //determines the number of mappers and reducers for thejob.
          "mtasks_avg" : 5,
          "mtasks_stddev" : 1,
          "rtasks_avg" : 5,
          "rtasks_stddev" : 1,

          //averge and stdev input param of LogNormal distribution controlling job duration
          "dur_avg" : 60,
          "dur_stddev" : 5,

          //averge and stdev input param of LogNormal distribution controlling mappers and reducers durations
          "mtime_avg" : 10,
          "mtime_stddev" : 2,
          "rtime_avg" : 20,
          "rtime_stddev" : 4,

          //averge and stdev input param of LogNormal distribution controlling memory and cores for map and reduce
          "map_max_memory_avg" : 1024,
          "map_max_memory_stddev" : 0.001,
          "reduce_max_memory_avg" : 2048,
          "reduce_max_memory_stddev" : 0.001,
          "map_max_vcores_avg" : 1,
          "map_max_vcores_stddev" : 0.001,
          "reduce_max_vcores_avg" : 2,
          "reduce_max_vcores_stddev" : 0.001,

          //probability of running this job with a reservation
          "chance_of_reservation" : 0.5,
          //input parameters of LogNormal distribution that determines the deadline slack (as a multiplier of job duration)
          "deadline_factor_avg" : 10.0,
          "deadline_factor_stddev" : 0.001,
        }
       ],
    // for each workload determines with what probability each time bucket is picked to choose the job starttime.
    // In the example below the jobs have twice as much chance to start in the first minute than in the second minute
    // of simulation, and then zero chance thereafter.
      "time_distribution" : [
        { "time" : 1, "weight" : 66 },
        { "time" : 60, "weight" : 33 },
        { "time" : 120, "jobs" : 0 }
     ]
    }
 ]
}

模拟器输入拓扑文件格式

以下是一个示例输入拓扑文件,其中有 3 个节点组织在一个机架中。

{
  "rack" : "default-rack",
  "nodes" : [ {
    "node" : "node1"
  }, {
    "node" : "node2"
  }, {
    "node" : "node3"
  }]
}

关于对数正态分布的说明

对数正态分布很好地表示了我们在实践中看到的许多参数(例如,大多数作业的映射程序数量较少,但少数可能非常大,少数非常小,但大于零。不过值得注意的是,它可能很难使用,因为平均值通常在分布的峰值(最常见值)的右侧,因为分布有一条单边尾)。