AWS Lambdaを使ってなにかを作るとき(Python)についての考察です。
■ Overview
- AWS LambdaとPythonを使って開発するときにローカル環境をどう作っていくかについて。
- AWS Lambdaをローカルで再現する手段
■ 問題点
- AWS LambdaとPythonで開発する際に、モジュールなどのインポートする際に依存関係があるとLambdaのGUI上ではそのまま実行できないため、一度他の場所で依存関係をインストールしてzip化してアップロードするケースが多い。
ただ、その都度変更があるためにzip化してアップロードするのは大変。すぐに反映を確認できないと開発効率がさがってしまう。
■ 解決策
主に2つあります。
① EC2 or AmazonLinux for Dockerを使い開発する
Lambdaは、AmazonLinuxで動いているためローカルPCでローカル開発環境を準備して開発し、それをzip化アップロードしても失敗するケースがあります。
なので、他に環境を用意する必要があります。方法は2つあり
- EC2インスタンスを立ててその中で開発する。
- AmazonLinuxの公式Dockerイメージの中で開発を行う。
前者のEC2を立てるのは、あまりスマートではなくコストもかかるのでおすすめできません。
AmazonLinuxの公式Dockerについては、その中で開発を行え常にクリーンな環境を保てます。
$ docker pull amazonlinux
② docker-lambdaを利用する
個人的には、こちらが一番良いかと思います。
https://github.com/lambci/docker-lambda
docker-lambdaを文字通り、AWS Lambdaの環境を再現したDockerイメージです。
これを使い、Dockerfileをビルドしてアップロードすればかなりよい環境ができあがります。AmazonLinuxのDockerイメージでもよいのですが、こちらのdocker-lambdaのほうがはじめからLambdaを再現できているので余計なことをせずに簡単です。
■ docker-lambdaを触ってみる
簡単ではありますが、サンプルをbuildするまで行ってみます。
まずは、DockerfileをビルドするのではなくCLIからイメージをrunして動作確認してみます。
公式のlambci/docker-lambdaリポジトリを参考にしていきます。
####lambda_function.pyを用意する。
AWS Lambdaではデフォールトでlambda_function.pyのlambda_handlerを呼び出すようになっているので今回も従っていきます・
import json
def lambda_handler(event, context):
return event
####docker-lambdaを実行していきます。
$ docker run -v "$PWD":/var/task lambci/lambda:python3.6 lambda_function.lambda_handler '{"Hello":"World"}'
Unable to find image 'lambci/lambda:python3.6' locally
python3.6: Pulling from lambci/lambda
5be106c3813f: Pull complete
e240967675e1: Pull complete
4ec29390cd9b: Pull complete
fc6ba7ddc407: Pull complete
492c7fb9ab78: Pull complete
Digest: sha256:6a5cddcf16942a7a3ddc209f140a8d4105a87ce95cd374ffc6d953dd0b593d01
Status: Downloaded newer image for lambci/lambda:python3.6
START RequestId: 3f39da27-a241-4fd2-ad77-e92ec63ffada Version: $LATEST
END RequestId: 3f39da27-a241-4fd2-ad77-e92ec63ffada
REPORT RequestId: 3f39da27-a241-4fd2-ad77-e92ec63ffada Duration: 12 ms Billed Duration: 100 ms Memory Size: 1536 MB Max Memory Used: 19 MB
{"Hello": "World"}
単純にJSONも文字列を渡して出力しているだけですが、無事に『{"Hello": "World"}』がでました。
####ビルドイメージをDockerfileに書いていく。
では、ここでビルド用のDockerfileを書いていきます。
内容としては、上記のlambda_function.pyと依存関係をzip化し、デプロイパッケージを作成していきます。
デプロイパッケージの作成
ビルド準備として、requirements.txt
というテキストを用意しそこに依存するインポート群を書いていきます。
requests
再度、requestsモジュールを使ったlambda_function.py
を用意します。DMM.comにアクセスしてstatus_code : 200を返すスクリプトです。
import requests
def lambda_handler(event, context):
res = requests.get('https://www.dmm.com/')
return res.status_code
では、ビルドしていきます。
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1
ADD . .
CMD pip3 install -r requirements.txt -t /var/task && \
zip -9 deploy_package.zip lambda_function.py && \
zip -r9 deploy_package.zip *
まずは、イメージを作成します。
$ docker build -t mylambda .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mylambda latest 14a0a97cd00a 5 minutes ago 1.8GB
lambci/lambda build-python3.6 0f6150a9635c 7 days ago 1.8GB
ではZipに固めていきます。
$ docker run -v "$PWD":/var/task mylambda
Collecting requests (from -r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/f1/ca/10332a30cb25b627192b4ea272c351bce3ca1091e541245cccbace6051d8/requests-2.20.0-py2.py3-none-any.whl (60kB)
~中略~
結果として、以下のようなディレクト構造ができます。deploy_package.zip
がアップロード対象のフォルダーです。
ls -a
. certifi deploy_package.zip requests urllib3-1.24.dist-info
.. certifi-2018.10.15.dist-info idna requests-2.20.0.dist-info
Dockerfile chardet idna-2.7.dist-info requirements.txt
bin chardet-3.0.4.dist-info lambda_function.py urllib3
結果
Lambda上で、deploy_package.zip
をzipアップロードすると結果として200が返ってきており、正しく動作していることがわかります。
基本的にアップロードをローカルで検証できたらで良くて、普段はコンテナ内や引数でpythonを実行して動作確認するのがよさそうです。
以上です。