Adicionar jars a uma tarefa do Spark - spark-submit

158

É verdade ... já foi discutido bastante.

No entanto, há muita ambiguidade e algumas das respostas fornecidas ... incluindo a duplicação de referências de jar nas opções ou na configuração de jars / executor / driver.

Os detalhes ambíguos e / ou omitidos

Após a ambiguidade, detalhes obscuros e / ou omitidos devem ser esclarecidos para cada opção:

  • Como o ClassPath é afetado
    • Motorista
    • Executor (para tarefas em execução)
    • Ambos
    • de modo nenhum
  • Caractere de separação: vírgula, dois pontos, ponto e vírgula
  • Se os arquivos fornecidos forem distribuídos automaticamente
    • para as tarefas (para cada executor)
    • para o driver remoto (se executado no modo de cluster)
  • tipo de URI aceito: arquivo local, hdfs, http, etc
  • Se copiado para um local comum, onde está esse local (hdfs, local?)

As opções às quais isso afeta:

  1. --jars
  2. SparkContext.addJar(...) método
  3. SparkContext.addFile(...) método
  4. --conf spark.driver.extraClassPath=... ou --driver-class-path ...
  5. --conf spark.driver.extraLibraryPath=...ou --driver-library-path ...
  6. --conf spark.executor.extraClassPath=...
  7. --conf spark.executor.extraLibraryPath=...
  8. para não esquecer, o último parâmetro do envio de spark também é um arquivo .jar.

Estou ciente de onde posso encontrar a documentação principal do spark e, especificamente, sobre como enviar , as opções disponíveis e também o JavaDoc . No entanto, isso deixou para mim ainda alguns buracos, embora respondesse parcialmente também.

Espero que não seja tão complexo e que alguém possa me dar uma resposta clara e concisa.

Se eu fosse adivinhar pela documentação, parece que --jars, e os métodos SparkContext addJare addFilesão os que distribuirão os arquivos automaticamente, enquanto as outras opções apenas modificam o ClassPath.

Seria seguro supor que, por simplicidade, eu posso adicionar arquivos jar de aplicativos adicionais usando as 3 opções principais ao mesmo tempo:

spark-submit --jar additional1.jar,additional2.jar \
  --driver-library-path additional1.jar:additional2.jar \
  --conf spark.executor.extraLibraryPath=additional1.jar:additional2.jar \
  --class MyClass main-application.jar

Encontrei um bom artigo sobre uma resposta a outra postagem . No entanto, nada de novo aprendeu. O pôster faz uma boa observação sobre a diferença entre o driver local (cliente-fio) e o driver remoto (cluster de fios). Definitivamente importante ter em mente.

YoYo
fonte
1
Em qual gerenciador de cluster você está executando? Autônomo / FIO / Mesos?
Yuval Itzchakov
Qualquer. Pretendo isso como um esclarecimento para a documentação original. Estou usando principalmente cluster autônomo, instância única, yarn-client, yarn-cluster. Outros podem estar usando o Mesos. Parece que você fez uma boa pesquisa original em seu blog sobre isso. Acabei fazendo o mesmo que você - usando um shader para criar um jar do Uber para simplificar meu processo de implantação.
YoYo 10/05
1
Vou postar uma resposta sobre como implantamos o Spark Standalone, que pode esclarecer algumas coisas.
Yuval Itzchakov
6
Fiz um esforço para responder a todas as suas perguntas. Espero que ajude :)
Yuval Itzchakov
@Yuval Itzchakov, assim como Yoyo mencionou, eu também uso um jar sombreado para agrupar todas as minhas dependências, por exemplo, classes de casos e outros jarros que eu possa estar usando. Estou tentando entender quando me deparo com uma situação em que preciso de vários frascos. Quero dizer, sempre posso agrupar esses vários frascos em um único frasco. Por que não consigo continuar vivendo com meus frascos sombreados agrupando todas as minhas dependências?
Sheel Pancholi

