概要
タイトルの通り、Layerを用いて一部ライブラリを共通化しているAWS Lambda functionを、serverlessでローカル実行しようとした時につまずいたので備忘録として残しておく。
経緯
最近、趣味でAWS Lambda上で動かすpythonプログラムを作成していて、デプロイが楽になるとのことなのでserverlessを導入していた。
その際、functionを複数作成しており、一部ライブラリはLayerを用いて共通化していた。
作成したプログラムの構成は以下。
プロジェクトルートディレクトリ
├─function1
│ └─src
│ └─handler.py
├─function2
│ └─src
│ └─handler.py
├─layer
│ ├─layer1
│ │ └─layer1のpythonライブラリ・ソース群
│ └─layer2
│ └─layer2のpythonライブラリ・ソース群
└─serverless.yml
また、serverless.ymlの記述内容は以下。
(本記事に関係ない細かな設定については省略している)
layers:
layer1:
path: layer/layer1
name: ${self:service}-layer1
compatibleRuntimes:
- python3.7
allowedAccounts:
- "*"
layer2:
path: layer/layer2
name: ${self:service}-layer2
compatibleRuntimes:
- python3.7
allowedAccounts:
- "*"
functions:
function1:
handler: function1/src/handler.lambda_handler
layers:
- { Ref: Layer1LambdaLayer }
- { Ref: Layer2LambdaLayer }
function2:
handler: function2/src/handler.lambda_handler
layers:
- { Ref: Layer1LambdaLayer }
- { Ref: Layer2LambdaLayer }
この設定でsls deploy
コマンドを実行すると、AWSへのデプロイは正常終了し、Lambda上でのpythonプログラムの起動もできるのだが、
何故か sls invoke local --function {function名}
コマンドによるローカル実行は下記エラーが発生してうまくいっていなかった。
ModuleNotFoundError: No module named '{layerに配置したライブラリ名}'
どうやら、ローカル実行するとlayerに配置したライブラリが参照できていないらしい。
AWSにデプロイするとちゃんと動くのに。解せぬ。
解決策
とにかくserverlessでローカル実行するとlayerをうまく参照できていないことがわかったので、コマンド実行時にオプションなどで設定を追加できないか公式ドキュメントを調べてみた。
…うーん、それっぽいオプションは見当たらない。
というかデータ入力の方法についての記述が大半でLayerについての説明なんかは皆無なんだなこれが。
とりあえずオプションを一通り試してみるかー、と半ばやけくそになりかけた時、気になる記述を見つけた。
--docker Enable docker support for NodeJS/Python/Ruby/Java. Enabled by default for other runtimes.
訳)--dockerオプションは、NodeJS / Python / Ruby / JavaのDockerサポートを有効にします。他のランタイムではデフォルトで有効になっています。
へー。pythonだとDockerはデフォルトで無効化されてるんだー。へー。
…ひょっとしてこれじゃね?
serverlessではなくAWS CLIを使ってLambda functionをローカルで実行するときは、ローカルのDocker上にLambdaの実行環境が作成されそこでfunctionが実行されることは知っていたのでそこから連想できた。
とにもかくにも、--dockerオプションを追加してコマンドをたたいてみる。
>sls invoke local --function function1 --docker
Serverless: Running "serverless" installed locally (in service node_modules)
(略)
{"statusCode":200,"body":"{\"message\": \"finished . !\"}"}
動いた!!!
どうやらlayerが参照できなかったのは、docker上で動かしてなかったからだった模様。
作成したfunctionを正しく動かすためにはほぼ本番に近いdocker上の環境で動かす必要があるのに、その設定はデフォルトで無効になっているという…。
いやまあ毎回docker上で動かしていると実行が遅くなったりするし、Lambda functionはLayer使わないものが大半だったりするとデフォルト無効の意義も理解はできるんだが…。
ちょいと公式ドキュメントが分かりづらすぎやしませんかね?
結論
Layerでライブラリを共通化したLambda functionがserverlessでローカル実行できない、といった事象に遭遇した方はコマンドに--dockerオプションを付け加えてみてはいかがだろうか。