Le applicazioni Spark spesso dipendono da librerie Java o Scala di terze parti. Di seguito sono riportati gli approcci consigliati per includere queste dipendenze quando invii un job Spark a un cluster Dataproc:
Quando invii un job dalla tua macchina locale con il
gcloud dataproc jobs submitcomando, utilizza il--properties spark.jars.packages=[DEPENDENCIES]flag.
Esempio: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'
Quando invii un job direttamente sul cluster utilizza il
spark-submitcomando con il--packages=[DEPENDENCIES]parametro.
Esempio:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Evita conflitti di dipendenza
Gli approcci precedenti potrebbero non riuscire se le dipendenze dell'applicazione Spark sono in conflitto
con le dipendenze di Hadoop. Questo conflitto può verificarsi perché Hadoop inserisce le sue
dipendenze nel classpath dell'applicazione
,
quindi le sue dipendenze hanno la precedenza sulle dipendenze
dell'applicazione. Quando si verifica un conflitto, NoSuchMethodError o altri errori
possono essere generati.
Esempio:
Guava
è la libreria principale di Google per Java utilizzata da molte librerie e framework, tra cui
Hadoop. Può verificarsi un conflitto di dipendenza se un job o le relative dipendenze richiedono una
versione di Guava più recente di quella utilizzata da Hadoop.
Hadoop v3.0 ha risolto questo problema , ma le applicazioni che si basano su versioni precedenti di Hadoop richiedono la seguente soluzione alternativa in due parti per evitare possibili conflitti di dipendenza.
- Crea un singolo JAR contenente il pacchetto dell'applicazione e tutte le relative dipendenze.
- Rilocare i pacchetti di dipendenza in conflitto all'interno dell'uber JAR per evitare che i relativi nomi di percorso siano in conflitto con quelli dei pacchetti di dipendenza di Hadoop. Anziché modificare il codice, utilizza un plug-in (vedi di seguito) per eseguire automaticamente questa rilocazione (nota anche come "ombreggiatura") nell'ambito del processo di creazione dei pacchetti.
Crea un uber JAR ombreggiato con Maven
Maven è uno strumento di gestione dei pacchetti per la creazione di applicazioni Java. Il plug-in Maven scala può essere utilizzato per creare applicazioni scritte in Scala, il linguaggio utilizzato dalle applicazioni Spark. Il plug-in Maven shade può essere utilizzato per creare un JAR ombreggiato.
Di seguito è riportato un file di configurazione pom.xml di esempio che ombreggia la libreria Guava, che si trova nel pacchetto com.google.common. Questa configurazione
indica a Maven di rinominare il pacchetto com.google.common in
repackaged.com.google.common e di aggiornare tutti i riferimenti alle
classi del pacchetto originale.
<?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>
Per eseguire la build:
mvn package
Note su pom.xml:
- ManifestResourceTransformer
elabora gli attributi nel file manifest dell'uber JAR (
MANIFEST.MF). Il manifest può anche specificare il punto di ingresso per l'applicazione. - L'ambito di Spark è
`provided`
provided, poiché Spark è installato su Dataproc. - Specifica la versione di Spark installata sul cluster Dataproc (vedi Elenco delle versioni di Dataproc). Se l'applicazione richiede una versione di Spark diversa da quella installata sul cluster Dataproc, puoi scrivere un' azione di inizializzazione o creare un'immagine personalizzata che installa la versione di Spark utilizzata dall'applicazione.
- La voce
<filters>esclude i file di firma dalle directoryMETA-INFdelle dipendenze. Senza questa voce, può verificarsi un'eccezione di runtimejava.lang.SecurityException: Invalid signature file digest for Manifest main attributesperché i file di firma non sono validi nel contesto dell'uber JAR. - Potresti dover ombreggiare più librerie. Per farlo, includi più percorsi.
L'esempio successivo ombreggia le librerie Guava e Protobuf.
<relocation> <pattern>com</pattern> <shadedPattern>repackaged.com</shadedPattern> <includes> <include>com.google.protobuf.**</include> <include>com.google.common.**</include> </includes> </relocation>
Crea un uber JAR ombreggiato con SBT
SBT
è uno strumento per la creazione di applicazioni Scala. Per creare un JAR ombreggiato con SBT,
aggiungi il sbt-assembly
plug-in alla definizione della build, prima creando un file denominato assembly.sbt
nella directory project/:
├── src/
└── build.sbt
└── project/
└── assembly.sbt
... quindi aggiungendo la seguente riga in assembly.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")Di seguito è riportato un file di configurazione build.sbt di esempio che ombreggia la libreria Guava, che si trova nel com.google.common package:
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 )
Per eseguire la build:
sbt assembly
Note su build.sbt:
- La regola di ombreggiatura nell'esempio precedente potrebbe non risolvere tutti i conflitti di dipendenza
perché SBT utilizza strategie di risoluzione dei conflitti rigorose. Pertanto,
potrebbe essere necessario fornire regole più granulari che uniscano esplicitamente tipi specifici
di file in conflitto utilizzando le strategie
MergeStrategy.first,last,concat,filterDistinctLines,rename, odiscard. Per maggiori dettagli, consulta la strategia di unionesbt-assembly. - Potresti dover ombreggiare più librerie. Per farlo, includi più percorsi.
L'esempio successivo ombreggia le librerie Guava e Protobuf.
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 )
Invia l'uber JAR a Dataproc
Dopo aver creato un uber JAR ombreggiato contenente le applicazioni Spark e le relative dipendenze, puoi inviare un job a Dataproc.
Passaggi successivi
- Consulta spark-translate, un'applicazione Spark di esempio che contiene file di configurazione sia per Maven che per SBT.
- Consulta la guida rapida Scrittura ed esecuzione di job Spark Scala su Dataproc. per scoprire come scrivere ed eseguire job Spark Scala su un cluster Dataproc.