如何解决 Amazon EMR 上 Spark 中的“Container killed by YARN for exceeding memory limits”错误?

上次更新时间:2019 年 4 月 9 日

如何解决 Amazon EMR 上 Spark 中的“Container killed by YARN for exceeding memory limits”错误?

简短描述

使用以下方法之一来解决此错误:

  • 提高内存开销
  • 减少执行程序内核的数量
  • 增加分区数量
  • 提高驱动程序和执行程序内存

解决方法

此错误的根本原因和适当解决方法取决于您的工作负载。您可能需要按以下顺序尝试以下每个方法,直到错误得到解决。在您继续另一个方法之前,撤回您在前一部分中对 spark-defaults.conf 进行的任何更改。

提高内存开销

内存开销是分配给每个执行者的外堆内存量。默认情况下,内存开销被设置为执行程序内存的 10% 或 384,以较高者为准。内存开销用于 Java NIO 直接缓冲区、线程堆栈、共享本机库或内存映射文件。

请考虑逐步增加内存开销,最高增加 25%。请确保驱动程序或执行程序内存加上驱动程序或执行程序内存开销的总和始终小于 您的 Amazon Elastic Compute Cloud (Amazon EC2) 实例类型的 yarn.nodemanager.resource.memory-mb 值:

spark.driver/executor.memory + spark.driver/executor.memoryOverhead < yarn.nodemanager.resource.memory-mb

如果驱动程序容器或执行程序容器中发生错误,则仅考虑提高该容器的内存开销。您可以在集群运行、启动新集群或提交作业时提高内存开销。

在正在运行的集群上:

修改主节点上的 spark-defaults.conf。例如:

sudo vim /etc/spark/conf/spark-defaults.conf

spark.driver.memoryOverhead 512
spark.executor.memoryOverhead 512

在新的集群上:

在启动集群时,添加以下类似的配置对象

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.driver.memoryOverhead": "512",
      "spark.executor.memoryOverhead": "512"
    }
  }
]

对于单个作业:

使用 --conf 选项在运行 spark-submit 时提高内存开销。例如:

spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster --conf spark.driver.memoryOverhead=512 --conf spark.executor.memoryOverhead=512 /usr/lib/spark/examples/jars/spark-examples.jar 100

如果增加内存开销无法解决问题,请减少执行者内核的数量。

减少执行程序内核的数量

这可减少执行程序可以执行的最大任务数量,从而减少所需的内存量。根据抛出此错误的驱动程序容器或者遇到此错误的另一个执行程序容器,考虑为驱动程序或执行程序减少内核数量。

在正在运行的集群上:

修改主节点上的 spark-defaults.conf。例如:

sudo vim /etc/spark/conf/spark-defaults.conf
spark.driver.cores  3
spark.executor.cores  3

在新的集群上:

在启动集群时,添加以下类似的配置对象

[
  {
    "Classification": "spark-defaults",
    "Properties": {"spark.driver.cores" : "3",
      "spark.executor.cores": "3"
    }
  }
]

对于单个作业:

使用 --executor-cores 选项在您运行 spark-submit 时减少执行程序内核的数量。例如:

spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster --executor-cores 3 --driver-cores 3 /usr/lib/spark/examples/jars/spark-examples.jar 100

如果仍遇到错误消息,增加分区数量。

增加分区数量

要增加分区数量,请为原始弹性分布式数据集增加 spark.default.parallelism 的值,或执行 .repartition() 操作。增加分区数量可减少每个分区所需的内存量。由于 Spark 大量使用集群 RAM 作为最大限度提高速度的有效方式,因此务必要使用 Ganglia 监控内存使用情况,然后验证集群设置和分区策略是否满足您不断增长的数据需求。如果您仍遇到“Container killed by YARN for exceeding memory limits”这一错误消息,请提高驱动程序和执行程序内存。

提高驱动程序和执行程序内存

如果驱动程序容器或执行程序容器发生错误,则请考虑增加驱动程序或执行程序的内存,但不能同时增加两者的内存。请确保驱动程序或执行程序内存加上驱动程序或执行程序内存开销的总和始终小于 您的 EC2 实例类型的 yarn.nodemanager.resource.memory-mb 值:

spark.driver/executor.memory + spark.driver/executor.memoryOverhead < yarn.nodemanager.resource.memory-mb

在正在运行的集群上:

修改主节点上的 spark-defaults.conf。例如:

sudo vim /etc/spark/conf/spark-defaults.conf

spark.executor.memory  1g
spark.driver.memory  1g

在新的集群上:

在启动集群时,添加以下类似的配置对象

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.executor.memory": "1g",
      "spark.driver.memory":"1g",
    }
  }
]

对于单个作业:

使用 --executor-memory--driver-memory 选择在您运行 spark-submit 时提高内存。例如:

spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster --executor-memory 1g --driver-memory 1g /usr/lib/spark/examples/jars/spark-examples.jar 100

如果您仍然收到错误消息,请尝试以下操作:

  • 基准测试:这是针对示例数据集运行您的应用程序的最佳实践。这可以帮助您识别速度下降和偏斜分区,从而可能会导致内存问题。
  • 数据筛选:确保您正在处理最少量的数据。如果您不筛选您的数据,或者如果您在应用程序运行后期筛选数据,则多余的数据可能会减缓应用程序的速度并提高内存异常几率。
  • 数据集大小:这是处理最低所需数据的最佳实践。对您的数据进行分区,以便只摄入所需数据。
  • 分区策略:考虑使用不同的分区策略。例如,对备用键进行分区,以免出现大型分区和偏斜分区。
  • EC2 实例类型:您的 EC2 实例可能没有工作负载所需的内存资源。切换到更大的内存优化型实例类型可能会解决该错误。如果您更改实例类型后仍然遇到内存异常,则对新实例尝试本文章前面提到的方法。