のーずいだんぷ

主に自分用メモですが、もしかしたら誰かの役に立つかもしれません

Serverless-FrameworkでLambdaにデプロイしたときの挙動とハマりポイント4点

やろうとしたこと

タイトルの通り、Serverless-Frameworkを使用してAWS Lambdaへデプロイすることを考えていた。 細かい話としては

  • ランタイムにPython
  • サードパーティのモジュールを使用
  • エントリポイントは複数モジュールに分割

一般的であるが、このような状況だった。 以下では更に具体的な状況について記載していく。

ディレクトリ構造

$ tree src/
src/
├── exception.py
├── gcs.py
├── handler.py
└── lib

serverless.yaml

  
service: test_deploy

provider:
  name: aws
  runtime: python3.7
  stage: ${opt:stage,'dev'}
  region: ${opt:region,'ap-northeast-1'}
  profile: ${env:AWS_PROFILE,'default'}

functions:
  testapp:
    role: arn:aws:iam::****************
    handler: src/handler.lambda_handler
    name: test_app
    description: deploy_test
    runtime: python3.7
    memorySize: 128
    timeout: 30
    tracing: False
    environment:
      stage: dev
      test_env: example
    tags:
      Name: test_deploy
    events:
      - s3:
          bucket: test_bucket
          events: s3:ObjectCreated:*
          existing: true
package:
  include:
    - src/**
  exclude:
    - ...

ここでのハマりポイント4点

  1. アップロード後のディレクトリ構造
  2. ハンドラの指定方法
  3. 新機能:既存のs3バケットへのイベント割当
  4. Pythonのサードパーティモジュールが見つからない(unable import to ***)

1. アップロード後のディレクトリ構造

Serverless-Frameworkではserverless.yamlpackageディレクティブでアップロードするファイルをしている。 コンソールからzipでアップロードする場合もおそらく同じだが、アップロード後は/var/task/以下に展開される。 例えば上記の例ではpackageディレクティブで指定しているディレクトリ構造そのままでアップロードされるので、例えばハンドラの場所は /var/task/src/handler.pyとなる。

2. ハンドラの指定方法

Lambdaではエントリポイントとなる関数を指定する必要があり、severless.yamlでもhandlerディレクティブで指定している。 具体的にどう指定するか、については今回のhandler.py内のlambda_handler()をエントリポイントとしたいとすると、src/handler.lambda_handlerのように指定すればよい。 /var/taskはワーキングディレクトリのため記載の必要ない。(おそらく動かなくなる)

3. 新機能:既存のs3バケットへのイベント割当

実は最近?ではないがこれまでなかった機能として既に存在するS3バケットのイベントを割り当てることができない問題があった。 (これまではプラグイン:serverless-plugin-existing-s3を使用していた。) 今回からexistingディレクティブをtrueとすることでそれが可能となる。 ただし、当機能で割り当てられるバケットは1つのみなので注意が必要。 複数割り当てたい人はこれまで通りプラグインでの対応が必要になる。

4. Pythonのサードパーティモジュールが見つからない(module *** not found error)

これは結論PYTHONPATHが通っていないことが原因で発生する。 デフォルトでは/var/taskまではパスは通っているのだが上記のようにsrc/以下になるとモジュールが見つからなくなる。 この場合は以下のようにPYTHONPATHを追加しよう。

import sys
sys.path.append('/var/task/src')

もしかしたらserverless.yamlenvironmentディレクティブでPYTHONPATHに追加するような記載をすればスマートに追加可能かもしれないが試してはいない。→ためしたところうまくできた。環境変数にPYTHONPATHを指定して、今回の例では/var/task/srcを記載すれば正しく設定されていた。

import周りを最適化するisortを使用すると、上記のsys.append(...)でのPYTHONPATH調整は使えなくなるので、できるだけ予めPYTHONPATHを通すような実装としたい。