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

AWS EMR Spark Cluster - Steps with Scala fat JAR, can't find MainClass

我有一个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

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

Exception in thread "main" java.lang.ClassNotFoundException: package.SparkSubmit
  at java.net.URLClassLoader.run(URLClassLoader.java:366)
  at java.net.URLClassLoader.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)

我的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")
  )

与我的 MainClass 对应的文件看起来像...

package org.company.platform.package

object SparkSubmit {
  def main(args: Array[String]): Unit = {
    // do stuff
  }
}

在 EMR 控制台中...在 "Add Step" 对话框中...在 "Arguments" 框旁边,它说...

"These are passed to the main function in the JAR. If the JAR does not specify a main class in its manifest file you can specify another class name as the first argument."

我想因为我确实在 build.sbt 中指定了一个主要的 class,所以我会没事的......但是它失败了并且没有记录任何关于失败的信息。如果我尝试将 main class 指定为第一个 arg,它会记录我在上面发布的失败。

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

arg0 arg1 arg2
package.SparkSubmit arg0 arg1 arg2
package/SparkSubmit arg0 arg1 arg2
org.company.platform.package.SparkSubmit arg0 arg1 arg2

还有其他一些,但没有任何效果。

版本信息... 电子病历 4.3 火花 1.6 斯卡拉 2.10 sbt 0.13.9

不知道我犯了什么愚蠢的错误,就是没有让 EMR/Spark 找到我的主要 class?

谢谢。

编辑 - 通过解决问题 1-6 将其变为 "work",但集群只是坐在那里说它是 "running" 第一步,却始终没有完成。我错误地将步骤类型设置为 "custom jar" 而不是 "spark application"。切换后,我认为只有 "problem 1" 的修复是相关的,仅此一项就可以解决我的问题。我不得不取消对下面问题 2、3 和 5 的修复,以使其与 "spark application" 步骤一起工作,而且我怀疑我也可以取消其余的问题。 结束编辑

我花了很长时间才让它起作用。我会 post 错误并按顺序修复,以防它对以后的其他人有用。

问题 1

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

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

mainClass in Compile := Some("org.company.platform.package.SparkSubmit")

然后在 "Add Step" 对话框中,我只是传入了 args,没有 class 指定...所以只是 "arg0 arg1 arg2".

如果您想在清单中设置不同的 MainClass 与 运行...How to set main class in build?

,这是一个有趣的参考

问题2

Exception in thread "main" org.apache.spark.SparkException: A master URL must be set in your configuration

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

不知道用哪个,因为EMR用的是Yarn,所以设置成"yarn"。 这是错误的。(留下它作为它生成的后续错误的记录)在 SparkSubmit.main() 中,我这样设置 master URL...

val conf = 
  new SparkConf()
  .setMaster("yarn")
  .setAppName("platform")

问题 3

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

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/SparkConf

在我的 build.sbt 中...我在 libraryDependencies 中将 spark-core 和 spark-sql 列为 "provided"...我不知道为什么这不起作用作为 EMR 步骤,因为集群已加载 Spark...但我删除了它并将其更改为...

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-core" % "1.6.0", // % "provided",
  "org.apache.spark" %% "spark-sql" % "1.6.0", //  % "provided",
  ...
)

注意 - 删除 "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'

答案在这里... 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
   }
}

我把它改成了这样...

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
   }
}

问题5

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

Exception in thread "main" org.apache.spark.SparkException: Could not parse Master URL: 'yarn'

我将 url 更改为 "local[2]"...

val conf = 
  new SparkConf()
  .setMaster("local[2]")
  .setAppName("starling_for_mongo")

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

需要了解此处设置的内容。

问题 6

接下来出现了很多序列化错误。我不明白为什么,当所有这些代码 运行 作为手动 spark-submit 或 spark-shell 没有任何问题时。我基本上通过并使每个 class extend Serializable.

来修复它

结束

那是我获得一个用 scala 编写的工作 jar 并用 sbt 编译以作为 EMR spark 集群中的一个步骤的旅程。我希望这可以帮助其他人。