概要
serverless frameworkを使ってDockerイメージをAWS Lambdaにデプロイしたところ、いくつものエラーに遭遇したので、その対応をしたお話。
今回のディレクトリ構造
root_directory/
├ Dockerfile
├ src/
│ └ handlers/
│ └ filename.py
└ serverless.yml
エラーたち
1. Error: Either "handler" or "image" property (not both) needs to be set on function "FuncName".
1-1. エラー
handlerかimageのどっちかにしろと怒られております: (´ºωº`):
$ sls deploy
Deploying func-name to stage stg (ap-northeast-1)ap-northeast-1.amazonaws.com/serverless-func-name-dev:tagname
✖ Stack func-name-dev failed to deploy (0s)
Environment: linux, node 14.18.3, framework 3.25.1, plugin 6.2.2, SDK 4.3.2
Docs: docs.serverless.com
Support: forum.serverless.com
Bugs: github.com/serverless/serverless/issues
Error:
Either "handler" or "image" property (not both) needs to be set on function "FuncName".
1-2. 原因と対策
Dockerイメージをデプロイして実行したいのであれば、以下のようにhandler
を消しましょう。
# 修正前: `image:` と `handler:` が両方ある
functions:
FuncName:
image:
name: python_image
command: ["src.handlers.filename.lambda_handler"]
handler: src.handlers.filename.lambda_handler
# 修正後
functions:
FuncName:
image:
name: python_image
command: ["src.handlers.filename.lambda_handler"]
# image: を使うなら handler は消そう
# handler: src.handlers.filename.lambda_handler
2. virtualenv: error: argument dest: the destination . is not write-able at /home
2-1. エラー
virtualenv: error: argument dest: the destination . is not write-able at /home
不適切なDockerイメージをAWS Lambdaにデプロイしていると発生することがある。
自分の場合、FROM python:3.7.12-slim
とかを使っていたのだけど、その際にこのエラーに遭遇してしまった。
2-2. 対策
docker imageはこれを使う。
FROM public.ecr.aws/lambda/python:3.8
AWS Lambda専用Dockerイメージみたいなものだろう。
AWS公式ドキュメントでもこのイメージが使用されているよ(*´v`)
2-3. 補足
ちなみに、public.ecr.aws/lambda/python
を使うと、packageのインストール方法が大きく変わるので注意。
debian系でよく使われるapt
コマンドではなく、redhat系でよく使われるyum
コマンドを使ってpackageをインストールしなければならない。
だいたい以下のようになる。
# Ubuntu系
# FROM python:3.7.12-slim
# RUN apt -y update \
# && apt install -y --no-install-recommends \
# apt-utils \
# python-dev \
# build-essential
# Redhat系
FROM public.ecr.aws/lambda/python:3.8
RUN yum update -y && \
yum install -y \
python-devel \
build-essential
3. entrypoint requires the handler name to be the first argument
-> 対応済
3-1. エラー
serverlessによるデプロイは成功したのだけど、AWS Lambdaを実行したら以下のようなエラーが発生した。
entrypoint requires the handler name to be the first argument
entrypoint requires the handler name to be the first argument
START RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxwwwwww Version: $LATEST
RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxwwwwww Error: Runtime exited with error: exit status 142
Runtime.ExitError
END RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxwwwwww
REPORT RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxwwwwww Duration: 19.26 ms Billed Duration: 20 ms Memory Size: 512 MB Max Memory Used: 3 MB
3-2. 原因
serverless.ymlのfunctions: -> funcname: -> image: -> command:
のところが間違っていた。
3-3. 対策
ここには、AWS Lambdaに実行させたいファイルへのパス、そして関数名を記載する。
これでエラーが解消する。
以下のような感じ。
# 修正前
functions:
FuncName:
description: 説明
image:
name: docker_image_tag
# これはよくない
command: ["poetry", "run", "python", "-m", "src.handlers.filename.lambda_handler"]
name: lambda-func-name
# 修正後
functions:
FuncName:
description: 説明
image:
name: docker_image_tag
# ↓これでOK: {実行したいpyファイルまでのパス}.{実行する関数名}
command: ["src.handlers.filename.lambda_handler"]
name: lambda-func-name
4. Unable to import module 'src.handlers.filename': No module named 'src'
4-1. エラー
こちらも、「3.」と同様で、serverlessによるデプロイが成功した後の話。
AWS Lambdaを実行したら以下のようなエラーが発生した。
[ERROR] Runtime.ImportModuleError: Unable to import module 'src.handlers.filename': No module named 'src'
Traceback (most recent call last):
4-2. 原因
Dockerfileに WORKDIR
を指定しない方がいいみたい。
厳密には、WORKDIR
を使っても対応可能なのではないかと予想しているのだけど、対処方法見つからず。
4-3. 対策
とりあえずWORKDIR
を消そう( *˙︶˙*)و
それじゃイヤだという場合は、
-
serverless frameworkのcommand:
を使ってる人の場合
serverless.yml
のfunctions: -> funcname: -> image: -> command:
に指定するパスを、必ずrootディレクトリから辿れるようにすればよさそう。 -
serverless frameworkのcommand:
は使ってない場合
DockerfileのCMD
に指定するパスを、上記と同様にrootディレクトリから辿れるパスにすればよさそう。
たとえば、以下のようなディレクトリ構造で、
root_directory/
├ Dockerfile
├ src/
│ └ handlers/
│ └ filename.py
└ serverless.yml
かつ、docker container内のroot_directory/
ディレクトリでpwd
を実行したときに、home/username/root_directory/
といった結果が返ってきたのであれば、CMD
やcommand:
に対して、以下のように書けばよさそう。試してないけど!
["home.username.root_directory.src.handlers.filename.lambda_handler"]
↑上記の方法試したけどうまくいかなかった。
面倒だから、WORKDIR
消した方が早いす~(´^ω^`)
** 2022/12/19追記 **
後になって以下のAWSのドキュメントでも確認してみたのだけど、
デフォルトではWORKDIR
が以下のようになる模様。
WORKDIR /var/task
確かに、WORKDIR
を一切設定せずにdockerイメージ(public.ecr.aws/lambda/python:3.8)をbuildしてdocker execで入ってみたところ、自動的にWORDKDIR
が/var/task
になってた。
# pwd
/var/task
この辺をうまく扱えれば解決するのかもしれない。
また、公式ドキュメントには以下のような記述もあり、
この記事の「代替ベースイメージからのイメージの作成」という項目の内容によると、WORKDIR
を変更することもできそう。
今回はその点で頑張る必要はないので、調査はここで終了。
4-4. 参考にした記事