Agregar frascos a un trabajo Spark - spark-submit

158

Es cierto ... se ha discutido bastante.

Sin embargo, hay mucha ambigüedad y algunas de las respuestas proporcionadas ... incluyendo duplicar referencias de jarras en la configuración u opciones de jarras / ejecutor / controlador.

Los detalles ambiguos y / u omitidos

Después de la ambigüedad, se deben aclarar los detalles poco claros y / u omitidos para cada opción:

  • Cómo se ve afectado ClassPath
    • Conductor
    • Ejecutor (para tareas en ejecución)
    • Ambos
    • De ningún modo
  • Carácter de separación: coma, dos puntos, punto y coma
  • Si los archivos provistos se distribuyen automáticamente
    • para las tareas (a cada ejecutor)
    • para el controlador remoto (si se ejecuta en modo de clúster)
  • tipo de URI aceptado: archivo local, hdfs, http, etc.
  • Si se copia en una ubicación común, ¿dónde está esa ubicación (hdfs, local?)

Las opciones a las que afecta:

  1. --jars
  2. SparkContext.addJar(...) método
  3. SparkContext.addFile(...) método
  4. --conf spark.driver.extraClassPath=... o --driver-class-path ...
  5. --conf spark.driver.extraLibraryPath=...o --driver-library-path ...
  6. --conf spark.executor.extraClassPath=...
  7. --conf spark.executor.extraLibraryPath=...
  8. Sin olvidar que el último parámetro del envío de chispas también es un archivo .jar.

Soy consciente de dónde puedo encontrar la documentación principal de chispa , y específicamente sobre cómo enviar , las opciones disponibles y también el JavaDoc . Sin embargo, eso me dejó todavía algunos agujeros, aunque también respondió parcialmente.

Espero que no sea tan complejo y que alguien pueda darme una respuesta clara y concisa.

Si tuviera que adivinar a partir de la documentación, parece que --jars, y los métodos SparkContext addJary addFileson los que distribuirán automáticamente los archivos, mientras que las otras opciones simplemente modifican la ClassPath.

¿Sería seguro asumir que, por simplicidad, puedo agregar archivos jar de aplicaciones adicionales usando las 3 opciones principales al mismo tiempo:

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

Encontré un buen artículo sobre una respuesta a otra publicación . Sin embargo, nada nuevo aprendido. El póster hace un buen comentario sobre la diferencia entre el controlador local (cliente de hilo) y el controlador remoto (grupo de hilo). Definitivamente importante tener en cuenta.

Yoyó
fuente
1
¿Con qué administrador de clúster estás ejecutando? Independiente / HILO / Mesos?
Yuval Itzchakov
Alguna. Pretendo esto como una aclaración a la documentación original. Estoy utilizando principalmente clúster independiente, instancia única, cliente de hilo, grupo de hilo. Otros podrían estar usando Mesos. Parece que hiciste una buena investigación original en tu blog sobre esto. Terminé haciendo casi lo mismo que tú: usar un sombreador para crear un jar Uber para simplificar mi proceso de implementación.
YoYo
1
Publicaré una respuesta sobre cómo implementamos Spark Standalone, que puede aclarar algunas cosas.
Yuval Itzchakov
66
Me he esforzado por responder a todas sus preguntas. Espero que ayude :)
Yuval Itzchakov
@Yuval Itzchakov, al igual que Yoyo mencionó, yo también uso un frasco sombreado para agrupar todas mis dependencias, por ejemplo, clases de casos y otros frascos que pueda estar usando. Estoy tratando de entender cuándo me encontraría con una situación en la que necesito varios frascos. Quiero decir que siempre puedo agrupar esos frascos múltiples en 1 frasco súper. ¿Por qué no puedo seguir viviendo con mis tarros sombreados agrupando todas mis dependencias?
Sheel Pancholi

Respuestas:

177

ClassPath:

ClassPath se ve afectado según lo que proporcione. Hay un par de formas de configurar algo en el classpath:

  • spark.driver.extraClassPatho es un alias --driver-class-pathpara establecer rutas de clase adicionales en el nodo que ejecuta el controlador.
  • spark.executor.extraClassPath para establecer una ruta de clase adicional en los nodos Worker.

Si desea que cierto JAR se efectúe tanto en el Maestro como en el Trabajador, debe especificarlos por separado en AMBOS indicadores.

Carácter de separación:

Siguiendo las mismas reglas que la JVM :

  • Linux: dos puntos :
    • p.ej: --conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar:/opt/prog/aws-java-sdk-1.10.50.jar"
  • Windows: un punto y coma ;
    • p.ej: --conf "spark.driver.extraClassPath=/opt/prog/hadoop-aws-2.7.1.jar;/opt/prog/aws-java-sdk-1.10.50.jar"

Distribución de archivos:

Esto depende del modo en el que esté ejecutando su trabajo:

  1. Modo de cliente: Spark enciende un servidor HTTP Netty que distribuye los archivos al inicio para cada uno de los nodos de trabajo. Puedes ver eso cuando comienzas tu trabajo de 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 clúster: en el modo de clúster, spark seleccionó un nodo de trabajador líder para ejecutar el proceso del controlador. Esto significa que el trabajo no se ejecuta directamente desde el nodo maestro. Aquí, Spark no configurará un servidor HTTP. Debe hacer que sus JARS estén disponibles manualmente para todos los nodos de trabajo a través de HDFS / S3 / Otras fuentes que están disponibles para todos los nodos.

URI aceptados para archivos

En "Envío de solicitudes" , la documentación de Spark hace un buen trabajo al explicar los prefijos aceptados para los archivos:

Cuando se utiliza el envío de chispas, el frasco de la aplicación junto con los frascos incluidos con la opción --jars se transferirán automáticamente al clúster. Spark usa el siguiente esquema de URL para permitir diferentes estrategias para diseminar frascos:

  • archivo: - Las rutas absolutas y el archivo: / URI son servidos por el servidor de archivos HTTP del controlador, y cada ejecutor extrae el archivo del servidor HTTP del controlador.
  • hdfs :, http :, https :, ftp: estos archivos desplegables y JAR del URI como se esperaba
  • local: un URI que comienza con local: / se espera que exista como un archivo local en cada nodo de trabajo. Esto significa que no se incurrirá en IO de red, y funciona bien para archivos / JAR grandes que se envían a cada trabajador o se comparten a través de NFS, GlusterFS, etc.

Tenga en cuenta que los archivos JAR y los archivos se copian en el directorio de trabajo para cada SparkContext en los nodos ejecutores.

Como se señaló, los JAR se copian en el directorio de trabajo para cada nodo Worker. ¿Dónde es exactamente eso? Por lo general/var/run/spark/work , está debajo , los verá así:

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

Y cuando mires dentro, verás todos los JAR que desplegaste:

[*@*]$ 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

Opciones afectadas:

Lo más importante para entender es la prioridad . Si pasa cualquier propiedad a través del código, tendrá prioridad sobre cualquier opción que especifique spark-submit. Esto se menciona en la documentación de Spark:

Cualquier valor especificado como banderas o en el archivo de propiedades se pasará a la aplicación y se combinará con los especificados a través de SparkConf. Las propiedades configuradas directamente en SparkConf tienen mayor prioridad , luego las banderas pasan a spark-submit o spark-shell, luego las opciones en el archivo spark-defaults.conf

Así que asegúrese de establecer esos valores en los lugares adecuados, para que no se sorprenda cuando uno tiene prioridad sobre el otro.

Analicemos cada opción en cuestión:

  • --jarsvs SparkContext.addJar: Estos son idénticos, solo uno se configura mediante envío de chispa y otro mediante código. Elija el que mejor le convenga. Una cosa importante a tener en cuenta es que el uso de cualquiera de estas opciones no agrega el JAR a su classpath de controlador / ejecutor , deberá agregarlos explícitamente usando la extraClassPathconfiguración en ambos.
  • SparkContext.addJarvs SparkContext.addFile: Use el primero cuando tenga una dependencia que deba usarse con su código. Use este último cuando simplemente desee pasar un archivo arbitrario a sus nodos de trabajo, lo que no es una dependencia en tiempo de ejecución en su código.
  • --conf spark.driver.extraClassPath=...o --driver-class-path: estos son alias, no importa cuál elijas
  • --conf spark.driver.extraLibraryPath=..., or --driver-library-path ... Igual que el anterior, alias.
  • --conf spark.executor.extraClassPath=...: Use esto cuando tenga una dependencia que no se puede incluir en un JAR uber (por ejemplo, porque hay conflictos de tiempo de compilación entre las versiones de la biblioteca) y que necesita cargar en tiempo de ejecución.
  • --conf spark.executor.extraLibraryPath=...Esto se pasa como la java.library.pathopción para la JVM. Use esto cuando necesite una ruta de biblioteca visible para la JVM.

¿Sería seguro asumir que, por simplicidad, puedo agregar archivos jar de aplicaciones adicionales usando las 3 opciones principales al mismo tiempo:

Puede asumir esto con seguridad solo para el modo Cliente, no para el modo Clúster. Como he dicho anteriormente. Además, el ejemplo que dio tiene algunos argumentos redundantes. Por ejemplo, pasar JAR a --driver-library-pathes inútil, debe pasárselos extraClassPathsi desea que estén en su classpath. En definitiva, lo que desea hacer cuando implementa JAR externos tanto en el controlador como en el trabajador es:

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
fuente
44
Gran y completa respuesta. Gracias. ¿También podría contar más sobre las mejores prácticas en la implementación con uber JAR frente a dependencias fuera de JAR (libs en la carpeta externa y listadas en el MANIFEST.MFarchivo)?
jsosnowski
2
@jsosnowski Por lo general, solo aplazo el uso de frascos externos cuando hay conflictos que son muy complejos de resolver con mi uber JAR. Por lo general, me las arreglo simplemente usando SBT assemblyMergeStrategyy seleccionando las clases que necesito si hay conflictos. En general, recomendaría lo mismo.
Yuval Itzchakov
9
@ Yuval-itzchakov Gracias por la gran respuesta, muy útil. Un punto que quisiera enfatizar para ayudar a otros que pueden haber cometido el mismo error que yo. El argumento --jars solo transporta los tarros a cada máquina en el clúster. NO le dice a spark que los use en la búsqueda de ruta de clase. También se requiere --driver-class-path (o argumentos similares o parámetros de configuración). Al principio pensé que eran formas alternativas de hacer lo mismo.
Tim Ryan
1
@TimRyan Definitivamente. Si miras la última parte de la respuesta, paso los frascos tanto a la --jarsbandera como a la ruta de la clase del conductor / ejecutor.
Yuval Itzchakov
1
Finalmente encontré cómo inyectar variables de entorno en zeppelin-env.shy añadido --jarsa SPARK_SUBMIT_OPTIONS. Eso funciono. El formato URI que uso es --jars=local:///mnt/dir/file.jar.
Mike
4

