Yarn上有Spark,容器以非零退出代码143退出

bvhaajcl  于 2021-06-28  发布在  Hive
关注(0)|答案(3)|浏览(1061)

我正在使用hdp2.5,运行spark submit作为Yarn簇模式。
我尝试使用Dataframe交叉连接生成数据。即

val generatedData = df1.join(df2).join(df3).join(df4)
generatedData.saveAsTable(...)....

df1存储级别是内存和磁盘
df2、df3、df4存储级别仅为内存单元
df1有更多的记录,即500万条,而df2到df4最多有100条记录。使用broadcastednestedloopjoin解释计划这样做,explain plain将获得更好的性能。
因为某些原因它总是失败。我不知道如何调试它,内存在哪里爆炸。
错误日志输出:

16/12/06 19:44:08 WARN YarnAllocator: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal

16/12/06 19:44:08 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal

16/12/06 19:44:08 ERROR YarnClusterScheduler: Lost executor 1 on hdp4: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal

16/12/06 19:44:08 WARN TaskSetManager: Lost task 1.0 in stage 12.0 (TID 19, hdp4): ExecutorLostFailure (executor 1 exited caused by one of the running tasks) Reason: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal

在此错误之前,我没有看到任何警告或错误日志。有什么问题?我应该在哪里查找内存消耗?我在斯巴奎的储存标签上看不到任何东西。日志取自hdp2.5上的yarn资源管理器ui
编辑查看容器日志,它似乎是一个 java.lang.OutOfMemoryError: GC overhead limit exceeded 我知道如何增加记忆,但我已经没有记忆了。如何使用4个Dataframe执行笛卡尔/乘积联接而不出现此错误。

iq3niunx

iq3niunx1#

原因1
默认情况下,洗牌计数为 200 . 洗牌太多会增加程序的复杂性和崩溃的几率。尝试控制spark会话中的洗牌次数。我把计数改为 5 使用下面的代码。

implicit val sparkSession = org.apache.spark.sql.SparkSession.builder().enableHiveSupport().getOrCreate()    
sparkSession.sql("set spark.sql.shuffle.partitions=5")

另外,如果您使用的是Dataframe,并且没有对Dataframe进行重新分区,那么执行将在单个执行器中完成。如果只有一个执行器运行一段时间,那么Yarn将使其他执行器关闭。稍后如果需要更多内存,尽管yarn尝试重新调用其他执行器,但有时执行器不会出现,因此进程可能会失败,并出现内存溢出问题。要克服这种情况,请尝试在调用操作之前重新划分Dataframe。

val df = df_temp.repartition(5)

请注意,您可能需要根据您的需求更改随机播放和分区计数。在我的情况下,上面的组合是有效的。
原因2
可能是由于内存未及时清除。例如,如果您正在使用scala运行spark命令,并且正在执行一堆sql语句并导出到csv。某些配置单元表中的数据将非常庞大,您必须管理代码中的内存。
例如,考虑下面的代码 lst_Sqls 是包含一组sql命令的列表

lst_Sqls.foreach(sqlCmd => spark.sql(sqlCmd).coalesce(1).write.format("com.databricks.spark.csv").option("delimiter","|").save("s3 path..."))

当你运行这个命令时,有时你会看到同样的错误。这是因为虽然spark清除内存,但它是以一种懒惰的方式进行的,即,您的循环将继续,但spark可能会在稍后的某个时间清除内存。
在这种情况下,您需要管理代码中的内存,即在执行每个命令后清除内存。为此,让我们稍微修改一下代码。我已经在下面的代码中注解了每一行的作用。

lst_Sqls.foreach(sqlCmd => 
 {          
      val df = spark.sql(sqlCmd) 
      // Store the result in in-memory. If in-memory is full, then it stored to HDD
      df.persist(StorageLevel.MEMORY_AND_DISK)
      // Export to csv from Dataframe
      df.coalesce(1).write.format("com.databricks.spark.csv").save("s3 path")
      // Clear the memory. Only after clearing memory, it will jump to next loop
      df.unpersist(blocking = true)
 })
btqmn9zl

btqmn9zl2#

所有容器和am的日志文件都可以在,

yarn logs -applicationId application_1480922439133_0845_02

如果你只想要am日志,

yarn logs -am -applicationId application_1480922439133_0845_02

如果你想找到为这个任务运行的容器,

yarn logs -applicationId application_1480922439133_0845_02|grep container_e33_1480922439133_0845_02

如果你只需要一个容器日志,

yarn logs -containerId container_e33_1480922439133_0845_02_000002

要使这些命令正常工作,必须将日志聚合设置为true,否则必须从各个服务器目录获取日志。
更新除了尝试交换之外,您什么也做不了,但这会降低性能很多。
gc开销限制意味着,gc一直连续不停地运行,但无法恢复太多内存。唯一的原因是,要么代码写得很差,并且有大量的反向引用(这是值得怀疑的,因为您正在进行简单的连接),要么内存容量已经达到。

nwlls2ji

nwlls2ji3#

我也遇到了这个问题,并试图通过参考一些博客来解决它。1.运行spark add conf bellow:

--conf 'spark.driver.extraJavaOptions=-XX:+UseCompressedOops -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps' \
--conf 'spark.executor.extraJavaOptions=-XX:+UseCompressedOops -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC  ' \

当使用jvm gc时,您将收到以下消息:

Heap after GC invocations=157 (full 98):
 PSYoungGen      total 940544K, used 853456K [0x0000000781800000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 860160K, 99% used [0x0000000781800000,0x00000007b5974118,0x00000007b6000000)
  from space 80384K, 0% used [0x00000007b6000000,0x00000007b6000000,0x00000007bae80000)
  to   space 77824K, 0% used [0x00000007bb400000,0x00000007bb400000,0x00000007c0000000)
 ParOldGen       total 2048000K, used 2047964K [0x0000000704800000, 0x0000000781800000, 0x0000000781800000)
  object space 2048000K, 99% used [0x0000000704800000,0x00000007817f7148,0x0000000781800000)
 Metaspace       used 43044K, capacity 43310K, committed 44288K, reserved 1087488K
  class space    used 6618K, capacity 6701K, committed 6912K, reserved 1048576K  
}

psyounggen和paroldgen都是99%,那么您将得到java.lang.outofmemoryerror:如果创建了更多对象,则超出了gc开销限制。
当有更多内存资源可用时,尝试为执行器或驱动程序添加更多内存:
--执行器内存10000m
--驾驶员记忆10000m
对于我的例子:psyounggen的内存比paroldgen小,这导致许多年轻对象进入paroldgen内存区域,最后paroldgen不可用。因此java.lang.outofmemoryerror:java堆空间错误出现。
正在为执行者添加conf:
'spark.executor.extrajavaoptions=-xx:newratio=1-xx:+usecompressedoops-verbose:gc -xx:+printgcdetails-xx:+printgctimestamps'
-xx:newratio=费率=paroldgen/psyounggen
它依赖于。你可以尝试gc策略,比如

-XX:+UseSerialGC :Serial Collector 
-XX:+UseParallelGC :Parallel Collector
-XX:+UseParallelOldGC :Parallel Old collector 
-XX:+UseConcMarkSweepGC :Concurrent Mark Sweep

java并发并行gc
如果第4步和第6步都完成了,但仍然出现错误,您应该考虑更改代码。例如,减少ml模型中的迭代器时间。

相关问题