Respostas:

177

ClassPath:

O ClassPath é afetado dependendo do que você fornece. Existem algumas maneiras de definir algo no caminho de classe:

  • spark.driver.extraClassPath ou é apelido --driver-class-path para definir caminhos de classe extras no nó que está executando o driver.
  • spark.executor.extraClassPath para definir o caminho extra da classe nos nós do trabalhador.

Se você deseja que um certo JAR seja efetuado no mestre e no trabalhador, é necessário especificá-los separadamente em AMBOS os sinalizadores.

Caractere de separação:

Seguindo as mesmas regras que a JVM :

  • Linux: dois pontos :
    • por exemplo: --conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar:/opt/prog/aws-java-sdk-1.10.50.jar"
  • Windows: um ponto-e-vírgula ;
    • por exemplo: --conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar;/opt/prog/aws-java-sdk-1.10.50.jar"

Distribuição de arquivos:

Isso depende do modo em que você está executando seu trabalho:

  1. Modo cliente - O Spark aciona um servidor HTTP Netty que distribui os arquivos na inicialização de cada um dos nós do trabalhador. Você pode ver que, ao iniciar seu trabalho do Spark:

    16/05/08 17:29:12 INFO HttpFileServer: HTTP File server directory is /tmp/spark-48911afa-db63-4ffc-a298-015e8b96bc55/httpd-84ae312b-5863-4f4c-a1ea-537bfca2bc2b
    16/05/08 17:29:12 INFO HttpServer: Starting HTTP Server
    16/05/08 17:29:12 INFO Utils: Successfully started service 'HTTP file server' on port 58922.
    16/05/08 17:29:12 INFO SparkContext: Added JAR /opt/foo.jar at http://***:58922/jars/com.mycode.jar with timestamp 1462728552732
    16/05/08 17:29:12 INFO SparkContext: Added JAR /opt/aws-java-sdk-1.10.50.jar at http://***:58922/jars/aws-java-sdk-1.10.50.jar with timestamp 1462728552767
  2. Modo de cluster - No modo de cluster, o spark selecionou um nó Worker líder para executar o processo do Driver. Isso significa que o trabalho não está sendo executado diretamente no nó Mestre. Aqui, o Spark não definirá um servidor HTTP. É necessário disponibilizar manualmente o seu JARS para todos os nós do trabalhador por meio de fontes HDFS / S3 / Other disponíveis para todos os nós.

URIs aceitos para arquivos

Em "Enviando aplicativos" , a documentação do Spark explica bem os prefixos aceitos para arquivos:

Ao usar o envio por spark, o jar do aplicativo, juntamente com os jars incluídos na opção --jars, será automaticamente transferido para o cluster. O Spark usa o seguinte esquema de URL para permitir estratégias diferentes para disseminar jarros:

  • file: - Caminhos absolutos e file: / URIs são atendidos pelo servidor de arquivos HTTP do driver, e todo executor extrai o arquivo do servidor HTTP do driver.
  • hdfs :, http :, https :, ftp: - esses arquivos e JARs suspensos do URI, conforme o esperado
  • local: - espera-se que um URI que comece com local: / exista como um arquivo local em cada nó do trabalhador. Isso significa que nenhuma E / S de rede será incorrida e funciona bem para arquivos / JARs grandes enviados por cada trabalhador ou compartilhados via NFS, GlusterFS etc.

Observe que JARs e arquivos são copiados para o diretório de trabalho de cada SparkContext nos nós do executor.

Conforme observado, os JARs são copiados para o diretório de trabalho de cada nó Worker. Onde exatamente é isso? É geralmente sob /var/run/spark/work, você vai vê-los como este:

drwxr-xr-x    3 spark spark   4096 May 15 06:16 app-20160515061614-0027
drwxr-xr-x    3 spark spark   4096 May 15 07:04 app-20160515070442-0028
drwxr-xr-x    3 spark spark   4096 May 15 07:18 app-20160515071819-0029
drwxr-xr-x    3 spark spark   4096 May 15 07:38 app-20160515073852-0030
drwxr-xr-x    3 spark spark   4096 May 15 08:13 app-20160515081350-0031
drwxr-xr-x    3 spark spark   4096 May 18 17:20 app-20160518172020-0032
drwxr-xr-x    3 spark spark   4096 May 18 17:20 app-20160518172045-0033

E quando você olhar para dentro, verá todos os JARs implementados:

[*@*]$ cd /var/run/spark/work/app-20160508173423-0014/1/
[*@*]$ ll
total 89988
-rwxr-xr-x 1 spark spark   801117 May  8 17:34 awscala_2.10-0.5.5.jar
-rwxr-xr-x 1 spark spark 29558264 May  8 17:34 aws-java-sdk-1.10.50.jar
-rwxr-xr-x 1 spark spark 59466931 May  8 17:34 com.mycode.code.jar
-rwxr-xr-x 1 spark spark  2308517 May  8 17:34 guava-19.0.jar
-rw-r--r-- 1 spark spark      457 May  8 17:34 stderr
-rw-r--r-- 1 spark spark        0 May  8 17:34 stdout

Opções afetadas:

A coisa mais importante a entender é a prioridade . Se você passar qualquer propriedade via código, ela terá precedência sobre qualquer opção que você especificar via spark-submit. Isso é mencionado na documentação do Spark:

Quaisquer valores especificados como sinalizadores ou no arquivo de propriedades serão passados ​​para o aplicativo e mesclados com os especificados por meio do SparkConf. As propriedades definidas diretamente no SparkConf têm precedência mais alta , depois os sinalizadores passados ​​para envio de spark ou shell de spark e opções no arquivo spark-defaults.conf

Portanto, certifique-se de definir esses valores nos lugares adequados, para que você não se surpreenda quando um tiver prioridade sobre o outro.

Vamos analisar cada opção em questão:

  • --jarsvs SparkContext.addJar: são idênticos, apenas um é definido através do envio de spark e outro via código. Escolha o que melhor combina com você. Uma coisa importante a ser observada é que o uso de uma dessas opções não adiciona o JAR ao caminho de classe do driver / executor , você precisará adicioná-las explicitamente usando a extraClassPathconfiguração em ambas.
  • SparkContext.addJarvs SparkContext.addFile: use o primeiro quando tiver uma dependência que precise ser usada com seu código. Use o último quando quiser simplesmente passar um arquivo arbitrário para os nós do trabalhador, o que não é uma dependência em tempo de execução do seu código.
  • --conf spark.driver.extraClassPath=...ou --driver-class-path: esses são aliases, não importa qual você escolher
  • --conf spark.driver.extraLibraryPath=..., or --driver-library-path ... O mesmo que acima, aliases.
  • --conf spark.executor.extraClassPath=...: Use isso quando tiver uma dependência que não possa ser incluída em um JAR uber (por exemplo, porque há conflitos de tempo de compilação entre versões da biblioteca) e que você precisará carregar em tempo de execução.
  • --conf spark.executor.extraLibraryPath=...Isso é passado como a java.library.pathopção para a JVM. Use isso quando precisar de um caminho de biblioteca visível para a JVM.

Seria seguro supor que, por simplicidade, eu posso adicionar arquivos jar de aplicativos adicionais usando as 3 opções principais ao mesmo tempo:

Você pode assumir isso com segurança apenas no modo Cliente, não no modo Cluster. Como eu disse anteriormente. Além disso, o exemplo que você deu possui alguns argumentos redundantes. Por exemplo, passar JARs para --driver-library-pathé inútil, você precisará passá-los extraClassPathse quiser que eles estejam no seu caminho de classe. Por fim, o que você deseja fazer ao implantar JARs externos no driver e no trabalhador é:

