Glue Jupyter Notebookでパラメータを扱う際の１案

# はじめに
データの加工処理によく使用するAWS Glueは、Jupyter Notebookを用いてインタラクティブに開発できる環境を提供しています。

https://aws.amazon.com/jp/about-aws/whats-new/2022/04/aws-glue-notebook-based-job-authoring/

これによってGlueの開発体験が劇的に向上しました。

そこで課題となるのが、”インタラクティブセッションでの開発時と、JOB実行時で、同じスクリプトにしたい”という点です。
開発都度、”インタラクティブセッション用のスクリプト部分をコメントアウトして、JOB実行時用のスクリプトをアンコメントする”ということをやるのはミスを招きかねず、何か問題が発生した際にその部分の影響を考慮に入れなければならなくなります。

今回はパラメータを与える方法について、これらの課題を解決する私なりの案を記事にしてみました。





# 参考

https://docs.aws.amazon.com/ja_jp/glue/latest/ug/using-notebooks-overview.html

https://qiita.com/zumax/items/5233178046733371d4eb

https://docs.aws.amazon.com/ja_jp/glue/latest/dg/aws-glue-api-crawler-pyspark-extensions-get-resolved-options.html

https://qiita.com/YujiHamada3/items/780af5c7bb5b9d4fa997


# 結論

まずインタラクティブセッションで使用するパラメータを宣言します。
```python
# インタラクティブセッションで使うパラメータの値
dicParams = {
    'hogehoge':'インタラクティブほげほげ',
    'mogemoge':'インタラクティブもげもげ'
}
```
次に、JOB実行時の際は上記で宣言したパラメータのkeyで引数を取得・上書きする処理を記述します。
```python
# JOBでの実行時は'--JOB_ID'が入ってくるようなので、それを判断基準とする
if '--JOB_ID' in sys.argv:
    args = getResolvedOptions( sys.argv,list( dicParams.keys() ) )
    for eachParamKey in dicParams:
        dicParams[eachParamKey] = args[eachParamKey]
```


# 試したこと

以下、調査時に行ったことです。

## パラメータなし時の動き

まず、パラメータを取り出す際に用いられるsys.argvに何が入ってくるのかを見てみました。

```:インタラクティブセッションの場合
print(sys.argv)

['/tmp/7435407421286361']
```
JOB実行の際は、Runsタブにある実行したJOBのところから、CloudWatchへのリンクがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/262d7f27-70d5-12f8-0619-24fafeabc955.png)
```:JOB実行の場合（CloudWatchで確認）
['/tmp/20221107-sample.py', 'true', 'library-set', 'analytics', '--JOB_ID', '<ID文字列>', '--encryption-type', 'sse-s3', '--JOB_RUN_ID', '<ID文字列>', '--JOB_NAME', '20221107-sample', '--TempDir', '<出力先S3パス>']
```

## パラメータあり時の動き

パラメータを入れて実行するには、`Run with parameters`で指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/cabe6f25-2d65-7f0d-f170-834e1cebae6c.png)

たたまれているJob parametersを開いて、KeyとValueを記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/4e268692-9401-dd5d-11d1-ff331faab19d.png)


インタラクティブセッションにこの設定は影響がなく、sys.argvの結果に変わりありません。
一方JOB実行では引数が入ってきます。
```:パラメータ付きJOB実行の場合（CloudWatchで確認）
['/tmp/20221107-sample.py', 'true', 'library-set', 'analytics', '--hogehoge', 'ほげほげ', '--JOB_ID', '<ID文字列>', '--encryption-type', 'sse-s3', '--JOB_RUN_ID', '<ID文字列>', '--JOB_NAME', '20221107-sample', '--TempDir', '<出力先S3パス>']
```

## パラメータを取り出す

取り出すためには以下の様にします。
```
args = getResolvedOptions(sys.argv,['hogehoge'])
print(args['hogehoge'])
```

パラメータを与えてJOB実行すると、ログから取得できることが確認できます。
```:パラメータ付きJOB実行の場合（CloudWatchで確認）
ほげほげ
```

ですがインタラクティブセッションではエラーになります。
```:インタラクティブセッションの場合
GlueArgumentError: the following arguments are required: --hogehoge
```

:::note warn
またJOB実行時でも、パラメータなしで実行すると、以下の様にエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/e9636d2b-c1f9-f1d7-b0d8-f498b015c76e.png)
（リトライの回数だけ再実行がかかりますので注意）
:::

## 起動方法で分岐

これらを考慮して、「インタラクティブセッションの時はパラメータをセルで定義」し、「JOB実行の時は与えられたパラメータを使用する」という風にしました。

以下は最初に書いた内容にprintを入れただけになります。
```python
# インタラクティブセッションで使うパラメータの値
dicParams = {
    'hogehoge':'インタラクティブほげほげ',
    'mogemoge':'インタラクティブもげもげ'
}

# JOBでの実行時は'--JOB_ID'が入ってくるようなので、それを判断基準とする
if '--JOB_ID' in sys.argv:
    args = getResolvedOptions( sys.argv,list( dicParams.keys() ) )
    for eachParamKey in dicParams:
        dicParams[eachParamKey] = args[eachParamKey]


print(dicParams)
```

それぞれの実行状態での結果は以下です。
```:インタラクティブセッションの場合
{'hogehoge': 'インタラクティブほげほげ', 'mogemoge': 'インタラクティブもげもげ'}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/e0dbccb8-4842-2da9-eb20-243d5aa6f932.png)
```:パラメータ付きJOB実行の場合（CloudWatchで確認）
{'hogehoge': 'JOBほげほげ', 'mogemoge': 'JOBもげもげ'}
```

想定通り、インタラクティブセッション時もJOB実行時も、スクリプトを修正することなく動かすことができました。


Step Functionsからも起動させてみます。パラメータは以下の様に与えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/5a8b7e8f-ed3d-9698-4a52-efb6f3d2e7dc.png)

（ロールは自動で作成して）実行すると、ログには以下の様に出てきました。
```:Step Functionsから実行の場合（CloudWatchで確認）
{'hogehoge': 'StepFunctionsほげほげ', 'mogemoge': 'StepFunctionsもげもげ'}
```

# おわりに

Jupyter Notebookはインタラクティブにスクリプトを記述できるので、複雑な処理を開発する際には大変強力です。
せっかくの開発体験を損なわないよう考えた案を紹介しました。

他にいい案や工夫がある方は是非教えていただけると助かります。