AWS EMR运行sbt打包的Scala Spark作业报upickle类NoClassDefFoundError错误
问题原因
你遇到的java.lang.NoClassDefFoundError错误是因为sbt package命令默认仅打包项目本身的代码,不会将upickle、ujson这类第三方非集群内置依赖打入最终jar包,AWS EMR集群运行时无法加载对应类导致报错。
修复步骤
1. 引入sbt-assembly插件
在项目的project/plugins.sbt文件中添加如下配置,用于打包包含所有依赖的fat jar(uber jar):
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0")
可根据你使用的sbt版本调整插件版本适配。
2. 修改build.sbt配置
将Spark、Hadoop、AWS SDK等EMR集群已经内置的依赖标记为Provided,避免重复打包引发版本冲突,第三方依赖保留原有配置即可,修改后示例如下:
scalaVersion := "2.12.14" libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.11.792" % Provided libraryDependencies += "com.amazonaws" % "aws-java-sdk-core" % "1.11.792" % Provided libraryDependencies += "org.apache.hadoop" % "hadoop-aws" % "3.3.0" % Provided libraryDependencies += "org.apache.hadoop" % "hadoop-common" % "3.3.0" % Provided libraryDependencies += "org.apache.hadoop" % "hadoop-client" % "3.3.0" % Provided libraryDependencies += "org.apache.spark" %% "spark-core" % "3.0.1" % Provided libraryDependencies += "org.apache.spark" %% "spark-sql" % "3.0.1" % Provided // 第三方非内置依赖无需标记Provided,会被打入fat jar libraryDependencies += "com.lihaoyi" %% "upickle" % "1.4.1" libraryDependencies += "com.lihaoyi" %% "ujson" % "1.4.1" // 可选配置:解决打包时的文件冲突问题 assemblyMergeStrategy in assembly := { case PathList("META-INF", _*) => MergeStrategy.discard case other => MergeStrategy.first }
3. 重新打包提交
使用sbt assembly命令替代原有的sbt package命令打包,生成的jar包默认存放在target/scala-2.12/目录下,将该jar包上传到EMR集群提交运行即可。
可选替代方案
如果不想打fat jar,且集群可以访问公网Maven仓库,也可以在spark-submit时通过--packages参数动态拉取依赖:
spark-submit \ --class 你的主类全路径 \ --master yarn \ --deploy-mode cluster \ --packages com.lihaoyi:upickle_2.12:1.4.1,com.lihaoyi:ujson_2.12:1.4.1 \ 你的jar包路径
版本校验补充
EMR 6.2.0内置的Spark版本为3.0.1、Scala版本为2.12.x,和你本地使用的版本完全匹配,不存在版本兼容问题。
内容的提问来源于stack exchange,提问作者matmiz
相关产品推荐
相关产品推荐