spark-submit --jars additional1.jar,additional2.jar \
  --driver-class-path additional1.jar:additional2.jar \
  --conf spark.executor.extraClassPath=additional1.jar:additional2.jar \
  --class MyClass main-application.jar
Yuval Itzchakov
fonte
4
Resposta ótima e abrangente. Obrigado. Você também pode contar mais sobre as práticas recomendadas na implantação com JAR uber vs. dependências externas ao JAR (bibliotecas na pasta externa e listadas no MANIFEST.MFarquivo)?
precisa saber é o seguinte
2
@jsosnowski Normalmente, adoro usar frascos externos quando há conflitos que são muito complexos para resolver com o meu JAR uber. Normalmente, eu simplesmente uso SBTs assemblyMergeStrategye seleciono as classes de que preciso se houver conflitos. Eu geralmente recomendo o mesmo.
Yuval Itzchakov
9
@ yuval-itzchakov Obrigado pela ótima resposta, muito útil. Um ponto que eu gostaria de enfatizar para ajudar outras pessoas que podem ter cometido o mesmo erro que eu. O argumento --jars transporta apenas os jars para cada máquina no cluster. NÃO diz ao spark para usá-los na pesquisa de caminho da classe. O caminho --driver-class-path (ou argumentos ou parâmetros de configuração semelhantes) também é necessário. Inicialmente, pensei que eram maneiras alternativas de fazer a mesma coisa.
Tim Ryan
1
@TimRyan Definitely. Se você olhar a última parte da resposta, passo os frascos para o --jarssinalizador e o caminho da classe driver / executor.
Yuval Itzchakov
1
Eventualmente, descobri como injetar variáveis ​​de ambiente zeppelin-env.she adicioná --jars- las SPARK_SUBMIT_OPTIONS. Isso funcionou. O formato URI que eu uso é --jars=local:///mnt/dir/file.jar.
Mike
4

Outra abordagem em spark 2.1.0 é usar --conf spark.driver.userClassPathFirst=truedurante o envio do spark que altera a prioridade da carga de dependência e, portanto, o comportamento do trabalho do spark, dando prioridade aos jars que o usuário está adicionando ao caminho da classe com a --jarsopção

Stanislav
fonte
2
Você terá que ter cuidado com isso - pois é possível quebrar a faísca com isso. Essa deve ser a última opção de solução. Potencialmente, poderia interferir com a camada que faz interface com o fio também quando usado no modo cliente do fio, embora eu não tenha certeza.
YoYo
Obrigado pela atenção. Existe alguma maneira de priorizar apenas 1 jar, que definitivamente existe no servidor em uma versão mais antiga, mas você não pode substituir fisicamente e sabe que não deseja usar?
Stanislav
1
Acho que, nesse caso, você pode tentar exatamente como sugeriu. Não disse que era um não absoluto. Lembre-se também de que a opção está marcada como 'experimental' - um aviso a ser atendido! Não há uma maneira segura de priorizar uma versão de uma biblioteca em detrimento de outra. Em algumas implementações, isso é resolvido movendo uma das bibliotecas em um espaço para nome diferente, para que você possa usar as duas versões ao mesmo tempo.
YoYo 04/04
1

Outras opções configuráveis ​​do Spark relacionadas a jars e classpath, no caso de yarnmodo de implementação, são as seguintes:
Na documentação do spark,

spark.yarn.jars

Lista de bibliotecas que contêm o código Spark para distribuir nos contêineres YARN. Por padrão, o Spark no YARN usará os jars Spark instalados localmente, mas os jars Spark também podem estar em um local legível por mundo no HDFS. Isso permite que o YARN o armazene em cache nos nós, para que não precise ser distribuído sempre que um aplicativo for executado. Para apontar para jars no HDFS, por exemplo, defina esta configuração como hdfs: /// some / path. Globs são permitidos.

spark.yarn.archive

