はじめに
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
...