Comment éliminer l'erreur « java.lang.ClassNotFoundException » dans Spark sur Amazon EMR ?

Dernière mise à jour : 02/03/2022

Lorsque j'utilise des fichiers JAR personnalisés dans une tâche spark-submit ou PySpark sur Amazon EMR, le message d'erreur java.lang .ClassNotFoundException s’affiche.

Brève description

Cette erreur se produit lorsque l'une des conditions suivantes est remplie :

  • La tâche spark-submit ne trouve pas les fichiers recherchés dans le chemin de classe.
  • Une action d'amorçage ou une configuration personnalisée annule les chemins de classe. Lorsque cela se produit, le chargeur de classe récupère uniquement les fichiers JAR existant à l'emplacement que vous avez défini dans votre configuration.

Solution

Vérifiez la trace de la pile pour trouver le nom de la classe manquante. Ensuite, ajoutez le chemin du JAR personnalisé (contenant la classe manquante) au chemin de classe Spark. Vous pouvez le faire lorsque le cluster est en cours d'exécution, lorsque vous lancez un nouveau cluster ou envoyez une tâche.

Sur un cluster en cours d'exécution

Dans /etc/spark/conf/spark-defaults.conf, ajoutez le chemin du fichier JAR personnalisé aux noms de classe spécifiés dans l'erreur de trace de la pile. Dans l'exemple suivant, /home/hadoop/extrajars/* est le chemin du fichier JAR personnalisé.

sudo vim /etc/spark/conf/spark-defaults.conf
spark.driver.extraClassPath <other existing jar locations>:/home/hadoop/extrajars/*
spark.executor.extraClassPath <other existing jar locations>:/home/hadoop/extrajars/*

Sur un nouveau cluster

Ajoutez le chemin du fichier JAR personnalisé aux chemins de classe existants dans /etc/spark/conf/spark-defaults.conf en fournissant un objet de configuration lorsque vous créez un cluster.

Remarque : pour utiliser cette option, vous devez créer un cluster à l'aide d'Amazon EMR, version 5.14.0 ou ultérieure.

Pour Amazon EMR 5.14.0 vers Amazon EMR 5.17.0, incluez les éléments suivants :

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.driver.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/home/hadoop/extrajars/*",
      "spark.executor.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/home/hadoop/extrajars/*"
    }
  }
]

Pour Amazon EMR 5.17.0 vers Amazon EMR 5.18.0, incluez /usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar comme chemin d'accès JAR supplémentaire :

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.driver.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/*",
      "spark.executor.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/*"
    }
  }
]

Pour Amazon EMR 5.19.0 vers Amazon EMR 5.32.0, mettez à jour le chemin d'accès au fichier JAR comme suit :

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.driver.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/*",
      "spark.executor.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/*"
    }
  }
]

Pour Amazon EMR 5.33.0 vers Amazon EMR 5.34.0, mettez à jour le chemin d'accès au fichier JAR comme suit :

[
  {
    "Classification": "spark-defaults",
    "Properties": {
      "spark.driver.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/",
      "spark.executor.extraClassPath": "/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/home/hadoop/extrajars/"
    }
  }
]

Pour les versions 6.0.0 et ultérieures d'Amazon EMR, la mise à jour du chemin d'accès JAR à l'aide de la configuration ne fonctionne pas. Avec ces versions, le fichier .conf contient plusieurs chemins de fichier jar. En outre, la longueur de la configuration de chaque propriété que vous mettez à jour ne doit pas dépasser 1 024 caractères. Vous pouvez toutefois ajouter une action d'amorçage pour transmettre l'emplacement JAR personnalisé à spark-defaults.conf. Pour plus d’informations, consultez Comment mettre à jour tous les nœuds Amazon EMR après la phase d'amorçage ?

Créez un script bash similaire à celui ci-dessous :

Remarque :

  • Veillez à remplacer s3://doc-example-bucket/Bootstraps/script_b.sh par le chemin Amazon Simple Storage Service (Amazon S3) de votre choix.
  • Veillez à remplacer/home/hadoop/extrajars/* par le chemin d'accès de votre fichier JAR personnalisé.
  • Assurez-vous que le rôle d'exécution Amazon EMR dispose des autorisations nécessaires pour accéder à ce compartiment S3.
#!/bin/bash
#
# This is an example of script_b.sh for changing /etc/spark/conf/spark-defaults.conf
#
while [ ! -f /etc/spark/conf/spark-defaults.conf ]
do
  sleep 1
done
#
# Now the file is available, do your work here
#
sudo sed -i '/spark.*.extraClassPath/s/$/:\/home\/hadoop\/extrajars\/\*/' /etc/spark/conf/spark-defaults.conf
exit 0

Lancez le cluster EMR et ajoutez une action d'amorçage similaire à ce qui suit :

#!/bin/bash
pwd
aws s3 cp s3://doc-example-bucket/Bootstraps/script_b.sh .
chmod +x script_b.sh
nohup ./script_b.sh &

Pour une seule tâche

Utilisez l'option --jars pour transmettre le chemin du fichier JAR personnalisé lorsque vous exécutez spark-submit.

Exemple :

spark-submit --deploy-mode client --class org.apache.spark.examples.SparkPi --master yarn spark-examples.jar 100 --jars /home/hadoop/extrajars/*

Remarque : pour éviter les conflits de classe, n'incluez pas les fichiers JAR standard lorsque vous utilisez l'option --jars. Ainsi, n'incluez pas le fichier spark-core.jar, car il existe déjà dans le cluster.

Pour plus d'informations sur les classifications de configuration, consultez Configurer Spark.