spark on yarn作业运行的jar包缓存优化

本文探讨了Spark on YARN环境下Jar包的缓存及清理问题,包括HDFS上和NodeManager节点上的处理方法。通过对Spark配置和YARN参数的调整,实现了自动清理遗留的Jar包。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址

参考自下文

这几天一直在追查spark on yarn的作业运行中的jar包分发,以及执行后的jar包删除的问题。从一开始的毫无头绪,到后来逐渐清晰,到后来通过hadoop的两个很简单的参数配置解决了问题。不得不说,虽然问题不大,对某些大牛们来说也真是小case,但是追查问题,定位问题到最终解决问题的过程,对我来说真是很不错的体验和学习过程。下面详细描述一下遇到的问题,以及解决的过程,给后面的同学一点参考。
BTW,很多时候定位问题,查清楚问题的原因,比解决这个”问题“要更重要。

问题描述

Spark版本是1.0.2,运行在cdh5.1.0-hadoop2.3.0版本的yarn上面。在每次提交作业执行之后,在我的HDFS的${yarn.app.mapreduce.am.staging-dir}/${username}/.sparkStaging下面,就会产生两个jar包,一个是spark-assembly-*.jar,另一个是提交执行的jar包。同时,在这个作业对应的executor的节点上,${yarn.nodemanager.local-dirs}目录下,也会有这两个jar包。而且不会被删除。

光spark-assembly*.jar就有一百多M,每次执行完都会留下这么多jar包,虽然不大,但可想而知,日积月累,可是个很麻烦的事情。所以,要搞清楚为啥会出现这个情况。

这个问题可以拆分成两个sub-task,一个是HDFS上的jar包为啥不会自动删除,一个是nodemanager节点上的jar包的自动删除。下面分别排查和解决:

HDFS上的jar包缓存

网上貌似很少有相关资料,就是找到了一个介绍如何配置spark.yarn.jar的文章,说是可以解决HDFS上的jar包缓存的问题。照着配置了一下,不起作用,又看作者说,要spark1.1.0以上的版本,所以重新编译了spark1.1.0,把集群的spark1.0.2升级到1.1.0。重新配置spark.yarn.jar,发现问题解决了。HDFS上仍然在每次提交作业时都会被上传两个jar包,但是用完后会自动删除。

配置方法

(1) 首先需要确保spark在1.1.0以上的版本。

(2) 在HDFS上建立一个公共lib库,比如/system/spark-lib/,设置权限为755。把spark-assembly-*.jar上传到公共lib库中。

(3) 在spark-env.sh中配置:

spark.yarn.jar                          hdfs://yarncluster/system/spark_lib/spark-assembly-1.1.0-hadoop2.3.0-cdh5.1.0.jar  
spark.yarn.preserve.staging.files       false
  • spark.yarn.jar配置成HDFS上的公共lib库中的jar包。这个配置项会使提交job时,不是从本地上传spark-assembly*.jar包,而是从HDFS的一个目录复制到另一个目录(不确定HDFS上的复制是怎么操作的),总的来说节省了一点时间。(网上有的文章里说,这里的配置,会节省掉上传jar包的步骤,其实是不对的,只是把从本地上传的步骤改成了在HDFS上的复制操作。)
  • spark.yarn.preserve.staging.files: 这个配置项配置成false,表示在执行结束后,不保留staging
    files,也就是两个jar包。然后HDFS上的.sparkStaging下的两个jar包在作业执行完成后就会被删除。如果配置成true,执行完后HDFS上的.sparkStaging下两个jar包都会保存下来。

然后再运行,发现HDFS上.sparkStaging目录下不会再保留jar包。

问题定位

按道理来说,因为spark.yarn.preserve.staging.files默认是false,所以HDFS上的jar包是不会被保留的。但是在spark1.0.2中,却没有删除。我看了下1.0.2的代码,删除的机制是存在的:

