Python
AWS
Docker
lambda

【AWS Lambda】Amazon Linux の Docker イメージを使ってデプロイパッケージを作成する

More than 1 year has passed since last update.

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 を作って実行します。


requirements.txt

pycrypto==2.6.1



lambda_function.py

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'

エラーログ全文を見ると Unable to import module 'lambda_function': /var/task/Crypto/Util/_counter.so: invalid ELF header とあります。pycrypto が参照するファイルのヘッダ情報が不正であるようです。原因は pycrypto をインストールした環境が Lambda の実行環境と異なるところにあります。


対策

Amazon Linux の Docker イメージを用いてライブラリのインストールを行うことでこの問題を回避することができます。

先述のファイルと同じ階層にこのような Dockerfile を用意して


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" が表示されます。

success


自動化

ちょっと叩くコマンド量が多いのでこのような Makefile を作っておくと make だけで zip が生成されて便利です。


Makefile

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 便利なのですがちょっと凝ったことをしようとすると泣きそうになりますね。