AWS EMR Spark 集群 - 使用 Scala fat JAR 的步骤,找不到 MainClass

kmh*_*kmh 5 scala jar executable-jar amazon-emr spark-submit

我有一个 fat jar,用 Scala 编写,由 sbt 打包。我需要在 AWS EMR 的 Spark 集群中使用它。

如果我手动启动集群,将 jar 复制到 master 并使用这样的命令运行 Spark-submit 作业,它的功能就很好......

spark-submit --class org.company.platform.package.SparkSubmit --name platform ./platform-assembly-0.1.0.jar arg0 arg1 arg2
Run Code Online (Sandbox Code Playgroud)

但是...如果我尝试将其作为步骤添加到 EMR 集群,则会失败。stderr 的日志看起来像这样......

Exception in thread "main" java.lang.ClassNotFoundException: package.SparkSubmit
  at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
  at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
  at java.lang.Class.forName0(Native Method)
  at java.lang.Class.forName(Class.java:278)
  at org.apache.hadoop.util.RunJar.run(RunJar.java:214)
  at org.apache.hadoop.util.RunJar.main(RunJar.java:136)
Run Code Online (Sandbox Code Playgroud)

我的 build.sbt 中的相关设置如下所示......

lazy val root = (project in file(".")).
  settings(
    name := "platform",
    version := "0.1.0",
    scalaVersion := "2.10.5",
    organization := "org.company",
    mainClass in Compile := Some("package/SparkSubmit")
  )
Run Code Online (Sandbox Code Playgroud)

我的 MainClass 的相应文件看起来像......

package org.company.platform.package

object SparkSubmit {
  def main(args: Array[String]): Unit = {
    // do stuff
  }
}
Run Code Online (Sandbox Code Playgroud)

在 EMR 控制台中...在“添加步骤”对话框中...“参数”框旁边,它显示...

“这些被传递到 JAR 中的主函数。如果 JAR 未在其清单文件中指定主类,您可以指定另一个类名作为第一个参数。”

我想因为我确实在 build.sbt 中指定了一个主类,所以我会没事......但它失败了并且不会记录有关失败的任何内容。如果我尝试将主类指定为第一个参数,它会记录我上面发布的失败。

我认为这可能是格式问题,但我不知道如何修复它,也没有出现任何示例。我尝试在“添加步骤”对话框中提交以下内容作为参数...

arg0 arg1 arg2
package.SparkSubmit arg0 arg1 arg2
package/SparkSubmit arg0 arg1 arg2
org.company.platform.package.SparkSubmit arg0 arg1 arg2
Run Code Online (Sandbox Code Playgroud)

还有其他一些,但没有任何作用。

版本信息... EMR 4.3 Spark 1.6 Scala 2.10 sbt 0.13.9

你知道我犯了什么愚蠢的错误吗?没有让 EMR/Spark 找到我的主类?

谢谢。

kmh*_*kmh 2

编辑- 通过使问题 1-6 消失来使其“工作”,但随后集群只是坐在那里说它正在“运行”第一步,但它从未完成。我错误地将步骤类型设置为“自定义 jar”而不是“spark 应用程序”。切换后,我认为只有“问题 1”的修复是相关的,仅此一项就可能解决了我的问题。我必须取消对下面问题 2、3 和 5 的修复,才能使其与“spark 应用程序”步骤一起工作,而且我怀疑我也可以取消其余的问题。 结束编辑

我花了很长时间才让它发挥作用。我将按顺序发布错误和修复,以防对其他人有用。

问题1

无论我作为第一个参数传递什么来尝试指向 MainClass...我都得到了同样的错误。问题出在我的 build.sbt 中。我(错误地)认为根目录中的组织和名称足以提供包前缀。

我更改了 build.sbt 中的 mainClass,以将文件顶部声明的包与其中的 SparkSubmit 对象相匹配...

mainClass in Compile := Some("org.company.platform.package.SparkSubmit")
Run Code Online (Sandbox Code Playgroud)

然后在“添加步骤”对话框中,我只是传递了参数,没有类指定......所以只是“arg0 arg1 arg2”。

如果您想在清单中与运行中设置不同的 MainClass,那么有趣的参考... 如何在构建中设置主类?

问题2

Exception in thread "main" org.apache.spark.SparkException: A master URL must be set in your configuration
Run Code Online (Sandbox Code Playgroud)

我找到了这个参考... https://spark.apache.org/docs/latest/submitting-applications.html#master-urls

我不知道该使用哪一个,但由于 EMR 使用 Yarn,所以我将其设置为“yarn”。 这是错误的。 (将其保留为它生成的后续错误的记录)在 SparkSubmit.main() 中,我像这样设置主 URL...

val conf = 
  new SparkConf()
  .setMaster("yarn")
  .setAppName("platform")
Run Code Online (Sandbox Code Playgroud)

问题3

主 URL 错误消失了,现在这是我的错误......

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/SparkConf
Run Code Online (Sandbox Code Playgroud)

在我的 build.sbt 中...我将 Spark-core 和 Spark-sql 在 LibraryDependency 中列为“提供”...我不知道为什么这不能作为 EMR 步骤工作,因为集群已加载 Spark...但我删除了它并将其更改为...

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-core" % "1.6.0", // % "provided",
  "org.apache.spark" %% "spark-sql" % "1.6.0", //  % "provided",
  ...
)
Run Code Online (Sandbox Code Playgroud)

注意 - 删除“provided”后,我收到了一个新错误,但将 Spark-core 和 Spark-sql 的版本更改为 1.6.0 以匹配 EMR 4.3 后,该错误就消失了。

问题解决了...新问题创建了!

问题4

Exception in thread "main" com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'akka.version'
Run Code Online (Sandbox Code Playgroud)

答案就在这里...... https://doc.akka.io/docs/akka/snapshot/general/configuration.html#when-using-jarjar-onejar- assembly-or-any-jar-bundler

基本上,Akka 的reference.conf 丢失了。我的 build.sbt mergeStrategy 看起来像这样......

mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
   {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case _ => MergeStrategy.first
   }
}
Run Code Online (Sandbox Code Playgroud)

我将其修改为看起来像这样......

mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
   {
    case "reference.conf" => MergeStrategy.concat
    case "application.conf" => MergeStrategy.concat
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case _ => MergeStrategy.first
   }
}
Run Code Online (Sandbox Code Playgroud)

问题5

我猜“yarn”在问题 2 中不是正确的选择。我得到了这个错误......

Exception in thread "main" org.apache.spark.SparkException: Could not parse Master URL: 'yarn'
Run Code Online (Sandbox Code Playgroud)

我将网址更改为“local[2]”...

val conf = 
  new SparkConf()
  .setMaster("local[2]")
  .setAppName("starling_for_mongo")
Run Code Online (Sandbox Code Playgroud)

该值没有有效的理由...不确定我实际需要多少个线程...或者甚至在哪里应用它...是在主服务器中,还是在某个虚拟机中...我不确定。需要更多地了解这一点,但我只是复制了这里的内容,因为......呃......为什么不呢? https://spark.apache.org/docs/1.6.1/configuration.html#spark-properties

需要了解这里设置的内容。

问题6

接下来出现了很多序列化错误。我不明白为什么,当所有这些代码作为手动 Spark-submit 或在 Spark-shell 中运行时没有任何问题。我通过本质上遍历并使每个类都扩展可序列化来修复它。

结束

这是我获得一个用 scala 编写的工作 jar 并使用 sbt 编译以充当 EMR Spark 集群中的一个步骤的旅程。我希望这对其他人有帮助。