LoginSignup
3
0

More than 1 year has passed since last update.

[AWS Lambda / Docker / Python3] AWS Lambdaでdockerイメージを動かそうとして発生したエラーたちの対応

Last updated at Posted at 2022-12-16

概要

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を消しましょう。

serverless.yml
# 修正前: `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. エラー

AWSコンソール画面のログで確認できるエラー
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はこれを使う。

Dockerfile
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をインストールしなければならない。

だいたい以下のようになる。

Dockerfile
# 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を実行したら以下のようなエラーが発生した。

AWSコンソール画面のログで確認できるエラー
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に実行させたいファイルへのパス、そして関数名を記載する。
これでエラーが解消する。

以下のような感じ。

serverless.yml
# 修正前
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.ymlfunctions: -> 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/といった結果が返ってきたのであれば、CMDcommand:に対して、以下のように書けばよさそう。試してないけど!

["home.username.root_directory.src.handlers.filename.lambda_handler"]

↑上記の方法試したけどうまくいかなかった。
面倒だから、WORKDIR消した方が早いす~(´^ω^`)

** 2022/12/19追記 **

後になって以下のAWSのドキュメントでも確認してみたのだけど、

デフォルトではWORKDIRが以下のようになる模様。

Dockerfile
WORKDIR /var/task

確かに、WORKDIRを一切設定せずにdockerイメージ(public.ecr.aws/lambda/python:3.8)をbuildしてdocker execで入ってみたところ、自動的にWORDKDIR/var/taskになってた。

dockerコンテナにexecした直後にpwdコマンドを打ってみた
# pwd
/var/task

この辺をうまく扱えれば解決するのかもしれない。

また、公式ドキュメントには以下のような記述もあり、

この記事の「代替ベースイメージからのイメージの作成」という項目の内容によると、WORKDIRを変更することもできそう。

今回はその点で頑張る必要はないので、調査はここで終了。

4-4. 参考にした記事

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0