[小ネタ] Glueのローカル実行でgluepytestをする時に気を付けたいこと
こんにちは!DA(データアナリティクス)事業本部 インテグレーション部の大高です。
Glueのライブラリはローカル実行用に公開されており、ローカル環境でも実行することができます。また、pytestの実行用に「gluepytest」コマンドも用意されているので、Pythonコードのテストにはこちらが利用可能です。
今回、実際にPythonコードのローカルでのテスト実行環境の検証を行ってみたのですが、いくつかハマったことがあったので注意点を書いてみたいと思います。
前提
今回試したのはGlue version 1.0(Python 3)で記述したGlueのコードをgluepytest
でテストしようとしたものになります。
また、環境構築については、下記の記事を参考に作成してみました。
この上で、以下が私がハマったことになります。
注意点1:Glue version 1.0(Python 3)の場合は「glue-1.0」のブランチを利用する
下記のGitHubリポジトリからaws-glue-libsをCloneする際ですが、ブランチが2つあります。1つはmaster
で、これはGlue version 0.9
になります。もう1つがglue-1.0
で、こちらはGlue version 1.0
になります。
今回はPython3であるGlue version 1.0
を利用したかったのでglue-1.0
ブランチからCloneすべきだったのですが、意識せずにCloneしていたのでmaster
ブランチからCloneしてしまっていました。
下記ドキュメントの「ローカル Python 開発の前提条件」の「2.」にもちゃんと書いてありました。
次のいずれかを行ってください。
- Glue バージョン 0.9 の場合は、 master ブランチにとどまります。
- Glue バージョン 1.0 の場合は、ブランチ glue-1.0 を確認します。このバージョンは Python 3 をサポートしています。
ちなみに、Clone先のパスはどこでも問題ありませんがPATHを通しておくと利用時に便利です。
注意点2:「SPARK_HOME」環境変数の設定
下記ドキュメント「ローカル Python 開発の前提条件」の「4.」と「5.」にある通り、Glue用のApache SparkをダウンロードしてSPARK_HOME
環境変数を適切に設定する必要があります。
Apache Spark自体はどこに置いてもよいのですが、SPARK_HOME
環境変数をきちんと置いた場所に合わせて設定しないとgluepytest
の実行時に、以下のようにpyspark
が無いというエラーが起きますので気を付けましょう。
E ModuleNotFoundError: No module named 'pyspark'
注意点3:Pythonのバージョン
Python3の場合、Pythonのサポートバージョンは3.6
になりますので、ここもキッチリ合わせます。うっかり3.8
で実行したりすると以下のようにエラーが起きます。
/opt/spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8/python/pyspark/cloudpickle.py:126: in _make_cell_set_template_code return types.CodeType( E TypeError: an integer is required (got type bytes)
Python シェルジョブを使用して、AWS Glue でシェルとして Python スクリプトを実行できます。Python シェルジョブを使用すると、Python 2.7 または Python 3.6 と互換性のあるスクリプトを実行することができます。
注意点4:Issueへの対応
基本的にはここまでの準備で良いのですが、gluepytest
を実行すると以下のエラーが起きました。
E py4j.protocol.Py4JJavaError: An error occurred while calling None.org.apache.spark.api.java.JavaSparkContext. E : java.lang.NoSuchMethodError: io.netty.buffer.PooledByteBufAllocator.defaultNumHeapArena()I
2020/03/10
現在においては、下記のIssueがあるようで、私が試した限りでは対応が必要でした。
私の対応としてはIssueへのコメントを参考に2つを実施しました。
1つは、下記2つのjarファイルを削除しました。
- aws-glue-libs/jarsv1/netty-3.6.2.Final.jar
- aws-glue-libs/jarsv1/netty-all-4.0.23.Final.jar
もう1つは、Issueへのコメントの通りにglue-setup.sh
を修正して、jarを削除する行を追加しておきました。
# Run mvn copy-dependencies target to get the Glue dependencies locally mvn -f $ROOT_DIR/pom.xml -DoutputDirectory=$ROOT_DIR/jarsv1 dependency:copy-dependencies # ADD THESE TWO LINES rm ${GLUE_JARS_DIR}/netty-3.6.2.Final.jar rm ${GLUE_JARS_DIR}/netty-all-4.0.23.Final.jar
注意点5:テスト対象コードの記述
上記までで正常にテスト実行はできるようになったのですが、今度は別の問題が発生しました。
テストコードからは、テスト対象コードをimportしてテストを記述しますが、Glueのコードが以下のようになっているとちょっと問題が起きます。
import sys from awsglue.transforms import * from awsglue.utils import getResolvedOptions from pyspark.context import SparkContext from awsglue.context import GlueContext from awsglue.job import Job def test_target_func(bar): foo = bar return foo ## @params: [JOB_NAME] args = getResolvedOptions(sys.argv, ['JOB_NAME'])
このようなジョブのtest_target_func
をテストしたいと思ってテストコードからimportすると、コード全体が評価がされてしまうので、以下のようにJOB_NAME
引数が無いよというエラーが起きてしまうのです。
E awsglue.utils.GlueArgumentError: the following arguments are required:
回避策としては、Glue側のコードをmain
関数に入れてあげて、かつ、自身のコードがエントリポイントとして呼ばれている時だけ実行されるように以下のコードを記述してあげます。
if __name__ == '__main__': main(sys.argv)
これで、テストコードでimportしてtest_target_func
のみをテストすることができました。
まとめ
以上、「Glueのローカル実行でgluepytestをする時に気を付けたいこと」でした。Glueのローカル実行はとても便利だと思うので、うまく活用していければと思います。
どなたかのお役に立てば幸いです。それでは!