Spark-Anwendungen sind häufig auf Java- oder Scala-Bibliotheken von Drittanbietern angewiesen. In diesem Artikel finden Sie empfohlene Vorgehensweisen dazu, wie Sie diese Abhängigkeiten einbeziehn, wenn Sie Spark-Jobs an Dataproc-Cluster senden:
Wenn Sie einen Auftrag von Ihrem lokalen Rechner mit dem
gcloud dataproc jobs submit
-Befehl senden, verwenden Sie das Flag--properties spark.jars.packages=[DEPENDENCIES]
.
Beispiel:gcloud dataproc jobs submit spark \ --cluster=my-cluster \ --region=region \ --properties=spark.jars.packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Wenn Sie einen Job direkt im Cluster senden, verwenden Sie den Befehl
spark-submit
mit dem Parameter--packages=[DEPENDENCIES]
.
Beispiel:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Abhängigkeitskonflikte vermeiden
Die obigen Vorgehensweisen können fehlschlagen, wenn Abhängigkeiten von Spark-Anwendungen mit Abhängigkeiten von Hadoop in Konflikt stehen. Ein solcher Konflikt kann entstehen, da Hadoop seine Abhängigkeiten in den Klassenpfad der Anwendung so einfügt, dass sie gegenüber den Abhängigkeiten der Anwendung Vorrang haben. Wenn ein Konflikt auftritt, können Fehler wie NoSuchMethodError
ausgegeben werden.
Beispiel:
Guava ist die zentrale Google-Mediathek für Java, die von vielen Mediatheken und Frameworks, einschließlich Hadoop, verwendet wird. Ein Abhängigkeitskonflikt kann auftreten, wenn für einen Job oder dessen Abhängigkeiten eine Version von Guava erforderlich ist, die neuer als die von Hadoop verwendete ist.
Mit Hadoop Version 3.0 wurde dieses Problem behoben. Bei Anwendungen, die auf früheren Hadoop-Versionen basieren, ist allerdings der folgende zweiteilige Workaround erforderlich, um mögliche Abhängigkeitskonflikte zu vermeiden.
- Erstellen Sie eine einzelne JAR-Datei, die das Paket der Anwendung und alle zugehörigen Abhängigkeiten enthält.
- Verschieben Sie die in Konflikt stehenden Abhängigkeitspakete innerhalb der Uber-JAR-Datei. So wird verhindert, dass ihre Pfadnamen mit den Pfadnamen der Hadoop-Abhängigkeitspakete in Konflikt geraten. Statt hierfür Ihren Code zu ändern, verwenden Sie ein Plug-in (siehe unten), um diese Verschiebung (auch als "Shading" bezeichnet) automatisch im Rahmen der Paketerstellung durchzuführen.
Shading-Uber-JAR-Datei mit Maven erstellen
Maven ist ein Paketverwaltungstool zum Erstellen von Java-Anwendungen. Mit dem Maven-Scala-Plug-in können Anwendungen erstellt werden, die in Scala geschrieben sind, der von Spark-Anwendungen verwendeten Sprache. Das Maven-Shade-Plug-in kann dazu verwendet werden, eine Shading-JAR-Datei zu erstellen.
Das folgende Beispiel zeigt eine pom.xml
-Konfigurationsdatei, die die Guava-Mediathek, die sich im Paket com.google.common
befindet, schattiert. Diese Konfiguration weist Maven an, das Paket com.google.common
in repackaged.com.google.common
umzubenennen und alle Verweise auf die Klassen aus dem ursprünglichen Paket zu aktualisieren.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <groupId><!-- YOUR_GROUP_ID --></groupId> <artifactId><!-- YOUR_ARTIFACT_ID --></artifactId> <version><!-- YOUR_PACKAGE_VERSION --></version> <dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version><!-- YOUR_SPARK_VERSION --></version> <scope>provided</scope> </dependency> <!-- YOUR_DEPENDENCIES --> </dependencies> <build> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion><!-- YOUR_SCALA_VERSION --></scalaVersion> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass><!-- YOUR_APPLICATION_MAIN_CLASS --></mainClass> </transformer> <!-- This is needed if you have dependencies that use Service Loader. Most Google Cloud client libraries do. --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> </transformers> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/maven/**</exclude> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <relocations> <relocation> <pattern>com</pattern> <shadedPattern>repackaged.com.google.common</shadedPattern> <includes> <include>com.google.common.**</include> </includes> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
So führen Sie den Build aus:
mvn package
Hinweise zu pom.xml
:
- ManifestResourceTransformer verarbeitet Attribute in der Manifestdatei der Uber-JAR-Datei (
MANIFEST.MF
). Das Manifest kann auch den Einstiegspunkt für Ihre Anwendung angeben. - Der Bereich von Spark ist
provided
, da Spark in Dataproc installiert ist. - Geben Sie die Version von Spark an, die in Ihrem Cloud Dataproc-Cluster installiert ist. Informationen hierzu finden Sie in der Liste der Cloud Dataproc-Versionen. Wenn die in Ihrem Dataproc-Cluster installierte Spark-Version für Ihre Anwendung nicht geeignet ist, schreiben Sie entweder eine Initialisierungsaktion oder erstellen Sie ein benutzerdefiniertes Image, mit dem die von der Anwendung verwendete Spark-Version installiert wird.
- Der Eintrag
<filters>
schließt Signaturdateien aus denMETA-INF
-Verzeichnissen Ihrer Abhängigkeiten aus. Ohne diesen Eintrag kann einejava.lang.SecurityException: Invalid signature file digest for Manifest main attributes
-Laufzeitausnahme auftreten, da die Signaturdateien im Kontext Ihrer Uber-JAR-Datei ungültig sind. - Es kann sein, dass mehrere Bibliotheken umbenannt werden müssen. Fügen Sie dazu mehrere Pfade ein.
Im nächsten Beispiel werden die Bibliotheken Guava und Protobuf umbenannt.
<relocation> <pattern>com</pattern> <shadedPattern>repackaged.com</shadedPattern> <includes> <include>com.google.protobuf.**</include> <include>com.google.common.**</include> </includes> </relocation>
Shading-Uber-JAR-Datei mit SBT erstellen
SBT ist ein Tool zum Erstellen von Scala-Anwendungen. Wenn Sie eine Shading-JAR-Datei mit SBT erstellen möchten, fügen Sie der Build-Definition das Plug-in sbt-assembly
hinzu. Erstellen Sie dazu zuerst eine Datei mit dem Namen assembly.sbt
im Verzeichnis project/
:
├── src/ └── build.sbt └── project/ └── assembly.sbt
... und fügen Sie dann die folgende Zeile in assembly.sbt
hinzu:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
Das folgende Beispiel zeigt die Konfigurationsdatei build.sbt
, mit der die Guava-Mediathek, die sich im com.google.common package
befindet, umbenannt wird (Shading).
lazy val commonSettings = Seq( organization := "YOUR_GROUP_ID", name := "YOUR_ARTIFACT_ID", version := "YOUR_PACKAGE_VERSION", scalaVersion := "YOUR_SCALA_VERSION", ) lazy val shaded = (project in file(".")) .settings(commonSettings) mainClass in (Compile, packageBin) := Some("YOUR_APPLICATION_MAIN_CLASS") libraryDependencies ++= Seq( "org.apache.spark" % "spark-sql_2.11" % "YOUR_SPARK_VERSION" % "provided", // YOUR_DEPENDENCIES ) assemblyShadeRules in assembly := Seq( ShadeRule.rename("com.google.common.**" -> "repackaged.com.google.common.@1").inAll )
So führen Sie den Build aus:
sbt assembly
Hinweise zu build.sbt
:
- Möglicherweise werden mit der Shading-Regel oben nicht alle Abhängigkeitskonflikte gelöst, da SBT ganz bestimmte Strategien zur Konfliktlösung verwendet. Daher kann es sein, dass Sie detailliertere Regeln angeben müssen, durch die bestimmte Arten von in Konflikt stehenden Dateien ausdrücklich zusammengeführt werden. Verwenden Sie hierbei die Strategie
MergeStrategy.first
,last
,concat
,filterDistinctLines
,rename
oderdiscard
. Weitere Informationen finden Sie in der Zusammenführungsstrategie vonsbt-assembly
. - Es kann sein, dass mehrere Bibliotheken umbenannt werden müssen. Fügen Sie dazu mehrere Pfade ein.
Im nächsten Beispiel werden die Bibliotheken Guava und Protobuf umbenannt.
assemblyShadeRules in assembly := Seq( ShadeRule.rename("com.google.common.**" -> "repackaged.com.google.common.@1").inAll, ShadeRule.rename("com.google.protobuf.**" -> "repackaged.com.google.protobuf.@1").inAll )
Uber-JAR-Datei an Dataproc senden
Nachdem Sie eine Shading-Uber-JAR-Datei erstellt haben, die Ihre Spark-Anwendungen und deren Abhängigkeiten enthält, können Sie einen Job an Dataproc senden.
Nächste Schritte
- Spark-Beispielanwendung spark-translate ansehen, die Konfigurationsdateien für Maven und SBT enthält.
- Spark-Scala-Jobs in Dataproc schreiben und ausführen – Kurzanleitung dazu, wie Spark-Scala-Jobs in einem Dataproc-Cluster geschrieben und ausgeführt werden