通过 Dataproc 的 Spark 作业的 Hadoop 安全性 GroupMappingServiceProvider 异常 API

Hadoop security GroupMappingServiceProvider exception for Spark job via Dataproc API

我正在尝试 运行 在 google dataproc 集群上执行 Spark 作业,但出现以下错误:

Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: class org.apache.hadoop.security.JniBasedUnixGroupsMapping not org.apache.hadoop.security.GroupMappingServiceProvider
    at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2330)
    at org.apache.hadoop.security.Groups.<init>(Groups.java:108)
    at org.apache.hadoop.security.Groups.<init>(Groups.java:102)
    at org.apache.hadoop.security.Groups.getUserToGroupsMappingService(Groups.java:450)
    at org.apache.hadoop.security.UserGroupInformation.initialize(UserGroupInformation.java:310)
    at org.apache.hadoop.security.UserGroupInformation.ensureInitialized(UserGroupInformation.java:277)
    at org.apache.hadoop.security.UserGroupInformation.loginUserFromSubject(UserGroupInformation.java:833)
    at org.apache.hadoop.security.UserGroupInformation.getLoginUser(UserGroupInformation.java:803)
    at org.apache.hadoop.security.UserGroupInformation.getCurrentUser(UserGroupInformation.java:676)
    at org.apache.spark.util.Utils$$anonfun$getCurrentUserName.apply(Utils.scala:2430)
    at org.apache.spark.util.Utils$$anonfun$getCurrentUserName.apply(Utils.scala:2430)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.util.Utils$.getCurrentUserName(Utils.scala:2430)
    at org.apache.spark.SparkContext.<init>(SparkContext.scala:295)
    at org.apache.spark.api.java.JavaSparkContext.<init>(JavaSparkContext.scala:58)
    at com.my.package.spark.SparkModule.provideJavaSparkContext(SparkModule.java:59)
    at com.my.package.spark.SparkModule$$ModuleAdapter$ProvideJavaSparkContextProvidesAdapter.get(SparkModule$$ModuleAdapter.java:140)
    at com.my.package.spark.SparkModule$$ModuleAdapter$ProvideJavaSparkContextProvidesAdapter.get(SparkModule$$ModuleAdapter.java:101)
    at dagger.internal.Linker$SingletonBinding.get(Linker.java:364)
    at spark.Main$$InjectAdapter.get(Main$$InjectAdapter.java:65)
    at spark.Main$$InjectAdapter.get(Main$$InjectAdapter.java:23)
    at dagger.ObjectGraph$DaggerObjectGraph.get(ObjectGraph.java:272)
    at spark.Main.main(Main.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:755)
    at org.apache.spark.deploy.SparkSubmit$.doRunMain(SparkSubmit.scala:180)
    at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)
    at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:119)
    at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
Caused by: java.lang.RuntimeException: class org.apache.hadoop.security.JniBasedUnixGroupsMapping not org.apache.hadoop.security.GroupMappingServiceProvider
    at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2324)
    ... 31 more

Dataproc 版本:1.1.51 和 1.2.15

工作配置:

地区:全球
集群 my-cluster
工作类型:Spark
Jar 文件:gs://bucket/jars/spark-job.jar
主要 class 或 jar:spark.Main
参数:
属性:
spark.driver.extraClassPath: /path/to/google-api-client-1.20.0.jar
spark.driver.userClassPathFirst: 真

我没问题运行在命令行上这样设置:

spark-submit --conf "spark.driver.extraClassPath=/path/to/google-api-client-1.20.0.jar" --conf "spark.driver.userClassPathFirst=true" --class spark.Main /path/to/spark-job.jar

但是 UI/API 不允许您同时传递 class 名称和 jar,所以它看起来像这样:

spark-submit --conf spark.driver.extraClassPath=/path/to/google-api-client-1.20.0.jar --conf spark.driver.userClassPathFirst=true --class spark.Main --jars /tmp/1f4d5289-37af-4311-9ccc-5eee34acaf62/spark-job.jar /usr/lib/hadoop/hadoop-common.jar

我不知道是不是提供 extraClassPath 有问题,还是 spark-job.jar 和 hadoop-common.jar 有某种冲突。

认为 这是由 userClassPathFirst 和 /usr/lib/hadoop/hadoop-common.jar 的组合引起的,jar 是 Dataproc 指定用于 spark-submit 的 jar。在某些情况下,将使用来自用户 class 加载器的 GroupMappingServiceProvider 实例,而在其他情况下,将使用来自系统 class 加载器的实例。由于从一个 class 加载程序加载的 class 不等于从另一个 class 加载程序加载的相同 class,您最终会遇到此异常。

而不是 userClassPathFirst,使用像 maven shade 这样的东西来重新定位冲突的 classes 是否有意义?

如果您不想关闭 spark.driver.userClassPathFirst=true,您可以检查 "org.apache.spark" %% "spark-core" % SPARK_VERSION 依赖项是否存在以及范围是否已正确定义。 当 spark-core jar 在类路径中时,不会抛出异常。

对于遇到此问题的任何人(即使您没有使用 Spark

此处引发此异常Configuration.java#L2585

Configuration.java

异常的含义

Hadoop 告诉你,第二个 class 是 而不是 另一个 class 的 subclasssubinterface

这就是 Class.isAssignableFrom(OtherClass) 在上图中 2584 行所做的事情

The java.lang.Class.isAssignableFrom() determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter.

但我们知道

  • org.apache.hadoop.security.GroupMappingServiceProvider

超class

  • org.apache.hadoop.security.JniBasedUnixGroupsMapping

那怎么会这样呢?

为什么会这样

发生这种情况的原因有多种

  1. 您的 classpath
  2. 上有两个版本的 hadoop-client
  3. 您已将 Hadoop 与一个库捆绑在一起,但它的版本或发行版与另一个捆绑的 Hadoop 库不同。
  4. 您的 class 路径指向多个 Hadoop 安装或 Hadoop 客户端,而它们应该只有一个 hadoop-client jar
  5. 您使用了错误的 Hadoop 包 - 例如,您的目标是 Cloudera 环境并且您使用的是开源版本的 Hadoop 库

解决方案

  • 使所有 Hadoop 库版本符合同一版本
  • 创建一个元包,并在所有插件、客户端、包中仅依赖于这个元包 jar。
  • 切换到使用客户端库的正确发行版本 - 示例:
  • 通过 hadoop-client-apihadoop-client-runtime
  • 使用阴影版本的 Hadoop 客户端库