//yarn/alpha/src/main/Scala/org/apache/spark/deploy/yarn/ApplicationMaster.scala 
/** 
   * Clean up the staging directory. 
   */  
  private def cleanupStagingDir() {  
    var stagingDirPath: Path = null  
    try {  
      val preserveFiles = sparkConf.get("spark.yarn.preserve.staging.files", "false").toBoolean  
      if (!preserveFiles) {  
        stagingDirPath = new Path(System.getenv("SPARK_YARN_STAGING_DIR"))  
        if (stagingDirPath == null) {  
          logError("Staging directory is null")  
          return  
        }  
        logInfo("Deleting staging directory " + stagingDirPath)  
        fs.delete(stagingDirPath, true)  
      }  
    } catch {  
      case ioe: IOException =>  
        logError("Failed to cleanup staging dir " + stagingDirPath, ioe)  
    }  
  }

按照这个逻辑,默认在AM关闭的时候,是会删除HDFS上的jar包的。不过没有正常删除。推测这应该是一个1.0.2里面的bug,而在1.1.0里面已经修复。

nodemanager节点上的jar包缓存

升级到1.1.0版本后,HDFS上的jar包问题就解决了。但是nodemanager节点上的jar包还是会保留。这个问题的定位很纠结,不过结果却出乎意料的简单。不说了,上结果吧。

配置方法

(1) 配置yarn-site.xml:

<property>
    <name>yarn.nodemanager.local-dirs</name>
    <value>local-dir1, local-dir2,local-dir3</value>
</property>
<property>
    <name>yarn.nodemanager.localizer.cache.target-size-mb</name>
    <value>1024</value>
</property>
<property>
    <name>yarn.nodemanager.localizer.cache.cleanup.interval-ms</name>
    <value>1800000</value>
</property>
  • yarn.nodemanager.local-dirs:
    这个目录是nodemanager上的作业中间数据存放路径。推荐配置多个盘上的多个路径,从而分散作业执行中的磁盘IO压力。
  • yarn.nodemanager.localizer.cache.target-size-mb:配置nodemanager上的缓存目录的最大限度。nodemanager上有一个deletion
    server服务,会定期检测,如果yarn.nodemanager.local-dirs中配置的目录大小(如果配置了多个,则计算多个目录的总大小)是否超过了这里设置的最大限度值。如果超过了,就删除一些已经执行完的Container的缓存数据。
  • yarn.nodemanager.localizer.cache.cleanup.interval-ms: deletion
    server多长时间做一次检测,并且清除缓存目录直到目录大小低于target-size-mb的配置。

因为spark提交作业后遗留在nodemanager上的jar包就在yarn.nodemanager.local-dirs下面,所以只要这里配置合适的大小值。那么nodemanager上的deletion server是会自动检测并保证目录总大小的。所以只要配置了这个量,我们就不需要再担心nodemanager上的jar包缓存问题了,交给yarn就好了!很简单啊有木有,可就这么个问题,居然花了我一个星期的时间去定位。

通过上面这三个量的配置,nodemanager会确保本地的缓存数据总量在target-size-mb之下,也就是超过了的话,之前的spark的jar包就会被删除。所以我们就不需要再担心nodemanager节点上的spark jar包缓存问题了。不过target-size-mb的默认值是10G,这个值当然可以根据你的实际情况进行调整。

问题定位

为什么mapreduce提交的任务在执行完后,会自动删除nodemanager上的所有中间文件,而spark却不会呢?

查看了下Hadoop的源码(相关代码就不贴出来了),MapReduce提交作业的时候,中间数据是以application file对象的方式被处理的,也就是在nodemanager上是存放在username/appcache/目录下的,而按照yarn的处理机制,application file是专门存放中间数据的对象,所以MapReduce作业的所有中间数据在作业执行完后会被yarn删除。

而spark on yarn的任务提交后,两个jar包却是以yarn的分布式缓存(distributed cache)的方式存放和处理的。Distributed Cache是yarn提供给用户用来分发和管理作业执行中用到的extra file的处理机制。spark没有调用yarn的application file对象,却提交成distributed cache类型,不知道为什么。不过这就是jar包最终没有被自动删除的原因。

