【问题标题】:Add JAR files to a Spark job - spark-submit将 JAR 文件添加到 Spark 作业 - spark-submit
【发布时间】:2016-09-05 01:54:19
【问题描述】:

确实……已经讨论了很多。

但是,存在很多歧义,并且提供了一些答案...包括在 jars/executor/driver 配置或选项中复制 JAR 引用。

模棱两可和/或遗漏的细节

应为每个选项澄清以下模棱两可、不清楚和/或遗漏的细节:

  • ClassPath 如何受到影响
    • 驱动程序
    • 执行器(用于正在运行的任务)
    • 两者
    • 根本没有
  • 分隔符:逗号、冒号、分号
  • 如果提供的文件是自动分发的
    • 任务(对每个执行者)
    • 用于远程驱动程序(如果在集群模式下运行)
  • 接受的 URI 类型:本地文件、HDFS、HTTP 等。
  • 如果复制一个公共位置,该位置在哪里(HDFS,本地?)

它影响的选项:

  1. --jars
  2. SparkContext.addJar(...)方法
  3. SparkContext.addFile(...)方法
  4. --conf spark.driver.extraClassPath=...--driver-class-path ...
  5. --conf spark.driver.extraLibraryPath=...,或--driver-library-path ...
  6. --conf spark.executor.extraClassPath=...
  7. --conf spark.executor.extraLibraryPath=...
  8. 别忘了,spark-submit 的最后一个参数也是一个.jar 文件。

我知道在哪里可以找到main Apache Spark documentation,特别是how to submitoptions 可用,还有JavaDoc。然而,这给我留下了相当多的漏洞,尽管它也得到了部分答案。

我希望它不是那么复杂,并且有人可以给我一个清晰简洁的答案。

如果我从文档中猜测,似乎 --jarsSparkContext addJaraddFile 方法将自动分发文件,而其他选项仅修改 ClassPath。

是否可以假设为简单起见,我可以同时使用三个主要选项添加其他应用程序 JAR 文件?

spark-submit --jar additional1.jar,additional2.jar \
  --driver-library-path additional1.jar:additional2.jar \
  --conf spark.executor.extraLibraryPath=additional1.jar:additional2.jar \
  --class MyClass main-application.jar

我在an answer to another posting 上找到了一篇不错的文章。然而,并没有学到什么新东西。海报确实很好地说明了本地驱动程序(纱线客户端)和远程驱动程序(纱线集群)之间的区别。一定要牢记这一点。

【问题讨论】:

  • 您在哪个集群管理器下运行?独立/YARN/Mesos?
  • 任何。我打算将此作为对原始文档的澄清。我主要使用独立集群、单实例、纱线客户端、纱线集群。其他人可能正在使用 Mesos。看来你对your blog 做了一些很好的原创研究。我最终做了和你差不多的事情——使用着色器创建一个 Uber jar 来简化我的部署过程。
  • @Yuval Itzchakov,就像 Yoyo 提到的那样,我也使用阴影 jar 来捆绑我所有的依赖项,例如案例类和我可能正在使用的其他罐子。我试图了解何时会遇到需要多个罐子的情况。我的意思是我总是可以将这些多个罐子捆绑到 1 个超级罐子中。为什么我不能继续使用捆绑所有依赖项的阴影 jar 包?
  • multijar uber 捆绑包在多用户环境中不实用,其中一些用户真的不是那么专家,他们只是对运行 Python 逻辑感兴趣,而不知道应该使用哪个雪花 jdbc jar 文件被添加到捆绑包中。这就是 SRE 的用武之地。

标签: java scala apache-spark jar spark-submit


【解决方案1】:

类路径:

ClassPath 会受到影响,具体取决于您提供的内容。有几种方法可以在类路径上设置一些东西:

  • spark.driver.extraClassPath 或别名 --driver-class-path 用于在运行驱动程序的节点上设置额外的类路径。
  • spark.executor.extraClassPath 在 Worker 节点上设置额外的类路径。

如果您希望某个 JAR 同时作用于 Master 和 Worker,则必须在 BOTH 标志中分别指定这些。

分隔符:

Following the same rules as the JVM:

  • Linux:冒号,:
    • 例如:--conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar:/opt/prog/aws-java-sdk-1.10.50.jar"
  • Windows:分号,;
    • 例如:--conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar;/opt/prog/aws-java-sdk-1.10.50.jar"