Um arquivo contendo os Spark jars necessários para distribuição no cache YARN. Se definida, essa configuração substituirá spark.yarn.jars e o archive será usado em todos os contêineres do aplicativo. O archive deve conter arquivos jar em seu diretório raiz. Como na opção anterior, o arquivo morto também pode ser hospedado no HDFS para acelerar a distribuição dos arquivos.

Os usuários podem configurar esse parâmetro para especificar seus jars, que são incluídos no caminho de classe do driver Spark.

DaRkMaN
fonte
1

Ao usar o envio de spark com --master yarn-cluster, o jar do aplicativo junto com os jars incluídos na opção --jars será automaticamente transferido para o cluster. URLs fornecidos após --jars devem ser separados por vírgulas. Essa lista está incluída nos caminhos de classe do driver e do executor

Exemplo:

spark-submit --master yarn-cluster --jars ../lib/misc.jar, ../lib/test.jar --class MainClass MainApp.jar

https://spark.apache.org/docs/latest/submitting-applications.html

Shiva Garg
fonte
0

Há restrições quanto ao uso --jars: se você deseja especificar um diretório para o local dejar/xml arquivo, ele não permite expansões de diretório. Isso significa que você precisa especificar o caminho absoluto para cada jar.

Se você especificar --driver-class-pathe estiver executando no modo de cluster de fios, a classe de driver não será atualizada. Podemos verificar se o caminho da classe está atualizado ou não no spark ui ou no servidor de histórico do spark no ambiente da guia.

A opção que funcionou para eu passar os frascos que contêm expansões de diretório e que funcionou no modo de cluster de fios foi a --confopção. É melhor passar caminhos de classe de driver e executor como --conf, o que os adiciona ao próprio objeto de sessão do spark e esses caminhos são refletidos na Configuração do Spark. Mas certifique-se de colocar os jarros no mesmo caminho no cluster.

spark-submit \
  --master yarn \
  --queue spark_queue \
  --deploy-mode cluster    \
  --num-executors 12 \
  --executor-memory 4g \
  --driver-memory 8g \
  --executor-cores 4 \
  --conf spark.ui.enabled=False \
  --conf spark.driver.extraClassPath=/usr/hdp/current/hbase-master/lib/hbase-server.jar:/usr/hdp/current/hbase-master/lib/hbase-common.jar:/usr/hdp/current/hbase-master/lib/hbase-client.jar:/usr/hdp/current/hbase-master/lib/zookeeper.jar:/usr/hdp/current/hbase-master/lib/hbase-protocol.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/scopt_2.11-3.3.0.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/spark-examples_2.10-1.1.0.jar:/etc/hbase/conf \
  --conf spark.hadoop.mapred.output.dir=/tmp \
  --conf spark.executor.extraClassPath=/usr/hdp/current/hbase-master/lib/hbase-server.jar:/usr/hdp/current/hbase-master/lib/hbase-common.jar:/usr/hdp/current/hbase-master/lib/hbase-client.jar:/usr/hdp/current/hbase-master/lib/zookeeper.jar:/usr/hdp/current/hbase-master/lib/hbase-protocol.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/scopt_2.11-3.3.0.jar:/usr/hdp/current/spark2-thriftserver/examples/jars/spark-examples_2.10-1.1.0.jar:/etc/hbase/conf \
  --conf spark.hadoop.mapreduce.output.fileoutputformat.outputdir=/tmp
Tanveer
fonte
Feliz Ano Novo!
YoYo 01/01
Feliz Ano Novo YoYo
Tanveer
0

Enquanto enviamos trabalhos de faísca usando o utilitário de envio de faísca, há uma opção --jars. Usando esta opção, podemos passar o arquivo jar para acionar aplicativos.

bala
fonte
A existência dessa —jaropção foi mencionada no pôster original, além de discutida com muito mais detalhes por mais de uma resposta. Não parece que você está fornecendo algo novo?
YoYo