はじめに
Pythonで書かれたLambda関数のコードをServerless Frameworksを使ってデブロイする際に、serverless-python-requirements プラグインを使うことで依存関係を含めることができる。
Dockerコンテナを使ったパッケージングにも対応しており、cryptography
のようにコンパイルが必要なライブラリが含まれる際はそれを使うのだが、設定によってはLambda実行でエラーが発生することがある。
環境
- Python 3.9
- Serverless Framework 3.19.0
事象
cryptography
を含むPythonアプリをServerless Frameworkを使ってDockerコンテナでパッケージ & デプロイ。
その後Lambdaを実行すると、Importで以下のようなエラーが発生した。
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_handler': /var/task/cryptography/hazmat/bindings/_rust.abi3.so: cannot open shared object file: No such file or directory
原因
上記はビルド環境と実行環境のアーキテクチャの違いによって発生したエラー。
元々これを防ぐためにDockerコンテナを使ってパッケージしたのだが、指定したコンテナイメージが実際のLambdaの実行環境と異なっていたことが原因。
まず、現在Lambdaは x86_64
と arm64
の2つのアーキテクチャが存在する。
Serverless Frameworkでは、serverless.ymlの architecture
パラメータでどちらのアーキテクチャを使うのかを指定でき、未指定は x86_64
。
私の環境では architecture
パラメータは未指定であり、そのためアーキテクチャ x86_64
となっていた。
また、パッケージ用のコンテナイメージには、AWS SAMの公式イメージである public.ecr.aws/sam/build-python3.9:latest を指定していた。
custom:
pythonRequirements:
dockerImage: public.ecr.aws/sam/build-python3.9:latest
dockerizePip: true
しかし、このイメージの latest
タグは、arm64
ベースのイメージとなっており、それによってビルド環境と実行環境で差異が生じ、先のエラーが発生した。
【補足】
コンテナイメージ未指定の場合は lambci/lambda がパッケージングに使われるのだが、現在Python 3.9のイメージはなく、プロジェクトも更新が滞っている。
現在、AWS公式のコンテナイメージ (本来はSAM用) が存在するため、そちらを使うようにイメージを指定していた。
解決策
パッケージに使うコンテナイメージとarchitecture
パラメータに指定するアーキテクチャを x86_64
or arm64
のどちらかに統一する。
AWS SAMの公式イメージには x86_64
と arm64
の両方が用意されており、タグで指定できる。
- {version}-arm64
- {version}-x86_64
アーキテクチャ名が含まれないタグは arm64
ベースのイメージである。
今回は arm64
に統一し、serverless.ymlは以下のように修正した、
...
custom:
pythonRequirements:
- dockerImage: public.ecr.aws/sam/build-python3.9:latest
+ dockerImage: public.ecr.aws/sam/build-python3.9:latest-arm64
dockerizePip: true
...
provider:
name: aws
runtime: python3.9
+ architecture: arm64
...