TL;DR
- Lambda Layerを使ってLambdaのデプロイを高速化します。
- ServerlessとServerless Pluginを使うことで簡単に実現することができます。
- 今回試した一例ですが、252.47秒->45.49秒で5倍高速化しました。
はじめに
Serverlessを用いたLambda Layerの活用については以前、Lambda LayerでHeadless Chromeを使う方法についてまとめたときに書いたのですが、Serverless Pluginなどを使うことで、より簡単にできることがわかったのでここにまとめたいと思います。
環境
- Mac OS X(10.14.3)
- AWS Lambda(Runtime: Python3.7)
- Serverless Framework: 1.40.0
- serverless-python-requirements: 4.3.0
Pythonライブラリのパッケージングにはserverless-python-requirementsというServerless Pluginを用います。このプラグインもとても便利なのでもし使っていない方がいたらチェックしてみてください。
課題: Lambdaでライブラリを使うとデプロイが鈍速に…
LambdaでPythonライブラリを使うためには、functionのコードとパッケージをまとめてzip化してデプロイする必要があります。 容量が小さいライブラリを使っている場合は特に問題ないのですが、統計や機械学習で良く使うscipy、pandasなどのライブラリを使うと容量が50MB以上になり、 デプロイに恐ろしく時間がかかる ようになってしまいます。
ちなみにどのくらいの容量なのか実験してみます。
計測は参考程度ということで time
コマンドで1度だけ計測しています。
初期状態の場合
まず sls create -t aws-python3
した状態のままデプロイしてみます。
初回はCloudFromationの作成など発生するので2回目で計測しています。
Uploading service live-scheduler-lambda.zip file to S3 (10.28 KB)... 45.49 real 3.50 user 1.41 sysc
10.28KB/45.49秒でした。
重いライブラリがある場合
Pythonライブラリの中でも容量が大きい、scipy, pandas, matplotlibを追加してデプロイしてみます。
scipy, pandasはC拡張されているライブラリなので、次のように dockerizePip: true
にすることでDocker上でsetupを行っています。
serverless.yml
custom: pythonRequirements: usePipenv: true dockerizePip: true
Uploading service live-scheduler-lambda.zip file to S3 (74.33 MB)... 252.47 real 70.49 user 4.38 sys
74.33MB/252.47秒と1回のデプロイに4分以上かかりました。 これはかなりストレスフルですね…。
Lambda Layerを使って解決
この問題を昨年発表されたLambda Layerを使って解決します!そもそものLambda Layerの仕組みについてはクラスメソッドさんのブログを見ていただくのが良いかもしれません。
ディレクトリ構成
まずディレクトリ構成ですが、次のようになります。
lambda
がfunctionを含むディレクトリで layer_requirements
がLambda Layer用のディレクトリです。インストールしたいライブラリが記載されているPipfileファイルを用意してください。(ここはrequirements.txtでも大丈夫です。)
├── lambda │ ├── handler.py │ └── serverless.yml └── layer_requirements ├── Pipfile └── serverless.yml
Layer側
まずLayer側の設定を見てみましょう。
layer_requirements/serverless.yml
service: python-requirements-layer plugins: - serverless-python-requirements custom: pythonRequirements: usePipenv: true # Pipfileを使いたくない場合はここを削除 dockerizePip: true layer: true # ここをtrueに provider: name: aws runtime: python3.7 region: ap-northeast-1 resources: Outputs: PythonRequirementsLambdaLayerExport: # function側から使いたい名前にする Value: Ref: PythonRequirementsLambdaLayer # これはpythonRequirementsで設定されるので固定値
1つ目のポイントは custom/pythonRequirements
で layer
をtrueにすることです。
もう1つのポイントは resources/Outputs
でfunction側からlayerを使えるようにする設定です。
ちょっと紛らわしいのですが、Outputsの名前をserverlessから呼び出されるので(後述します)、ここを呼び出したい名前にします。 Value/Ref
で参照するのはpythonRequirementsで設定される固定の PythonRequirementsLambdaLayer
になります。
デプロイは普通に行います。
sls deploy
時間はさっきと同じくらいかかるのですが、こちらはライブラリを更新しない限りデプロイ不要なので、デプロイの度に、という問題は発生しません。
AWS Consoleから確認するとLayerが追加されていることがわかります。
function側
それではfunction側の設定を見てみましょう。
service: sample-lambda custom: default_stage: dev stage: ${opt:stage, self:custom.default_stage} requirements_service: python-requirements-layer # layer側で設定したservice requirements_export: PythonRequirementsLambdaLayerExport # layer側で設定したOutputsの名前 requirements_layer: ${cf:${self:custom.requirements_service}-${self:custom.stage}.${self:custom.requirements_export}} # python-requirements-layer-dev.PythonRequirementsLambdaLayerExport を呼び出します provider: name: aws runtime: python3.7 region: ap-northeast-1 functions: hello: handler: handler.hello layers: - ${self:custom.requirements_layer} # ここを設定
最終的には functions/${function_name}/layers
に先程作成したLayerを指定すればよいのですが、やや煩雑になるので custom
内で上手く設定しています。 ${cf:~~~}
はCloudFormationのOutputを呼び出すServerlessの関数で詳しくはドキュメントを参照してください。
こちらもデプロイはそのまま行います。
sls deploy
Uploading service live-scheduler-lambda.zip file to S3 (10.19 KB)... 39.42 real 3.14 user 0.59 sys
10.19KB/39.42秒で、ライブラリがない状態と同じになりました! AWS Consoleから確認するとfunctionに対してLayerが追加されていることがわかります。
テスト
念の為、ちゃんとライブラリが読み込めているか確認してみます。
import json import pandas, scipy, matplotlib def hello(event, context): body = { "pandas.__version__": pandas.__version__, "matplotlib.__version__": matplotlib.__version__, "scipy.__version__": scipy.__version__ } return { "statusCode": 200, "body": json.dumps(body) }
関数を呼び出してみましょう。
sls invoke -f hello # { "statusCode": 200, "body": { "pandas.__version__": "0.24.2", "matplotlib.__version__": "3.0.3", "scipy.__version__": "1.2.1" } }
正しく読み込めていますね。
まとめ
- Lambda Layerを使ってServerlessのデプロイを高速化する方法をまとめました。
- デプロイを高速化することで開発のイテレーションを加速させましょう💪