As aplicações Spark dependem frequentemente de bibliotecas Java ou Scala de terceiros. Seguem-se abordagens recomendadas para incluir estas dependências quando envia uma tarefa do Spark para um cluster do Dataproc:
Quando enviar uma tarefa a partir da sua máquina local com o comando
gcloud dataproc jobs submit
, use a flag--properties spark.jars.packages=[DEPENDENCIES]
.
Exemplo: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 enviar um trabalho diretamente no cluster, use o comando
spark-submit
com o parâmetro--packages=[DEPENDENCIES]
.
Exemplo:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
Evite conflitos de dependências
As abordagens anteriores podem falhar se as dependências da aplicação Spark entrarem em conflito com as dependências do Hadoop. Este conflito pode surgir porque o Hadoop injeta as respetivas dependências no classpath da aplicação, pelo que as respetivas dependências têm precedência sobre as dependências da aplicação. Quando ocorre um conflito, podem ser gerados NoSuchMethodError
ou outros erros.
Exemplo:
Guava
é a biblioteca principal da Google para Java que é usada por muitas bibliotecas e frameworks, incluindo o
Hadoop. Pode ocorrer um conflito de dependência se uma tarefa ou as respetivas dependências exigirem uma versão do Guava mais recente do que a usada pelo Hadoop.
O Hadoop v3.0 resolveu este problema , mas as aplicações que dependem de versões anteriores do Hadoop requerem a seguinte solução alternativa de duas partes para evitar possíveis conflitos de dependência.
- Crie um único JAR que contenha o pacote da aplicação e todas as respetivas dependências.
- Mova os pacotes de dependências em conflito no JAR uber para impedir que os respetivos nomes de caminhos entrem em conflito com os dos pacotes de dependências do Hadoop. Em vez de modificar o código, use um plug-in (veja abaixo) para realizar automaticamente esta recolocação (também conhecida como "shading") como parte do processo de embalagem.
Crie um JAR uber sombreado com o Maven
O Maven é uma ferramenta de gestão de pacotes para criar aplicações Java. O plug-in Maven scala pode ser usado para criar aplicações escritas em Scala, o idioma usado pelas aplicações Spark. O plug-in Maven shade pode ser usado para criar um JAR sombreado.
Segue-se um exemplo de um ficheiro de configuração pom.xml
que sombreia a biblioteca Guava, que se encontra no pacote com.google.common
. Esta configuração
indica ao Maven que mude o nome do pacote com.google.common
para
repackaged.com.google.common
e que atualize 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 compilação:
mvn package
Notas sobre pom.xml
:
- O ManifestResourceTransformer processa atributos no ficheiro de manifesto do JAR uber (
MANIFEST.MF
). O manifesto também pode especificar o ponto de entrada da sua aplicação. - O âmbito do Spark é
provided
, uma vez que o Spark está instalado no Dataproc. - Especifique a versão do Spark instalada no seu cluster do Dataproc (consulte a lista de versões do Dataproc). Se a sua aplicação precisar de uma versão do Spark diferente da versão instalada no cluster do Dataproc, pode escrever uma ação de inicialização ou criar uma imagem personalizada que instale a versão do Spark usada pela sua aplicação.
- A entrada
<filters>
exclui ficheiros de assinatura dos diretórios das suas dependências.META-INF
Sem esta entrada, pode ocorrer uma exceção de tempo de execução porque os ficheiros de assinatura são inválidos no contexto do seu JAR uber.java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
- Pode ter de sombrear várias bibliotecas. Para tal, inclua vários caminhos.
O exemplo seguinte 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>
Crie um JAR com sombreado com o SBT
SBT
é uma ferramenta para criar aplicações Scala. Para criar um JAR sombreado com o SBT,
adicione o plug-in sbt-assembly
à definição de compilação. Primeiro, crie um ficheiro denominado assembly.sbt
no diretório project/
:
├── src/ └── build.sbt └── project/ └── assembly.sbt
... then by adding the following line in assembly.sbt
:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
Segue-se um ficheiro de configuração de build.sbt
de exemplo que sombreia a biblioteca Guava, que se encontra em 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 compilação:
sbt assembly
Notas sobre build.sbt
:
- A regra de ocultação no exemplo anterior pode não resolver todos os conflitos de dependência porque o SBT usa estratégias de resolução de conflitos rigorosas. Por conseguinte, pode ter de fornecer regras mais detalhadas que unam explicitamente tipos específicos de ficheiros em conflito através das estratégias
MergeStrategy.first
,last
,concat
,filterDistinctLines
,rename
oudiscard
. Consulte a estratégia de união dasbt-assembly
para ver mais detalhes. - Pode ter de sombrear várias bibliotecas. Para tal, inclua vários caminhos.
O exemplo seguinte 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 )
Envie o JAR completo para o Dataproc
Depois de criar um JAR uber sombreado que contenha as suas aplicações Spark e respetivas dependências, tem tudo pronto para enviar uma tarefa para o Dataproc.
O que se segue?
- Consulte spark-translate, uma aplicação Spark de exemplo que contém ficheiros de configuração para o Maven e o SBT.
- Escreva e execute tarefas do Spark Scala no Dataproc. Início rápido para saber como escrever e executar tarefas do Spark Scala num cluster do Dataproc.