排查 Amazon EMR 上 Spark 中的“Container killed by YARN for exceeding memory limits”错误

image.png

作为一名大数据开发工程师,经常需要处理和优化分布式计算任务。最近,我在处理一个复杂的 Spark 作业时,遇到了一个棘手的问题:容器因为内存超限被 YARN 终止。

这次经历让我深刻理解了内存管理的重要性,也学到了许多实用的技巧。下面,我将分享这次排查和解决问题的全过程。

起因:意外的中断

那天,我接到一个紧急任务,需要处理一组巨大的日志数据来生成每日业务报表。这份报表对于公司的业务决策至关重要,需要在几个小时内完成。于是,我将任务提交到 Amazon EMR 上的 Spark 集群,希望能快速完成计算。然而,不久后,我收到了错误通知:“Container killed by YARN for exceeding memory limits”。任务中断了,我必须找到解决方案。

初步调查:日志分析

image.png

第一步,我深入查看了 YARN 日志,确认了内存超限是导致容器被终止的原因。显然,默认的内存设置不足以支撑这个高负载任务。于是,我决定从几个方面逐步优化,确保作业顺利运行。

提高内存开销

我的第一个尝试是提高内存开销。内存开销是分配给每个执行者的外堆内存量,用于 Java NIO 直接缓冲区、线程堆栈等。默认情况下,内存开销是执行程序内存的 10% 或 384MB,以较高者为准。我逐步将内存开销提高到 25%,希望能缓解内存压力。

image.png

  1. 在正在运行的集群上修改配置

    sudo vim /etc/spark/conf/spark-defaults.conf
    
    spark.driver.memoryOverhead 512
    spark.executor.memoryOverhead 512
    
  2. 在新的集群上启动时添加配置

    [
      {
        "Classification": "spark-defaults",
        "Properties": {
          "spark.driver.memoryOverhead": "512",
          "spark.executor.memoryOverhead": "512"
        }
      }
    ]
    
  3. 对于单个作业,在运行 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
    

遗憾的是,尽管内存开销增加了,问题依然存在。我意识到需要进一步优化。

减少执行程序内核数量

接下来,我考虑减少执行程序的内核数量。这意味着每个执行程序可以执行的任务数量减少,从而降低内存需求。我将驱动程序和执行程序的内核数量减少到 3 核。

image.png

  1. 在正在运行的集群上修改配置

    sudo vim /etc/spark/conf/spark-defaults.conf
    
    spark.driver.cores 3
    spark.executor.cores 3
    
  2. 在新的集群上启动时添加配置

    [
      {
        "Classification": "spark-defaults",
        "Properties": {
          "spark.driver.cores": "3",
          "spark.executor.cores": "3"
        }
      }
    ]
    
  3. 对于单个作业,在运行 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() 操作,成功减少了每个分区的内存需求。然而,问题依然未能完全解决。

image.png

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

最后,我决定提高驱动程序和执行程序的内存,但不能同时增加两者。确保总内存开销不超过实例的限制是关键。
image.png

  1. 在正在运行的集群上修改配置

    sudo vim /etc/spark/conf/spark-defaults.conf
    
    spark.executor.memory 1g
    spark.driver.memory 1g
    
  2. 在新的集群上启动时添加配置

    [
      {
        "Classification": "spark-defaults",
        "Properties": {
          "spark.executor.memory": "1g",
          "spark.driver.memory": "1g"
        }
      }
    ]
    
  3. 对于单个作业,在运行 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
    

image.png

总结:最终解决方案

经过多次尝试和调整,我终于解决了内存超限的问题。总结起来,以下几点是关键:

基准测试

使用示例数据集运行应用程序,识别潜在的内存问题。

image.png

数据筛选

确保处理最少量的数据,避免多余数据拖慢应用程序。
image.png

分区策略

使用合理的分区策略,避免大型分区和数据倾斜。
image.png

EC2 实例类型

选择更大的内存优化型实例,以满足工作负载需求。
image.png

通过这些方法,我成功优化了 Spark 任务,确保了业务报表的及时生成。这次经历不仅提升了我的技术能力,也让我深刻体会到内存管理的重要性。

image.png

希望这些经验对大家有所帮助,在面对类似问题时,能够快速找到合适的解决方案。

  • 37
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据小羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值