如何解决 Amazon EMR 上 Spark 中的“Container killed by YARN for exceeding memory limits”(由于超出内存限制,容器被 YARN 终止)错误?

2 分钟阅读
0

我想要排查 Amazon EMR 上的 Spark 中的“Container killed by YARN for exceeding memory limits”(由于超出内存限制,容器被 YARN 终止)错误。

简短描述

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

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

解决方法

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

提高内存开销

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

请考虑逐步增加内存开销,最高增加 25%。驱动程序或执行程序内存加上内存开销的总和必须小于您的实例类型的 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”(由于超出内存限制,容器被 YARN 终止)错误消息,请提高驱动程序和执行程序内存。

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

如果驱动程序容器或执行程序容器发生错误,则请考虑增加驱动程序或执行程序的内存,但不能同时增加两者的内存。请确保驱动程序或执行程序内存加上驱动程序或执行程序内存开销的总和始终小于您的 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 实例可能没有工作负载所需的内存资源。切换到更大的内存优化型实例类型可能会解决该错误。如果在更改实例类型后仍然遇到内存异常,请在新实例上尝试这些问题排查方法。

相关信息

Spark 配置

如何解决 Amazon EMR 上的 Spark 中的“java.lang.ClassNotFoundException”错误?

相关视频

AWS 官方
AWS 官方已更新 1 年前