AWS Lambda の実行環境に無いライブラリを利用する場合には、ローカル環境でライブラリのソース群をダウンロードしておいてそれをデプロイパッケージに含めて Lambda にアップロードする必要があります。
Python の例だとデプロイパッケージを作る際に例えば下記のような手順を踏みます。
$ virtualenv -p python2.7 venv
$ source venv/bin/activate
$ pip install -r requirements.txt
$ zip deploy_package.zip lambda_function.py # 実行スクリプトを zip にする
$ cd venv/lib/python2.7/site-packages
$ zip -r ../../../../deploy_package.zip * # pip でインストールしたライブラリを zip に追加
利用したいライブラリが純粋な Python コードであればこのパッケージは問題無く Lambda 上でも動作するのですが、OS の機能に依存しているようなライブラリの場合、ローカル環境でビルド・インストールされたものを Lambda 上にアップロードしても動きません。
上記の例で言うと pip install
は Lambda の実行環境と同じ環境で行う必要があります。
失敗例
pycrypto
というライブラリを使う Lambda Function を作って実行します。
pycrypto==2.6.1
from Crypto.PublicKey import RSA
def lambda_handler(event, context):
return {"messagge": "success"}
上記のファイルを用意してローカルの OS X 環境でデプロイパッケージを作成します。
$ virtualenv -p python2.7 venv
$ source venv/bin/activate
$ pip install -r requirements.txt
$ zip deploy_package.zip lambda_function.py
$ cd venv/lib/python2.7/site-packages
$ zip -r ../../../../deploy_package.zip *
作成した deploy_package.zip
を AWS Lambda にアップロードして実行すると次のようなエラーが出ます。
Unable to import module 'lambda_function'
エラーログ全文を見ると Unable to import module 'lambda_function': /var/task/Crypto/Util/_counter.so: invalid ELF header
とあります。pycrypto
が参照するファイルのヘッダ情報が不正であるようです。原因は pycrypto
をインストールした環境が Lambda の実行環境と異なるところにあります。
対策
Amazon Linux の Docker イメージを用いてライブラリのインストールを行うことでこの問題を回避することができます。
先述のファイルと同じ階層にこのような Dockerfile を用意して
FROM amazonlinux:latest
RUN yum -y update && yum -y install gcc python27-devel
RUN cd /tmp && \
curl https://bootstrap.pypa.io/get-pip.py | python2.7 - && \
pip install virtualenv
WORKDIR /app
CMD virtualenv -p python2.7 venv-for-deployment && \
source venv-for-deployment/bin/activate && \
pip install -r requirements.txt
このようにコマンドを実行すると venv-for-deployment
という名前で、Amazon Linux でビルドされた Python ライブラリコードが作成されます。
$ docker build . -t packager
$ docker run --rm -it -v $(PWD):/app packager
その後は下記のようにデプロイパッケージの zip を作成して AWS Lambda にアップロードします。
$ zip deploy_package.zip lambda_function.py
$ cd venv-for-deployment/lib/python2.7/site-packages
$ zip -r ../../../../deploy_package.zip * .* # dotfile が含まれる場合は ".*" も
実行するとライブラリが import 出来て無事 "success"
が表示されます。
自動化
ちょっと叩くコマンド量が多いのでこのような Makefile を作っておくと make
だけで zip が生成されて便利です。
package:
docker build . -t packager
docker run --rm -it -v $(PWD):/app packager
zip deploy_package.zip lambda_function.py
cd venv-for-deployment/lib/python2.7/site-packages && zip -r ../../../../deploy_package.zip * .*
echo "Completed. Please upload deploy_package.zip to AWS Lambda"
サンプル
今回用いた Lambda Function のサンプルはこちらのリポジトリに置いています。
https://github.com/morishin/python-lambda-function-test
所感
AWS Lambda 便利なのですがちょっと凝ったことをしようとすると泣きそうになりますね。