下面是MapReduce任务提交后nm上分发中间文件的日志:

这里写图片描述

可以看到中间文件都被下载到appcache的目录下。

再看看spark任务提交后nm上分发jar包的日志:
这里写图片描述

很明显jar包被存放在filecache目录下。也就是它是作为distributed cache的方式分发到nm上的。不知道为什么spark采用这样的设计方式。file cache在作业结束后,是不会主动被删除的。

就是这样了,这个问题其实根本也算不上一个问题。也就是一个深入学习spark on yarn的架构和解惑的过程。

更新:

spark采用file cache而不是application cache的原因,是因为spark在每个NM上启动的是一个executor,每个task作为executor里的一个线程在运行。而只需要在executor启动时,在这个节点上用file cache机制分发一次jar包,然后在每个task执行时,能共享到通过file cache传过来的jar包。所以采用file cache机制的原因,是要在task之间共享jar包。

<think>我们正在使用Hiveon Spark,并且已经按照之前的步骤将Spark的依赖JAR上传到了HDFS,并在hive-site.xml中配置了spark.yarn.jars指向HDFS路径。现在需要验证Hiveon Spark是否真的使用了HDFS上的JAR,而不是从本地临时上传。验证方法可以从以下几个方面进行: 1. **查看YARN应用程序的日志**:在提交Hive onSpark任务后,通过YARN的日志查看Spark任务启动时的日志,特别是寻找是否有关于spark.yarn.jars的日志信息。2.**观察任务提交时的行为**:如果没有配置HDFS上的JAR路径,每次提交任务时,Spark都会将本地的JAR上传到YARN的临时目录(如/tmp),这会导致任务提交时间较长。如果配置正确,则提交任务时不会上传这些JAR,因此提交速度会明显加快。 3. **检查HDFS上的JAR访问时间**:如果HDFS上的JAR被使用,那么这些文件的访问时间会更新。 具体操作步骤:###方法1:通过YARN日志验证1.提交一个Hive onSpark任务,例如:```sqlSEThive.execution.engine=spark;SELECT count(*) FROMyour_table; --替换为实际存在的表```2.获取该任务对应的YARN ApplicationID。可以通过YARN ResourceManager的Web UI查看,或者通过命令行:```bashyarnapplication -list -appStatesRUNNING```3.查看该Application的日志,特别是AM(Application Master)的日志。可以使用以下命令:```bashyarnlogs -applicationId<your_application_id>| grep"spark.yarn.jars"```或者直接查看完整的AM日志,搜索关键字`spark.yarn.jars`。如果配置正确,日志中应该会显示类似这样的信息:```INFOspark.SparkContext: AddedJARs: hdfs://namenode:8020/spark-jars/xxx.jar,...```或者```INFOspark.SparkContext: Usingmasteryarnwith properties...spark.yarn.jars=hdfs://namenode:8020/spark-jars/*``` ###方法2:观察任务提交速度-在配置之前,提交一个较大的任务(比如含多个stage的任务)时,可能会在提交阶段花费较长时间(因为要上传大量JAR)。-配置之后,同样的任务提交速度应该明显加快(因为跳过了上传JAR的过程)。 ###方法3:检查HDFS文件访问时间1.在提交任务前,记录HDFS上SparkJAR目录下文件的访问时间(如果HDFS配置了访问时间跟踪):```bashhdfsdfs -ls /spark-jars``` 2.提交一个Hiveon Spark任务。3.任务完成后,再次检查HDFS上文件的访问时间。如果文件被使用,那么它们的访问时间应该更新为任务执行的时间(注意:HDFS默认不更新访问时间,需要配置`dfs.namenode.accesstime.precision`,但通常不开启。所以这个方法可能不适用,除非集群特别配置了访问时间更新)。 ###方法4:通过SparkWebUI验证(如果可访问)1.在任务运行时,访问Spark WebUI(通过YARN ApplicationMaster的TrackingURL进入)。 2.在Environment标签页中,查看`spark.yarn.jars`属性,确认其值是否为配置的HDFS路径。 ###方法5:检查YARN本地资源在YARN的NodeManager节点上,检查任务本地化资源目录(例如`yarn.nodemanager.local-dirs`配置的目录),通常位于`/tmp`或`/hadoop/yarn/local`。在任务运行期间,这些目录下会缓存从HDFS下载的JAR(作为本地资源)。如果配置了`spark.yarn.jars`,那么应该能看到这些JAR被下载到本地缓存。###常见问题排查- **路径错误**:确保`spark.yarn.jars`配置的路径正确,括NameNode的地址和端口(如8020)以及路径是否存在。- **权限问题**:运行YARN任务的用户(通常是提交任务的用户)必须有权限读取HDFS上的`/spark-jars`目录。 -**配置未生效**:确保修改的`hive-site.xml`已经分发到所有Hive服务节点,并且重启了HiveServer2和MetaStore服务。 通过以上方法,特别是查看YARN日志,可以明确知道是否成功使用了HDFS上的JAR。[^1]:Hive3.1.2支持的Spark是2.4.5,所以需要将下载的 Hive3.1.2的源码中的pom文件中的Spark版本改为3.0.0,然后再编译打,得到支持 Spark3.0.0的Jar[^2]:说明2:Hive任务最终由Spark来执行,Spark任务资源分配由Yarn来调度,该任务有可能被分配到集群的任何一个节点。所以需要将Spark的依赖上传到HDFS集群路径,这样集群中任何一个节点都能获取到hdfsdfs -put /opt/server/spark/jars/* /spark-jars/3.5.修改hive-site.xml文件[^3]:背景:Hive默认使用MapReduce作为执行引擎,即Hiveon mr。实际上,Hive还可以使用Tez和Spark作为其执行引擎,分别为Hive onTez和Hive onSpark。由于MapReduce中间计算均需要写入磁盘,而Spark是放在内存中,所以总体来讲Spark比MapReduce快很多。因此,Hive onSpark也会比Hive onmr快。为了对比Hive onSpark和Hiveon mr的速度,需要在已经安装了Hadoop集群的机器上安装Spark集群(Spark集群是建立在Hadoop集群之上的,也就是需要先装Hadoop集群,再装Spark集群,因为Spark用了Hadoop的HDFS、YARN等),然后把Hive的执行引擎设置为Spark。[^4]:hive onspark的架构和常见问题 -hive onspark使用的是 yarnclient模式还是yarn cluster模式?。hiveon spark的架构和常见问题- hiveon spark使用的是yarn client模式还是 yarncluster模式?1.回顾下spark的架构图和部署模式§§相关问题§§1.如何解决Hiveon Spark任务提交时出现的`ClassNotFoundException`错误?2.如何为Hiveon Spark动态更新HDFS中的Spark依赖版本?3.如何优化Hiveon Spark任务YARN资源分配以提高性能? 4.在Hive onSpark中,如何配置和管理Executor的内存和CPU资源?</think>### 如何验证 Hive on Spark 是否成功使用 HDFS 上的 JAR 以下是 5 种验证方法,按执行难度排序(从简单到复杂): #### 1. **检查 YARN 日志中的加载记录(推荐)** ```bash yarn logs -applicationId <your_app_id> | grep "spark.yarn.jars" ``` - **预期结果**: `INFO SparkContext: Added JARs: hdfs://namenode:8020/spark-jars/*.jar at ...`[^2] - **关键点**:日志中应显示从 HDFS 路径加载 JAR,而非本地路径(如 `/tmp`) #### 2. **观察任务提交速度** - **未使用 HDFS JAR 的表现**: 任务提交耗时 >30 秒(需上传数百 MB JARYARN) - **成功使用 HDFS JAR 的表现**: 提交时间 <5 秒(直接引用 HDFS 资源)[^2] #### 3. **检查 HDFS 访问记录** ```bash # 1. 记录 JAR 目录初始访问时间 hdfs dfs -ls /spark-jars > before.txt # 2. 提交测试任务 hive -e "SET hive.execution.engine=spark; SELECT count(*) FROM your_table;" # 3. 检查访问时间更新 hdfs dfs -ls /spark-jars > after.txt diff before.txt after.txt ``` - **验证点**:任务执行后 JAR 文件的访问时间(`atime`)应更新 #### 4. **Spark Web UI 验证** 1. 通过 YARN RM 进入 Spark Application UI 2. 查看 **Environment** 标签页: - `spark.yarn.jars` 的值应为 HDFS 路径 - `Jars` 列表应显示 `hdfs://` 开头的路径[^4] #### 5. **文件系统监控(高级)** 在 YARN NodeManager 节点执行: ```bash # 监控本地临时目录 watch "ls -l /tmp | grep spark" ``` - **正确情况**:任务运行时不会在 `/tmp` 生成大量 `spark-*.jar` 文件 - **错误情况**:出现类似 `spark-12345.jar.tmp` 的临时文件 --- ### 验证结果解读表 | 验证方法 | 成功标志 | 失败原因排查 | |---------|---------|-------------| | YARN 日志 | 显示 HDFS 路径加载 | 1. 配置未生效<br>2. 路径权限不足 | | 提交速度 | <5 秒快速提交 | 1. JAR 路径错误<br>2. 网络延迟 | | HDFS 访问时间 | JAR 文件 atime 更新 | 1. 路径未匹配<br>2. 缓存未刷新 | | Spark UI | Environment 显示 HDFS 路径 | 1. 配置未分发<br>2. 服务未重启 | | 本地临时文件 | 无新 JAR 文件生成 | 1. 配置被覆盖<br>2. Spark 版本不匹配 | --- ### 常见问题处理 若验证失败,检查以下配置: ```xml <!-- 确保路径含 NameNode 地址和通配符 --> <property> <name>spark.yarn.jars</name> <value>hdfs://active-namenode:8020/spark-jars/*</value> </property> ``` - **权限修复命令**: ```bash hdfs dfs -chmod -R 755 /spark-jars hdfs dfs -chown yarn:yarn /spark-jars ``` > **关键提示**:在 HA 集群中必须使用逻辑命名空间(如 `hdfs://mycluster/spark-jars/*`),否则切换 NameNode 会导致路径失效[^2]。 通过以上方法,可 100% 确认 Hive on Spark 是否正确使用 HDFS JAR 。通常在日志中看到 `Added JARs from HDFS` 即为成功[^2][^4]。 [^1]:Hive3.1.2 支持的 Spark 是 2.4.5,所以需要将下载的 Hive3.1.2 的源码中的 pom 文件中的 Spark 版本改为 3.0.0,然后再编译打,得到支持 Spark 3.0.0 的 Jar [^2]:说明2:Hive任务最终由Spark来执行,Spark任务资源分配由Yarn来调度,该任务有可能被分配到集群的任何一个节点。所以需要将Spark的依赖上传到HDFS集群路径,这样集群中任何一个节点都能获取到 hdfs dfs -put /opt/server/spark/jars/* /spark-jars/ 3.5. 修改hive-site.xml文件 [^3]:背景:Hive默认使用MapReduce作为执行引擎,即Hive on mr。实际上,Hive还可以使用Tez和Spark作为其执行引擎,分别为Hive on Tez和Hive on Spark。由于MapReduce中间计算均需要写入磁盘,而Spark是放在内存中,所以总体来讲Spark比MapReduce快很多。因此,Hive on Spark也会比Hive on mr快。为了对比Hive on Spark和Hive on mr的速度,需要在已经安装了Hadoop集群的机器上安装Spark集群(Spark集群是建立在Hadoop集群之上的,也就是需要先装Hadoop集群,再装Spark集群,因为Spark用了Hadoop的HDFS、YARN等),然后把Hive的执行引擎设置为Spark。 [^4]:hive on spark 的架构和常见问题 - hive on spark 使用的是 yarn client 模式还是 yarn cluster 模式?。hive on spark 的架构和常见问题 - hive on spark 使用的是 yarn client 模式还是 yarn cluster 模式? 1. 回顾下 spark 的架构图和部署模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值