Saltar al contenido

Resolución de problemas de dependencia en Apache Spark

Solución:

La ruta de clase de Apache Spark se construye dinámicamente (para adaptarse al código de usuario por aplicación), lo que lo hace vulnerable a tales problemas. La respuesta de @ user7337271 es correcta, pero hay algunas preocupaciones más, dependiendo de la Cluster Manager (“maestro”) que está utilizando.

Primero, una aplicación Spark consta de estos componentes (cada uno es una JVM separada, por lo tanto, potencialmente contiene diferentes clases en su ruta de clases):

  1. Conductor: esa es tu aplicación creando un SparkSession (o SparkContext) y conectarse a un administrador de clúster para realizar el trabajo real
  2. Cluster Manager: sirve como un “punto de entrada” al clúster, encargado de asignar ejecutores para cada aplicación. Hay varios tipos diferentes compatibles con Spark: independiente, YARN y Mesos, que describiremos a continuación.
  3. Ejecutores: estos son los procesos en los nodos del clúster, que realizan el trabajo real (ejecutando Spark Tareas)

La relación entre estos se describe en este diagrama de la descripción general del modo de clúster de Apache Spark:

Descripción general del modo de clúster

Ahora – ¿Qué clases deben residir en cada uno de estos componentes?

Esto se puede responder mediante el siguiente diagrama:

Descripción general de la ubicación de la clase

Analicemos eso lentamente:

  1. Código de chispa son las bibliotecas de Spark. Deberían existir en TODOS Tres componentes ya que incluyen el pegamento que permite a Spark realizar la comunicación entre ellos. Por cierto, los autores de Spark tomaron una decisión de diseño para incluir código para TODOS los componentes en TODOS los componentes (por ejemplo, para incluir código que solo debería ejecutarse en Executor en el controlador también) para simplificar esto, por lo que el “fat jar” de Spark (en versiones hasta 1.6 ) o “archivo” (en 2.0, detalles a continuación) contienen el código necesario para todos los componentes y deberían estar disponibles en todos ellos.

  2. Código solo para el conductor este es un código de usuario que no incluye nada que deba usarse en los Ejecutores, es decir, código que no se usa en ninguna transformación en el RDD / DataFrame / Dataset. Esto no tiene que estar necesariamente separado del código de usuario distribuido, pero puede serlo.

  3. Código distribuido Este es el código de usuario que se compila con el código del controlador, pero también debe ejecutarse en los Ejecutores; todo lo que utilizan las transformaciones reales debe incluirse en este (s) jar (s).

Ahora que lo entendimos bien cómo ¿Conseguimos que las clases se carguen correctamente en cada componente y qué reglas deben seguir?

  1. Código de chispa: como indican las respuestas anteriores, debe usar el mismo Scala y Chispa – chispear Versiones en todos los componentes.

    1,1 en Ser único modo, hay una instalación Spark “preexistente” a la que las aplicaciones (controladores) pueden conectarse. Eso significa que todos los controladores deben usar la misma versión de Spark ejecutándose en el maestro y ejecutores.

    1,2 en HILO / Mesos, cada aplicación puede usar una versión diferente de Spark, pero todos los componentes de la misma aplicación deben usar la misma. Eso significa que si usó la versión X para compilar y empaquetar su aplicación de controlador, debe proporcionar la misma versión al iniciar SparkSession (por ejemplo, a través de spark.yarn.archive o spark.yarn.jars parámetros cuando se utiliza YARN). Los frascos / archivo que proporcione deben incluir todas las dependencias de Spark (incluidas las dependencias transitivas), y el administrador del clúster lo enviará a cada ejecutor cuando se inicie la aplicación.

  2. Código del conductor: eso depende completamente de – el código del controlador se puede enviar como un montón de frascos o un “frasco grande”, siempre que incluya todas las dependencias de Spark + todo el código de usuario

  3. Código distribuido: además de estar presente en el controlador, este código debe enviarse a los ejecutores (nuevamente, junto con todas sus dependencias transitivas). Esto se hace usando el spark.jars parámetro.

Para resumir, aquí hay un enfoque sugerido para crear e implementar una aplicación Spark (en este caso, usando YARN):

  • Cree una biblioteca con su código distribuido, empaquételo como un jar “normal” (con un archivo .pom que describe sus dependencias) y como un “jar gordo” (con todas sus dependencias transitivas incluidas).
  • Cree una aplicación de controlador, con dependencias de compilación en su biblioteca de código distribuido y en Apache Spark (con una versión específica)
  • Empaquete la aplicación del controlador en un frasco grande para implementarlo en el controlador
  • Pase la versión correcta de su código distribuido como el valor de spark.jars parámetro al iniciar el SparkSession
  • Pase la ubicación de un archivo de almacenamiento (por ejemplo, gzip) que contiene todos los archivos jar debajo lib/ carpeta de los binarios de Spark descargados como el valor de spark.yarn.archive