Otro enfoque spark 2.1.0es usar --conf spark.driver.userClassPathFirst=truedurante el envío de chispas que cambia la prioridad de la carga de dependencia y, por lo tanto, el comportamiento del trabajo de chispa, dando prioridad a los tarros que el usuario agrega a la ruta de clase con la --jarsopción.

Stanislav
fuente
2
Tendrá que tener cuidado con eso, ya que es posible romper la chispa al hacerlo. Esta debería ser una última opción de solución. Potencialmente, podría interferir con la capa que interactúa con el hilo también cuando se usa en modo cliente de hilo, aunque no estoy seguro.
YoYo
Gracias por el aviso. ¿Hay alguna forma de priorizar solo 1 jar, que definitivamente existe en el servidor en una versión anterior pero no puede reemplazarlo físicamente y sabe que no desea usarlo?
Stanislav
1
Supongo que en ese caso podrías intentar exactamente como sugeriste. No dije que era un no absoluto. También tenga en cuenta que la opción está marcada como 'experimental': ¡una advertencia a tener en cuenta! No hay una forma segura de priorizar una versión de una biblioteca sobre otra. En algunas implementaciones, esto se resuelve moviendo una de las bibliotecas en un espacio de nombres diferente, para que pueda usar ambas versiones al mismo tiempo.
YoYo
1

Otras opciones configurables de Spark relacionadas con jarras y classpath, en el caso de un yarnmodo de implementación, son las siguientes:
De la documentación de spark,

spark.yarn.jars

Lista de bibliotecas que contienen código Spark para distribuir a contenedores YARN. Por defecto, Spark en YARN usará los frascos Spark instalados localmente, pero los frascos Spark también pueden estar en una ubicación legible en todo el mundo en HDFS. Esto permite a YARN almacenarlo en caché en los nodos para que no necesite distribuirse cada vez que se ejecuta una aplicación. Para apuntar a frascos en HDFS, por ejemplo, establezca esta configuración en hdfs: /// some / path. Se permiten globos.

spark.yarn.archive

Un archivo que contiene los frascos Spark necesarios para su distribución al caché YARN. Si se establece, esta configuración reemplaza spark.yarn.jars y el archivo se usa en todos los contenedores de la aplicación. El archivo debe contener archivos jar en su directorio raíz. Al igual que con la opción anterior, el archivo también se puede alojar en HDFS para acelerar la distribución de archivos.

Los usuarios pueden configurar este parámetro para especificar sus jarras, que se incluyen en el classpath del controlador Spark.

Hombre oscuro
fuente
1

Al usar spark-submit con --master yarn-cluster, el frasco de la aplicación junto con los frascos incluidos con la opción --jars se transferirán automáticamente al clúster. Las URL proporcionadas después de --jars deben estar separadas por comas. Esa lista está incluida en las rutas de clase del controlador y del ejecutor

Ejemplo:

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
fuente
0

Existe una restricción en el uso --jars: si desea especificar un directorio para la ubicación del jar/xmlarchivo, no permite expansiones de directorio. Esto significa que si necesita especificar una ruta absoluta para cada jar.

Si especifica --driver-class-pathy está ejecutando en modo cluster de hilo, la clase de controlador no se actualiza. Podemos verificar si la ruta de clase se actualiza o no en spark ui o en el servidor de historial de chispas en el entorno de pestaña.

La opción que funcionó para mí para pasar frascos que contienen expansiones de directorio y que funcionó en modo cluster de hilo fue la --confopción. Es mejor pasar rutas de clase de controlador y ejecutor como --conf, lo que las agrega al objeto de sesión de chispa en sí mismo y esas rutas se reflejan en Spark Configuration. Pero asegúrese de colocar frascos en la misma ruta a través del clúster.

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
fuente
¡Feliz año nuevo!
YoYo
Feliz año nuevo YoYo
Tanveer
0

Si bien enviamos trabajos de chispa utilizando la utilidad de envío de chispas, hay una opción --jars. Con esta opción, podemos pasar el archivo jar a aplicaciones de chispa.

bala
fuente
Que hay esto —jar opción fue mencionada por el póster original, más discutido en mucho más detalle por más de una respuesta. ¿No parece que estás proporcionando algo nuevo?
YoYo