代码之家  ›  专栏  ›  技术社区  ›  kmh

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

  •  4
  • kmh  · 技术社区  · 7 年前

    我有一个胖罐子,用Scala编写,由sbt打包。我需要在AWS EMR的Spark cluster中使用它。

    如果我手动启动集群,将jar复制到主机,并使用这样的命令运行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$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)
    

    我的版本中的相关设置。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控制台中。。。在“添加步骤”对话框中。。。在“参数”框旁边,写着。。。

    “它们被传递给JAR中的主函数。如果JAR没有在其清单文件中指定主类,您可以 指定另一个类名作为第一个参数 ."

    我会这样想,因为我确实在构建中指定了一个主类。sbt,我很好。。。但它失败了,没有记录任何关于失败的信息。如果我尝试将主类指定为第一个参数,它会记录我在上面发布的失败。

    我认为这可能是一个格式问题,但我无法解决它,也没有出现任何示例。我已经尝试在“添加步骤”对话框中提交以下参数。。。

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

    还有一些其他的,但都不起作用。

    版本信息。。。 电子病历4.3 Scala 2.10 sbt 0.13.9

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

    谢谢

    1 回复  |  直到 7 年前
        1
  •  2
  •   kmh    7 年前

    编辑 -让问题1-6消失,让它“工作”,但集群只是坐在那里说它正在“运行”第一步,但它从未完成。我错误地将步骤类型设置为“custom jar”,而不是“spark application”。在切换之后,我认为只有“问题1”的修复是相关的,而这可能已经修复了我的问题。我不得不退出下面问题2、3和5的修复,以使其与“spark应用程序”步骤一起工作,我想我也可以退出其余的步骤。 结束编辑

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

    问题1

    无论我作为第一个试图指向MainClass的参数传入什么。。。我也犯了同样的错误。问题出在我的身材上。sbt。我(错误地)认为root中的组织和名称足以提供包前缀。

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

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

    有趣的参考,如果你想在清单和运行中设置不同的主类。。。 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使用纱线,我将其设置为“纱线”。 这是错误的。 (将其保留为其生成的后续错误的记录)在SparkSubmit中。main(),我这样设置主URL。。。

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

    问题3

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

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

    在我的身材中。sbt。。。我在libraryDependencies中将spark core和spark sql列为“提供的”。。。我不知道为什么这不能作为EMR步骤,因为集群已经加载了火花。。。但我删除了它并将其更改为。。。

    libraryDependencies ++= Seq(
      "org.apache.spark" %% "spark-core" % "1.6.0", // % "provided",
      "org.apache.spark" %% "spark-sql" % "1.6.0", //  % "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的参考。conf迷路了。我的身材。sbt合并策略看起来像这样。。。

    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

    我想在问题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中运行时没有任何问题。我基本上通过遍历并使每个类扩展可序列化来修复它。

    结束

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