外部パッケージの Pillow と独自モジュール(.py ファイル)を Lambda Layer に配置する(後編)
概要
記事一覧はこちらです。
外部パッケージの Pillow と独自モジュール(.py ファイル)を Lambda Layer に配置する(前編) からの続きです。
参照したサイト・書籍
PyCharm and PYTHONPATH
https://stackoverflow.com/questions/28326362/pycharm-and-pythonpathInstall a Python package into a different directory using pip?
https://stackoverflow.com/questions/2915471/install-a-python-package-into-a-different-directory-using-pipAWS: Delete lambda layer still retains layer version history
https://stackoverflow.com/questions/60824745/aws-delete-lambda-layer-still-retains-layer-version-history
目次
- IntelliJ IDEA 上でユニットテストが成功するように設定する
- コマンドプロンプトから
python -m unittest -v
を実行してユニットテストが成功するように設定する - Docker コンテナから
python -m unittest -v
を実行してユニットテストが成功するように設定する - IntelliJ IDEA からユニットテストを実行する時に Docker コンテナで実行する(docker-compose 版)
- deploy する
- 画像をアップロードしてサムネイル画像を生成してみる
- 1度 Lambda を実行してから Lambda Layer 側の内容を変更して再度実行すると変更は反映されるのか?
- 最後に
手順
IntelliJ IDEA 上でユニットテストが成功するように設定する
今の状態だとモジュール検索のパスに my_module_layer/python が追加されていないので resize_service/handler.py に追加した from image_lib import resize_utils
に赤波線が表示されます。
IntelliJ IDEA でどうやって環境変数 PYTHONPATH を設定すればよいのか調べてみたのですが、my_module_layer/python でコンテキストメニューを表示してから「Mark Directory as」-「Sources Root」を選択して、
Source Folder(青色のフォルダーアイコンになります)にすれば、
resize_service/handler.py の from image_lib import resize_utils
から赤波線が消えました。
これで tests/test_resize.py で「Run 'Unittests for test_r...'」を選択してテストを実行すると、
テストが成功しました。breakpoint を設定して debug 実行することも出来ました。
コマンドプロンプトから python -m unittest -v
を実行してユニットテストが成功するように設定する
venv/Scripts/activate.bat、venv/Scripts/deactivate.bat 内で環境変数 PYTHONPATH に my_module_layer/python の絶対パス(自分の環境では D:\project-serverless\ksbysample-serverless\python-lambda-layer-project\my_module_layer\python
)をセットします。
venv/Scripts/activate.bat の最後に以下の記述を追加します。
if defined PYTHONPATH ( set _OLD_PYTHONPATH=%PYTHONPATH% ) set PYTHONPATH=%PYTHONPATH%;D:\project-serverless\ksbysample-serverless\python-lambda-layer-project\my_module_layer\python
venv/Scripts/deactivate.bat の最後に以下の記述を追加します。
set PYTHONPATH= if defined _OLD_PYTHONPATH ( set PYTHONPATH=%_OLD_PYTHONPATH% ) set _OLD_PYTHONPATH=
これでコマンドプロンプトから python -m unittest -v
を実行してテストが成功するようになります。
ただし IntelliJ IDEA の Terminal からは成功しませんでした。(venv)
の文字が表示されていますが、venv/Scripts/activate.bat で activate している訳ではないようです。
Settings ダイアログの「Tools」-「Terminal」の「Environment Variables」に PYTHONPATH=D:\project-serverless\ksbysample-serverless\python-lambda-layer-project\my_module_layer\python
をセットすれば成功しましたが、あちこち設定するのは好みではないので Terminlal には設定せず pip install 専用にしようと思います。
Docker コンテナから python -m unittest -v
を実行してユニットテストが成功するように設定する
今回は volume で指定したいディレクトリが2ヶ所あるので docker-compose で実行します。
まずはプロジェクトのルートディレクトリ直下に Dockerfile を新規作成し、以下の内容を記述します。
FROM lambci/lambda:build-python3.8 COPY shared_package_layer/requirements.txt /tmp/requirements.txt RUN pip install --upgrade pip RUN pip install -r /tmp/requirements.txt RUN pip install moto ENV PYTHONPATH "${PYTHONPATH}:/opt/python"
次にプロジェクトのルートディレクトリ直下に docker-compose.yml を新規作成し、以下の内容を記述します。
# docker-compose build # docker-compose run --rm python-unittest version: '3' services: python-unittest: build: context: . image: lambci/lambda:build-python3.8-python-lambda-layer-project container_name: python-unittest volumes: - .:/var/task - ./my_module_layer/python:/opt/python command: python -m unittest -v
コマンドプロンプトを起動して docker-compose build
を実行し Docker Image を作成します。
最後に docker-compose run --rm python-unittest
を実行すると Docker コンテナで python -m unittest -v
が実行されてテストが成功します。
IntelliJ IDEA からユニットテストを実行する時に Docker コンテナで実行する(docker-compose 版)
IntelliJ IDEA のメインメニューから「File」-「Project Structure...」を選択して「Project Structure」ダイアログを表示します。
画面左側で「SDKs」を選択した後、中央で「+」-「Add Python SDK...」を選択します。
「Add Python Interpreter」ダイアログが表示されます。画面左側で「Docker Compose」を選択した後、右側の設定を以下の画像のようにしてから「OK」ボタンをクリックします。
「Name」に長い名前が設定されるので Remote Python 3.8.3 Docker Compose (python-lambda-layer-project)
に変更して「OK」ボタンをクリックしダイアログを閉じます。
IntelliJ IDEA のメインメニューから「Run」-「Edit Configurations...」を選択して「Run/Debug Configurations」ダイアログを表示します。
画面左側で「Templates」-「Python tests」-「Unittests」を選択し、以下の内容を設定して「OK」ボタンをクリックします。
- 「Python interpreter」で「Use specified interpreter」を選択した後「Remote Python 3.8.3 Docker Compose (python-lambda-layer-project)」を選択します。
- 「Working directory」に
D:\project-serverless\ksbysample-serverless\python-lambda-layer-project
を入力します。
設定は以上で完了です。エディタから「Debug 'Unittests for test_r...'」を選択して1回 debug 実行します。
この時テストは成功しますが、まだ docker-compose で実行されていません。
再度「Run/Debug Configurations」ダイアログを表示すると左側に「Python tests」-「Unittests for test_resize.TestResizeService.test_resize」が追加されているので、「Python interpreter」を「Use SDK of module」→「Use specified interpreter」に変更します。
再度エディタから「Debug 'Unittests for test_r...'」を選択して debug 実行します。
Console に以下のように表示されて、
「Step Into」「Step Over」で進めると my_module_layer/python/image_lib/resize_utils.py の resize_image 関数まで進めることができます。
Services Tool Window を見ると python-unittest コンテナが実行されており、
Debug Tool Window で「Resume Program」ボタンを押してテストを最後まで実行すると成功します。
Services Tool Window を見ると pycharm_helptest_IU コンテナが残っているのは docker コマンドで実行した時と同じですが、python-unittest コンテナまで残っています。。。
「Run/Debug Configurations」ダイアログから docker-compose 実行時のオプションを確認すると --rm
オプションがありません。「Command and options」で設定しようとしましたが、何かエラーが出て設定できませんでした。どうやったら設定できるのだろう?
手動で残っているコンテナを削除することにします。自動で削除できないなら docker-compose ではなく docker コマンドで実行する方式に変更した方が良さそうです。
deploy する
resize_service を delploy します。
マネジメントコンソールで deploy された resize-service-dev-resize を見ると shared-package-layer と my-module-layer の2つの Lambda Layer が使用されていることが分かります。
画像をアップロードしてサムネイル画像を生成してみる
ksbysample-upload-bucket に sample2.jpg をアップロードすると、
ksbysample-resize-bucket に sample2_thumb.jpg が生成されています。
npx sls logs -f resize
を実行してログを見ても特にエラーは出ていませんでした。
1度 Lambda を実行してから Lambda Layer 側のみ内容を変更して再度実行すると変更は反映されるのか?
my_module_layer/python/image_lib/resize_utils.py で thumbnail_size の値をログに出力するよう変更してから、
import logging from PIL import Image logger = logging.getLogger() logger.setLevel(logging.INFO) thumbnail_size = 320, 180 def resize_image(image_path, resized_path): logger.info(thumbnail_size) with Image.open(image_path) as image: image.thumbnail(thumbnail_size) image.save(resized_path)
thumbnail_size = 320, 180
→ thumbnail_size = 160, 90
に変更して my_module_layer のみ deploy したら変更が反映されるのか確認します。
まずは念の為 resize_service → my_module_layer → shared_package_layer の順に remove して、逆の順番で deploy し直します。thumbnail_size = 320, 180
の状態です。
sample.jpg をアップロードしてログを確認すると (320, 180)
と出力されており、初回なので Init Duration
も出力されています。
thumbnail_size = 160, 90
に変更して my_module_layer のみ deploy してから sample.jpg をアップロードすると、
ログに出力される値は (320, 180)
のまま変わらず、既にインスタンスが生成されて再利用されているので Init Duration
は出力されません。
生成済みのインスタンスが破棄されたら変更された Lambda Layer の内容が反映されるのか確認したいので、20分程度何もせずに放置します。
約20分後に sample.jpg をアップロードすると、
Init Duration
が出ているにもかかわらず (320, 180)
のままでした。。。
原因は、マネジメントコンソールで my-module-layer のバージョンを見ると現在 12 なのですが、
resize-service-dev-resize 関数が参照している my-module-layer のバージョンは 11 で、最新の 12 に更新されていないためのようです。latest のような指定が出来ないのかも見てみましたが、設定できるところが見当たりませんでした。
ちなみに resize_service を何も変更せずに delploy すると、関数が参照している my-module-layer のバージョンは 12 に更新されて、
sample.jpg をアップロードすると今度は (160, 90)
が出力されました。
Lambda Layer を deploy した場合には、それを利用する関数側も deploy しないと反映されない、という結果でした。latest で指定できるようにならないかな。。。
最後に
まとめてみると、
- Python + Serverless Framework で Lambda Layer を使うなら serverless-python-requirements プラグインの「Lambda Layer」の説明を見ること。Serverless Framework のドキュメントの AWS - Layers ではない。
- Lambda Layer を deploy したら、それを利用する関数も deploy し直すこと。そうしないと更新された Layer の最新バージョンを参照してくれない。
- Lambda Layer は Python のサイズの大きなパッケージを配置する程度に留めた方がよいのかもしれない(deploy 時間短縮のため)。
履歴
2020/06/20
初版発行。