Os aplicativos Spark geralmente dependem de bibliotecas Java ou Scala de terceiros. Veja a seguir as abordagens recomendadas para incluir essas dependências quando você envia um job do Spark para um cluster do Dataproc:
Ao enviar um job da máquina local com o comando
gcloud dataproc jobs submit
use a sinalização--properties spark.jars.packages=[DEPENDENCIES]
.
Examplo: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'
Ao enviar um job diretamente no cluster, use o comando
spark-submit
com o parâmetro--packages=[DEPENDENCIES]
.
Examplo:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Como evitar conflitos de dependência
As abordagens acima podem falhar se as dependências do aplicativo Spark entrarem em conflito com as dependências do Hadoop. Esse conflito pode surgir porque o Hadoop injeta suas dependências no classpath do aplicativo de forma que as dependências dele têm precedência sobre as dependências do aplicativo. Quando ocorre um conflito, NoSuchMethodError
ou outros erros podem ser gerados.
Exemplo:
Guava
é a biblioteca principal do Google para Java usada por muitas bibliotecas e estruturas, incluindo
Hadoop. Um conflito de dependência pode ocorrer se um job ou as dependências desse job exigirem uma versão do Guava que seja mais recente do que a utilizada pelo Hadoop.
O Hadoop v3.0 resolveu esse problema, mas os aplicativos que dependem de versões anteriores do Hadoop exigem a seguinte solução alternativa com duas partes para evitar possíveis conflitos de dependência.
- Crie um único JAR que contenha o pacote do aplicativo e todas as suas dependências.
- Realoque os pacotes de dependência conflitantes dentro do JAR uber para impedir que seus nomes de caminho conflitem com os dos pacotes de dependência do Hadoop. Em vez de modificar seu código, use um plug-in (veja abaixo) para executar automaticamente essa realocação (também conhecida como "sombreamento") como parte do processo de empacotamento.
Como criar um JAR uber sombreado com o Maven
O Maven é uma ferramenta de gerenciamento de pacotes para a criação de aplicativos Java. O plug-in Maven scala pode ser usado para criar aplicativos escritos em Scala, a linguagem usada pelos aplicativos Spark. O plug-in de sombreamento do Maven pode ser usado para criar um JAR sombreado.
Este é um exemplo de arquivo de configuração pom.xml
que sombreia a biblioteca Guava, localizada no pacote com.google.common
. Essa configuração instrui o Maven a renomear o pacote com.google.common
como repackaged.com.google.common
e a atualizar todas as referências às classes do pacote 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>
Para executar a versão:
mvn package
Observações sobre pom.xml
:
- ManifestResourceTransformer
processa atributos no arquivo de manifesto do JAR uber (
MANIFEST.MF
). O manifesto também pode especificar o ponto de entrada para seu aplicativo. - O escopo do Spark
é
provided
, já que o Spark está instalado no Dataproc. - Especifique a versão do Spark instalada no cluster do Dataproc (consulte a Lista de versões do Dataproc). Se o aplicativo exigir uma versão do Spark diferente da versão instalado no cluster do Dataproc, é possível gravar ação de inicialização ou construa uma imagem personalizada que instala a versão do Spark usada pelo seu aplicativo.
- A entrada
<filters>
exclui arquivos de assinatura dos diretóriosMETA-INF
das dependências. Sem essa entrada, uma exceção de tempo de execuçãojava.lang.SecurityException: Invalid signature file digest for Manifest main attributes
pode ocorrer pelo fato dos arquivos de assinatura serem inválidos no contexto do JAR uber. - Pode ser necessário sombrear várias bibliotecas. Para fazer isso, inclua vários caminhos.
O exemplo a seguir sombreia as bibliotecas Guava e Protobuf.
<relocation> <pattern>com</pattern> <shadedPattern>repackaged.com</shadedPattern> <includes> <include>com.google.protobuf.**</include> <include>com.google.common.**</include> </includes> </relocation>
Como criar um JAR uber sombreado com o SBT
SBT
é uma ferramenta para criar aplicativos Scala. Para criar um JAR sombreado com o SBT,
adicione o plug-in sbt-assembly
à sua definição de build, criando primeiro um arquivo chamado assembly.sbt
no diretório project/
:
├── src/ └── build.sbt └── project/ └── assembly.sbt
... adicionando a seguinte linha em assembly.sbt
:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
Este é um exemplo de arquivo de configuração build.sbt
que sombreia a biblioteca Guava, localizada no 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 )
Para executar a versão:
sbt assembly
Observações sobre build.sbt
:
- A regra de sombreamento no exemplo acima pode não resolver todos os conflitos de dependência, uma vez que o SBT usa estratégias restritas de resolução de conflitos. Portanto, talvez seja necessário fornecer regras mais granulares que mesclem explicitamente tipos específicos de arquivos conflitantes usando estratégias
MergeStrategy.first
,last
,concat
,filterDistinctLines
,rename
oudiscard
. Consulte a estratégia de mesclagem desbt-assembly
para mais detalhes. - Pode ser necessário sombrear várias bibliotecas. Para fazer isso, inclua vários caminhos.
O exemplo a seguir sombreia as bibliotecas 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 )
Como enviar o JAR uber para o Dataproc
Depois de criar um JAR uber sombreado que contenha os aplicativos do Spark e as respectivas dependências, estará tudo pronto para enviar um job ao Dataproc.
A seguir
- Consulte spark-translate, uma amostra do aplicativo Spark que contém arquivos de configuração para o Maven e para o SBT.
- Grave e execute jobs do Spark Scala no Dataproc. Guia de início rápido para aprender a escrever e executar jobs do Spark Scala em um cluster do Dataproc.