Las aplicaciones de Spark a menudo dependen de bibliotecas de terceros de Java o Scala. Estos son los enfoques recomendados para incluir estas dependencias cuando envías un trabajo de Spark a un clúster de Managed Service para Apache Spark:
Cuando envíes un trabajo desde tu máquina local con el
gcloud dataproc jobs submitcomando, usa la marca--properties spark.jars.packages=[DEPENDENCIES].
Ejemplo: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'
Cuando envíes un trabajo directamente en tu clúster usa el
spark-submitcomando con el--packages=[DEPENDENCIES]parámetro.
Ejemplo:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Evita conflictos de dependencias
Los enfoques anteriores pueden fallar si las dependencias de la aplicación de Spark entran en conflicto con las dependencias de Hadoop. Este conflicto puede surgir porque Hadoop inyecta sus
dependencias en la
ruta de clase de la aplicación,
por lo que esas dependencias tienen prioridad sobre las
de la aplicación. Cuando ocurre un conflicto, se pueden generar errores como NoSuchMethodError.
Ejemplo:
Guava
es la biblioteca principal de Google para Java que usan muchas bibliotecas y frameworks, incluido
Hadoop. Puede ocurrir un conflicto de dependencias si un trabajo o sus dependencias requieren una versión de Guava más nueva que la que usa Hadoop.
Hadoop v3.0 resolvió este problema , pero las aplicaciones que se basan en versiones anteriores de Hadoop requieren la siguiente solución de dos partes para evitar posibles conflictos de dependencias.
- Crea un solo JAR que contenga el paquete de la aplicación y todas sus dependencias
- Reubicar los paquetes de dependencia en conflicto dentro del JAR uber para evitar que sus nombres de ruta de acceso entren en conflicto con los de los paquetes de dependencia de Hadoop. En lugar de modificar tu código, usa un complemento (se detalla a continuación) para realizar de forma automática esta reubicación (también conocida como “shading”) como parte del proceso de empaquetado
Crea un JAR uber con shading mediante Maven
Maven es una herramienta de administración de paquetes para crear aplicaciones de Java. El complemento Maven scala se puede usar para crear aplicaciones escritas en Scala, el lenguaje que usan las aplicaciones de Spark. El complemento de Maven shade se puede usar para crear un JAR con shading.
El siguiente es un ejemplo de archivo de configuración pom.xml que aplica shading en la biblioteca de Guava, que se encuentra en el paquete com.google.common: Esta configuración le indica a Maven que cambie el nombre del paquete com.google.common a repackaged.com.google.common y que actualice todas las referencias a las clases del paquete original.
<?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>
Usa el siguiente comando para ejecutar la compilación:
mvn package
Notas sobre pom.xml:
- ManifestResourceTransformer
procesa los atributos en el archivo de manifiesto del JAR uber (
MANIFEST.MF). El manifiesto también puede especificar el punto de entrada para tu aplicación. - El permiso de Spark
es
provided, ya que Spark está instalado en Managed Service para Apache Spark. - Especifica la versión de Spark que está instalada en tu clúster de Managed Service para Apache Spark (consulta la lista de versiones de Managed Service para Apache Spark). Si tu aplicación requiere una versión de Spark diferente de la versión instalada en tu clúster de Managed Service para Apache Spark, puedes escribir una acción de inicialización o construir una imagen personalizada que instale la versión de Spark que usa tu aplicación.
- La entrada
<filters>excluye los archivos de firma de los directoriosMETA-INFde tus dependencias. Sin esta entrada, puede ocurrir una excepción de tiempo de ejecuciónjava.lang.SecurityException: Invalid signature file digest for Manifest main attributesporque los archivos de firma no son válidos en el contexto de tu JAR uber. - Es posible que debas aplicar shading en varias bibliotecas. Para ello, debes incluir múltiples rutas de acceso.
En el siguiente ejemplo, se aplica shading en las bibliotecas de Guava y Protobuf.
<relocation> <pattern>com</pattern> <shadedPattern>repackaged.com</shadedPattern> <includes> <include>com.google.protobuf.**</include> <include>com.google.common.**</include> </includes> </relocation>
Crea un JAR uber con shading mediante SBT
SBT
es una herramienta para compilar aplicaciones de Scala. Para crear un JAR con shading con SBT,
agrega el sbt-assembly
complemento a tu definición de compilación antes de crear un archivo llamado assembly.sbt
en el directorio project/:
├── src/
└── build.sbt
└── project/
└── assembly.sbt
... luego, agrega la siguiente línea en assembly.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")El siguiente es un ejemplo de archivo de configuración build.sbt que aplica shading en la biblioteca de Guava, que se encuentra en el 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 )
Usa el siguiente comando para ejecutar la compilación:
sbt assembly
Notas sobre build.sbt:
- Es posible que la regla de shading del ejemplo anterior no resuelva todos los conflictos de dependencias porque SBT usa estrategias de resolución de conflictos estrictas. Por lo tanto, es posible que debas proporcionar reglas más detalladas que combinen de manera explícita tipos específicos de archivos en conflicto mediante
MergeStrategy.first,last,concat,filterDistinctLines,rename, odiscardestrategias. Consulta la estrategia de combinación desbt-assembly's para obtener más detalles. - Es posible que debas aplicar shading en varias bibliotecas. Para ello, debes incluir múltiples rutas de acceso.
En el siguiente ejemplo, se aplica shading en las bibliotecas de Guava y 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 )
Envía el JAR uber a Managed Service para Apache Spark
Después de crear un JAR uber con shading que contenga tus aplicaciones de Spark y sus dependencias, puedes enviar un trabajo a Managed Service para Apache Spark.
¿Qué sigue?
- Consulta spark-translate, una aplicación de ejemplo de Spark que contiene archivos de configuración para Maven y SBT.
- Escribe y ejecuta trabajos de Spark Scala en la guía de inicio rápido de Managed Service para Apache Spark. para aprender a escribir y ejecutar trabajos de Spark Scala en un clúster de Managed Service para Apache Spark.