Amazon EMR の Spark で発生する「java.lang.ClassNotFoundException」を解決するにはどうすればよいですか?

最終更新日: 2022 年 3 月 2 日

Amazon EMR の spark-submit または PySpark ジョブでカスタムの JAR ファイルを使用すると、「java.lang.ClassNotFoundException error」エラーが発生します。

簡単な説明

このエラーは、以下の条件のいずれかに当てはまる場合に発生します。

  • spark-submit ジョブが、クラスパス内に関連ファイルを見つけることができない場合。
  • クラスパスを、ブートストラップアクションまたはカスタム設定が上書きしている場合。この場合、クラスローダーは、設定内で指定した場所に存在する JAR ファイルだけをピックアップします。

解決方法

不足しているクラスの名前を見つけるためにスタックトレースを確認します。次に、(不足しているクラスを含む) カスタム JAR のパスを Spark クラスパスに追加します。このパスは、クラスターの稼働中、新しいクラスターの起動時、またはジョブの送信時に追加することができます。

実行中のクラスターの場合

/etc/spark/conf/spark-defaults.conf 内の、エラースタックトレースで指定されているクラス名に、カスタム JAR のパスを追加します。次の例では、/home/hadoop/extrajars/* がカスタム JAR パスです。

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/*

新しいクラスターの場合

クラスターの作成時に設定オブジェクトを指定して、/etc/spark/conf/spark-defaults.conf 内の既存のクラスパスに、カスタム JAR パスを追加します。

注: このオプションを使用するには、Amazon EMR のリリースバージョン 5.14.0 以降を使用して、クラスターを作成する必要があります。

Amazon EMR 5.14.0 から 5.17.0 を使用する場合は、以下を含めます。

[
  {
    "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/*"
    }
  }
]

Amazon EMR 5.17.0 から 5.18.0 を使用する場合は、JAR パスとして /usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar を追加します。

[
  {
    "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/*"
    }
  }
]

Amazon EMR 5.19.0 から 5.32.0 の場合は、次のように JAR パスを更新します。

[
  {
    "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/*"
    }
  }
]

Amazon EMR 5.33.0 から 5.34.0 の場合は、次のように JAR パスを更新します。

[
  {
    "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/"
    }
  }
]

Amazon EMR リリースバージョン 6.0.0 以降では、設定により JAR パスを更新することはできません。これらのバージョンの .conf ファイルには、複数の jar ファイルパスが記述されています。また、各プロパティを更新する際、長さが 1024 文字を超える設定値は使用できません。ただし、ブートストラップアクションを追加することで、カスタム JAR の場所を spark-defaults.conf に渡すことは可能です。詳細については、「ブートストラップフェーズ後にすべての Amazon EMR ノードを更新する方法を教えてください。」を参照してください。

次のような Bash スクリプトを作成します。

注:

  • s3://doc-example-bucket/Bootstraps/script_b.sh は、使用している Amazon Simple Storage Service (Amazon S3) のパスに置き換えてください。
  • /home/hadoop/extrajars/* は、実際のカスタム JAR ファイルのパスに置き換えてください。
  • Amazon EMR の実行ロールには、この 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

EMR クラスターを起動し、次のようなブートストラップアクションを追加します。

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

単一ジョブの場合:

spark-submit を実行する際にカスタム JAR パスを渡すには、--jars オプションを使用します。

例:

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

注: クラスの競合を防ぐために、--jars オプションを使用する場合は、標準の JAR を含めないでください。例えば、spark-core.jar はクラスター内にすでに存在するので、新たに含めないようにします。

分類の設定の詳細については、「Configure Spark」(Spark の設定) を参照してください。