文件分发:

这取决于您运行作业的模式:

  1. 客户端模式 - Spark 启动一个Netty HTTP 服务器,该服务器在启动时为每个工作节点分发文件。当您开始 Spark 作业时,您会看到:

    16/05/08 17:29:12 INFO HttpFileServer: HTTP File server directory is /tmp/spark-48911afa-db63-4ffc-a298-015e8b96bc55/httpd-84ae312b-5863-4f4c-a1ea-537bfca2bc2b
    16/05/08 17:29:12 INFO HttpServer: Starting HTTP Server
    16/05/08 17:29:12 INFO Utils: Successfully started service 'HTTP file server' on port 58922.
    16/05/08 17:29:12 INFO SparkContext: Added JAR /opt/foo.jar at http://***:58922/jars/com.mycode.jar with timestamp 1462728552732
    16/05/08 17:29:12 INFO SparkContext: Added JAR /opt/aws-java-sdk-1.10.50.jar at http://***:58922/jars/aws-java-sdk-1.10.50.jar with timestamp 1462728552767
    
  2. 集群模式 - 在集群模式下,Spark 选择了一个领导 Worker 节点来执行 Driver 进程。这意味着作业不是直接从 Master 节点运行的。在这里,Spark 不会设置 HTTP 服务器。您必须通过 HDFS、S3 或其他可用于所有节点的源手动使您的 JAR 文件可用于所有工作节点。

已接受的文件 URI

"Submitting Applications" 中,Spark 文档很好地解释了可接受的文件前缀:

使用 spark-submit 时,应用程序 jar 以及任何 jar 包含在 --jars 选项中的将自动转移到 集群。 Spark 使用以下 URL 方案来允许不同的 传播罐子的策略:

  • file: - 绝对路径和 file:/ URI 由驱动程序的 HTTP 提供 文件服务器,每个执行程序从驱动程序 HTTP 拉文件 服务器。
  • hdfs:, http:, https:, ftp: - 这些下拉文件和 JAR 来自 URI 的预期
  • local: - 以 local:/ 开头的 URI 期望作为本地文件存在于每个工作节点上。这意味着 不会产生网络 IO,并且适用于大文件/JAR 推送给每个工作人员,或通过 NFS、GlusterFS 等共享。

请注意,每个 JAR 文件和文件都会复制到工作目录中 执行器节点上的 SparkContext。

如上所述,JAR 文件被复制到每个 Worker 节点的工作目录。那具体在哪里?它通常/var/run/spark/work下,你会看到它们是这样的:

drwxr-xr-x    3 spark spark   4096 May 15 06:16 app-20160515061614-0027
drwxr-xr-x    3 spark spark   4096 May 15 07:04 app-20160515070442-0028
drwxr-xr-x    3 spark spark   4096 May 15 07:18 app-20160515071819-0029
drwxr-xr-x    3 spark spark   4096 May 15 07:38 app-20160515073852-0030
drwxr-xr-x    3 spark spark   4096 May 15 08:13 app-20160515081350-0031
drwxr-xr-x    3 spark spark   4096 May 18 17:20 app-20160518172020-0032
drwxr-xr-x    3 spark spark   4096 May 18 17:20 app-20160518172045-0033

当您查看内部时,您会看到您部署的所有 JAR 文件:

[*@*]$ cd /var/run/spark/work/app-20160508173423-0014/1/
[*@*]$ ll
total 89988
-rwxr-xr-x 1 spark spark   801117 May  8 17:34 awscala_2.10-0.5.5.jar
-rwxr-xr-x 1 spark spark 29558264 May  8 17:34 aws-java-sdk-1.10.50.jar
-rwxr-xr-x 1 spark spark 59466931 May  8 17:34 com.mycode.code.jar
-rwxr-xr-x 1 spark spark  2308517 May  8 17:34 guava-19.0.jar
-rw-r--r-- 1 spark spark      457 May  8 17:34 stderr
-rw-r--r-- 1 spark spark        0 May  8 17:34 stdout

受影响的选项:

要了解的最重要的事情是优先级。如果您通过代码传递任何属性,它将优先于您通过spark-submit 指定的任何选项。 Spark 文档中提到了这一点:

任何指定为标志或属性文件中的值都将被传递 在应用程序上并与通过指定的应用程序合并 火花会议。 直接在 SparkConf 上设置的属性最高 优先级,然后将标志传递给 spark-submit 或 spark-shell,然后 spark-defaults.conf 文件中的选项

因此,请确保将这些值设置在适当的位置,这样当一个优先于另一个时,您不会感到惊讶。

让我们分析问题中的每个选项:

  • --jars vs SparkContext.addJar:它们是相同的。只有一个是通过 Spark 提交设置的,一个是通过代码设置的。选择一个更适合你的。需要注意的重要一点是,使用这些选项中的任何一个不会将 JAR 文件添加到您的驱动程序/执行程序类路径。您需要在两者上使用 extraClassPath 配置显式添加它们。
  • SparkContext.addJar vs SparkContext.addFile:当你有一个依赖需要与你的代码一起使用时,使用前者。当您只想将任意文件传递给工作节点时使用后者,这不是代码中的运行时依赖项。
  • --conf spark.driver.extraClassPath=...--driver-class-path:这些是别名,你选择哪一个并不重要
  • --conf spark.driver.extraLibraryPath=..., or --driver-library-path ... 同上,别名。
  • --conf spark.executor.extraClassPath=...:当您有一个无法包含在 über JAR 中的依赖项(例如,因为库版本之间存在编译时冲突)并且需要在运行时加载时,请使用此选项。
  • --conf spark.executor.extraLibraryPath=... 这是作为 JVM 的 java.library.path 选项传递的。当您需要对 JVM 可见的库路径时,请使用此选项。

是否可以安全地假设为简单起见,我可以添加额外的 同时使用 3 个主要选项的应用程序 jar 文件:

您可以放心地假设这仅适用于 Client 模式,而不适用于 Cluster 模式。正如我之前所说。此外,您给出的示例有一些多余的论点。例如,将 JAR 文件传递​​给 --driver-library-path 是没有用的。如果您希望它们位于您的类路径中,则需要将它们传递给 extraClassPath。最终,当您在驱动程序和工作程序上部署外部 JAR 文件时,您希望:

spark-submit --jars additional1.jar,additional2.jar \
  --driver-class-path additional1.jar:additional2.jar \
  --conf spark.executor.extraClassPath=additional1.jar:additional2.jar \
  --class MyClass main-application.jar

【讨论】:

  • 伟大而全面的答案。谢谢你。您能否详细介绍一下使用 uber JARJAR 外部依赖项(外部文件夹中的库并在 MANIFEST.MF 文件中列出)的最佳实践?
  • @jsosnowski 通常,当我的 uber JAR 存在非常复杂的冲突时,我只会推迟使用外部 jar。我通常只需使用 SBT assemblyMergeStrategy 并在有冲突时选择我需要的类即可。我通常会推荐相同的。
  • @yuval-itzchakov 感谢您的出色回答,非常有帮助。我想强调一点,以帮助可能与我犯同样错误的其他人。 --jars 参数仅将 jar 传输到集群中的每台机器。它不会告诉 spark 在类路径搜索中使用它们。 --driver-class-path(或类似的参数或配置参数)也是必需的。我最初认为它们是做同样事情的替代方法。
  • @TimRyan 绝对是。如果您查看答案的最后一部分,我会将 jars 传递给 --jars 标志 驱动程序/执行程序类路径。
  • 最终我在zeppelin-env.sh 中找到了how to inject 环境变量并将--jars 添加到SPARK_SUBMIT_OPTIONS。那行得通。我使用的 URI 格式是--jars=local:///mnt/dir/file.jar
【解决方案2】:

Apache Spark 2.1.0 中的另一种方法是在 spark-submit 期间使用--conf spark.driver.userClassPathFirst=true,这会改变依赖加载的优先级,从而改变 spark-job 的行为,通过优先考虑用户所在的 JAR 文件使用 --jars 选项添加到类路径。

【讨论】:

  • 你必须小心这一点 - 因为这样做可能会破坏火花。这应该是最后的选择解决方案。在纱线客户端模式下使用时,它也可能会干扰与纱线接口的层,尽管我不确定。
  • 感谢您的提醒。有没有办法只优先考虑 1 个 jar,它肯定存在于旧版本的服务器中,但您无法物理替换并且您知道您不想使用?
  • 我想在这种情况下,您可以完全按照您的建议尝试。没说绝对不行。另请注意,该选项被标记为“实验性” - 需要注意的警告!没有一种安全的方法可以将库的一个版本优先于另一个版本。在某些实现中,这是通过将其中一个库移动到不同的命名空间来解决的,因此您可以同时使用这两个版本。
【解决方案3】:

使用--jars有一个限制:如果你想为jar/xml文件的位置指定一个目录,它不允许目录扩展。这意味着如果您需要为每个 JAR 文件指定一个绝对路径。

如果您指定--driver-class-path 并且您在纱线集群模式下执行,那么驱动程序类不会得到更新。我们可以在 Tab 环境下的 Spark UI 或 Spark 历史服务器下验证类路径是否更新。

--conf 选项对我有用以传递包含目录扩展并在纱线集群模式下工作的 JAR 文件。最好将驱动程序和执行程序类路径作为--conf 传递,这会将它们添加到 Spark 会话对象本身,并且这些路径会反映在 Spark 配置中。但是确保将 JAR 文件放在集群中的同一路径上。

spark-submit \
  --master yarn \
  --queue spark_queue \
  --deploy-mode cluster    \
  --num-executors 12 \
  --executor-memory 4g \
  --driver-memory 8g \
  --executor-cores 4 \
  --conf spark.ui.enabled=False \
  --conf spark.driver.extraClassPath=/usr/hdp/current/hbase-master/lib/hbase-server.jar:/usr/hdp/current/hbase-master/lib/hbase-common.jar:/usr/hdp/current/hbase-master/lib/hbase-client.jar:/usr/hdp/current/hbase-master/lib/zookeeper.jar:/usr/hdp/current/hbase-master/lib/hbase-protocol.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/scopt_2.11-3.3.0.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/spark-examples_2.10-1.1.0.jar:/etc/hbase/conf \
  --conf spark.hadoop.mapred.output.dir=/tmp \
  --conf spark.executor.extraClassPath=/usr/hdp/current/hbase-master/lib/hbase-server.jar:/usr/hdp/current/hbase-master/lib/hbase-common.jar:/usr/hdp/current/hbase-master/lib/hbase-client.jar:/usr/hdp/current/hbase-master/lib/zookeeper.jar:/usr/hdp/current/hbase-master/lib/hbase-protocol.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/scopt_2.11-3.3.0.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/spark-examples_2.10-1.1.0.jar:/etc/hbase/conf \
  --conf spark.hadoop.mapreduce.output.fileoutputformat.outputdir=/tmp

【讨论】:

    【解决方案4】:

    虽然我们使用 spark-submit 实用程序提交 Apache Spark 作业,但有一个选项 --jars 。使用此选项,我们可以将 JAR 文件传递​​给 Spark 应用程序。

    【讨论】:

    • 原始发帖人提到了这个—jar 选项,并且不止一个答案进行了更详细的讨论。您似乎没有提供任何新内容?
    【解决方案5】:

    yarn作为部署模式的情况下,与JAR文件和类路径相关的其他可配置Spark选项如下。

    来自 Spark 文档,

    spark.yarn.jars

    包含要分发到 YARN 容器的 Spark 代码的库列表。 默认情况下,YARN 上的 Spark 将使用本地安装的 Spark jar,但 Spark jar 也可以 在 HDFS 上一个世界可读的位置。这允许 YARN 将其缓存在节点上,这样它就不会 每次应用程序运行时都需要分发。例如,要指向 HDFS 上的 jar, 将此配置设置为 hdfs:///some/path。允许使用 Glob。

    spark.yarn.archive

    包含分发到 YARN 缓存所需的 Spark jar 的存档。如果设置,这 配置替换了 spark.yarn.jars 并且存档用于所有 应用程序的容器。存档应在其根目录中包含 jar 文件。 与上一个选项一样,存档也可以托管在 HDFS 上以加速文件 分布。

    用户可以配置此参数来指定他们的 JAR 文件,这些文件又会包含在 Spark 驱动程序的类路径中。

    【讨论】:

      【解决方案6】:

      将 spark-submit 与 --master yarn-cluster 一起使用时,应用程序 JAR 文件以及包含在 --jars 选项中的任何 JAR 文件将自动传输到集群。 --jars 之后提供的 URL 必须用逗号分隔。该列表包含在驱动程序和执行程序类路径中

      例子:

      spark-submit --master yarn-cluster --jars ../lib/misc.jar, ../lib/test.jar --class MainClass MainApp.jar
      

      参考

      Submitting Applications

      【讨论】:

        猜你喜欢
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-13
        • 2017-05-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多