Al crear e implementar aplicaciones Spark, todas las dependencias requieren versiones compatibles.

  • Versión Scala. Todos los paquetes deben usar la misma versión principal de Scala (2.10, 2.11, 2.12).

    Considere seguir (incorrecto) build.sbt:

    name := "Simple Project"
    
    version := "1.0"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" % "spark-core_2.11" % "2.0.1",
       "org.apache.spark" % "spark-streaming_2.10" % "2.0.1",
       "org.apache.bahir" % "spark-streaming-twitter_2.11" % "2.0.1"
    )
    

    Usamos spark-streaming para Scala 2.10, mientras que los paquetes restantes son para Scala 2.11. A válido el archivo podría ser

    name := "Simple Project"
    
    version := "1.0"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" % "spark-core_2.11" % "2.0.1",
       "org.apache.spark" % "spark-streaming_2.11" % "2.0.1",
       "org.apache.bahir" % "spark-streaming-twitter_2.11" % "2.0.1"
    )
    

    pero es mejor especificar la versión globalmente y usar %% (que agrega la versión scala para usted):

    name := "Simple Project"
    
    version := "1.0"
    
    scalaVersion := "2.11.7"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" %% "spark-core" % "2.0.1",
       "org.apache.spark" %% "spark-streaming" % "2.0.1",
       "org.apache.bahir" %% "spark-streaming-twitter" % "2.0.1"
    )
    

Del mismo modo en Maven:

    
      com.example
      simple-project
      4.0.0
      Simple Project
      jar
      1.0
      
        2.0.1
       
      
         
          org.apache.spark
          spark-core_2.11
          $spark.version
        
        
          org.apache.spark
          spark-streaming_2.11
          $spark.version
         
        
          org.apache.bahir
          spark-streaming-twitter_2.11
          $spark.version
        
      
    
  • Versión Spark Todos los paquetes deben usar la misma versión principal de Spark (1.6, 2.0, 2.1, …).

    Considere lo siguiente (incorrecto) build.sbt:

    name := "Simple Project"
    
    version := "1.0"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" % "spark-core_2.11" % "1.6.1",
       "org.apache.spark" % "spark-streaming_2.10" % "2.0.1",
       "org.apache.bahir" % "spark-streaming-twitter_2.11" % "2.0.1"
    )
    

    Usamos spark-core 1.6 mientras que los componentes restantes están en Spark 2.0. A válido el archivo podría ser

    name := "Simple Project"
    
    version := "1.0"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" % "spark-core_2.11" % "2.0.1",
       "org.apache.spark" % "spark-streaming_2.10" % "2.0.1",
       "org.apache.bahir" % "spark-streaming-twitter_2.11" % "2.0.1"
    )
    

    pero es mejor usar una variable
    (todavía incorrecto):

    name := "Simple Project"
    
    version := "1.0"
    
    val sparkVersion = "2.0.1"
    
    libraryDependencies ++= Seq(
       "org.apache.spark" % "spark-core_2.11" % sparkVersion,
       "org.apache.spark" % "spark-streaming_2.10" % sparkVersion,
       "org.apache.bahir" % "spark-streaming-twitter_2.11" % sparkVersion
    )
    

Del mismo modo en Maven:

    
      com.example
      simple-project
      4.0.0
      Simple Project
      jar
      1.0
      
        2.0.1
        2.11
       
      
         
          org.apache.spark
          spark-core_$scala.version
          $spark.version
        
        
          org.apache.spark
          spark-streaming_$scala.version
          $spark.version
         
        
          org.apache.bahir
          spark-streaming-twitter_$scala.version
          $spark.version
        
      
    
  • La versión de Spark utilizada en las dependencias de Spark debe coincidir con la versión de Spark de la instalación de Spark. Por ejemplo si usa 1.6.1 en el clúster, debe usar 1.6.1 para construir frascos. Las versiones menores que no coinciden no siempre se aceptan.

  • La versión de Scala utilizada para compilar jar debe coincidir con la versión de Scala utilizada para compilar Spark implementado. De forma predeterminada (binarios descargables y compilaciones predeterminadas):

    • Spark 1.x -> Scala 2.10
    • Spark 2.x -> Scala 2.11
  • Los paquetes adicionales deben estar accesibles en los nodos de trabajo si se incluyen en el frasco de grasa. Hay varias opciones que incluyen:

    • --jars argumento a favor spark-submit – distribuir local jar archivos.
    • --packages argumento a favor spark-submit – para recuperar dependencias del repositorio de Maven.

    Al enviar en el nodo del clúster, debe incluir la aplicación jar en --jars.

Además de la respuesta muy extensa ya dada por el usuario7337271, si el problema resulta de la falta de dependencias externas, puede construir un jar con sus dependencias con, por ejemplo, el complemento de ensamblaje de maven

En ese caso, asegúrese de marcar todas las dependencias centrales de Spark como “provistas” en su sistema de compilación y, como ya se señaló, asegúrese de que se correlacionen con su versión de Spark en tiempo de ejecución.

Si te sientes estimulado, tienes el poder dejar un escrito acerca de qué le añadirías a esta división.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *