スポンサーリンク

Serverless FrameworkでPythonのコードをAWSLambdaにデプロイする

前回に引き続き今回もServerless Frameworkの勉強関連。今回はランタイムとしてNode.jsではなくPythonが必要なコードをAWS Lambdaにデプロイした際の備忘録になります。

Serverless FrameworkでPythonのコードをデプロイするとは?

  • そもそもServerless Frameworkはnpmでパッケージ管理しNode.jsで動作するライブラリ
    • Lambdaで動作するようなPythonのコードを作成 → Serverless Frameworkでデプロイするということ
    • serverless-python-requirementsというプラグインを使用することでrequirements.txtにまとめたpipでインストールするパッケージ群を踏まえたLambdaのデプロイができる
      • 具体的にはパッケージ群が自動でレイヤー化され配置される
      • ↓レイヤー画面
      • ↓関数画面

AWS Lambdaで選択可能なPythonのランタイムについて

  • 以下に添付の画像の通りLambdaでは関数を実行するPythonバージョンとして3.7,3.8,3.9の3つがサポートされている模様
2023年2月18日時点の情報カスタムランタイムというのが何かは分かってないけど入門で使うものではなさそう。。
  • Lambda側でサポートしてないバージョンで開発を進めてしまうとpipでインストールするモジュールと、実行するPythonのバージョンの兼ね合いで動作しないことが考えられるので、開発環境のPythonバージョンは3.7~3.9のいずれかにし、運用環境と合わせると良さそう。

Python3.9+Node.jsの環境構築(DevContainer使用)

AWSLambdaのランタイムに関する制約を受け今回はPython3.9の開発環境を作ることにしました。

  • 個人用PCにPython3.8とPython3.11をインストールしているが、Python3.9はインストールしていない
  • PC内のPathはPython3.11を向いているままにしておきたい
  • DevContainerを使わずにコンテナ環境を構築した場合、pip installによってインストールされるパッケージがホストPCから参照できない(しづらい)場所にインストール先される
    • エディタで補間が効かず開発が進めづらい
    • 具体的にはユーザーのホームディレクトリ以下の.local/lib/python3.9/site-packages配下にインストールされる

個人的な理由を含んでますが、今回はVSCodeのDevContainerを使った環境を構築することにしました。

services:
  python:
    build:
      context: ./
      dockerfile: ./.docker/python/Dockerfile
    volumes:
      - .:/app:cached
    tty: true
    environment:
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
FROM python:3.9
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

ENV TZ Asia/Tokyo

RUN groupadd -g 1000 app-group && \
    adduser app-user --uid 1000 --gid 1000 --disabled-password --gecos ""

WORKDIR /app

RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - && \
    apt-get update -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false && \
    apt-get -y --no-install-recommends install git \
    nodejs && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

USER app-user
{
  "name": "serverless-python-example - Existing Docker Compose (Extend)",
  "dockerComposeFile": [],
  "service": "python",
  "workspaceFolder": "/app",
  "postCreateCommand": "/app/.devcontainer/post-create.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python"
      ],
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash"
      }
    }
  },
  "remoteUser": "app-user"
}
#!/bin/bash

npm install
npx serverless config credentials --provider aws --key $AWS_ACCESS_KEY_ID --secret $AWS_SECRET_ACCESS_KEY
pip install -r requirements.txt

  • python:3.9をベースとしたコンテナを起動
    • Node.js18についてリポジトリを追加し、apt-get installからインストール
    • serverlessでデプロイするのに必要となるアクセスキー/シークレットアクセスキーを.envからコンテナに渡す
  • DevContainerのpostCreateCommandでpost-create.shを1度だけ実行
    • package.jsonに基づいたパッケージのインストール
    • serverlessでLambdaデプロイするのに必要となるAWSのクレデンシャル作成
    • requirements.txtに基づいたpipモジュールのインストール

などを行っています。

最終的な開発環境のフォルダ構成は↑の画像のような感じになりました。
個人的にはnpm用のpackage.jsonとpythonのファイルが並んでるのが結構違和感ある。

Pythonのプログラムを作成

今回は参考サイトのQiitaの記事と同じ内容で作成させていただきました。

import datetime
import jpholiday

def hello(event, context):
    holidayName = jpholiday.is_holiday_name(datetime.date(2021, 8, 8))
    return {
        "message": holidayName,
        "event": event
    }

作成したhandler.pyは関数の実行を持たないコードなので、開発環境で動作確認する場合は以下のようなワンライナーでインポート→関数実行とする必要がある。

$ python -c "from handler import hello; print(hello({}, {}));"

Makefileに以下のような感じで書いておくと便利(make test-handlerで呼び出す)

test-handler:
  python -c "from handler import hello; print(hello({}, {}));"

DevContainerの開発環境で呼び出すと↑の画像のような感じで結果が得られます。

Serverless Frameworkの設定について

デプロイで必要となるIAMロールついては前回の内容と被るので省略。→ 前回の記事

最終的に以下のようなserverless.ymlとなりました。
概ね参考サイトの通りですが今回の私のケースではserverlessのバージョンが3なので、レイヤーの設定の辺りが微妙に違いました(このへん公式ドキュメント読んだ)。
また、後述するpackageの設定によってデプロイするソースに余分なものを含めないようにする設定なども行ってみました。

service: serverless-python-example

frameworkVersion: '3'

provider:
  name: aws
  runtime: python3.9
  region: ap-northeast-1
  stage: dev

functions:
  hello:
    handler: handler.hello
    # pipでインストールするパッケージをLambdaでレイヤーとして扱うためのオプション設定
    layers:
      - Ref: PythonRequirementsLambdaLayer

package:
  patterns:
    # 一旦すべて除外 → Lambdaを動かすのに必要となるソースだけ明示する
    # deploy時に生成されるzipに以下のファイルだけが含まれるようになる
    # 今回のケースでは実行したい関数を含むhandler.pyだけで良い
    - "!**"
    - "handler.py"

# serverless-python-requirementsをコマンドからインストールしたときに自動で設定された(ような気がする)
plugins:
  - serverless-python-requirements

# pipでインストールするパッケージをLambdaでレイヤーとして扱うためのオプション設定
custom:
  pythonRequirements:
    layer: true
  • packageの設定を行わずにデプロイするとpackage.json、package-lock.json、docker-compose.yml等の不要なソースが展開されてしまう。
    • package.json、package-lock.jsonなどはserverless frameworkをインストールするための設定なのでデプロイに含める必要がない
    • requirements.txtについてはデプロイ時にpythonRequirements.zipという名前のzipファイルを生成しS3にアップするような仕組みとなっているため、Lambda上にアップする必要がない

      ↑S3のバケット内にアップされたpythonRequirements.zip。S3からダウンロードして展開するとpipでインストールしたjpholidayのソースが含まれている。
    • docker-compose.yml、.env.eaxmple、.dockerディレクトリなど開発環境を作るためのファイルなどは全て不要
    • 上のような内容によって今回のシンプルな関数においてはhandler.pyだけで良いことが分かった。package設定について「全てを除外 → handler.pyだけ対象」というような設定をした
      ↑の画像はpackage設定前と設定後のS3にアップロードされるzipファイルのサイズを比較したもの。設定前に112KBだったものが設定後274Bにまで縮小できました。

今回はPython+Serverless Frameworkの入門的な内容なのでLambdaの関数を作るところまでになりましたが、CloudWatch Eventも含めて指定時間毎に実行するような構成のデプロイもできる模様。

デプロイおよび動作確認

以下のコマンドでデプロイ(これも実際にはMakefileに書いてmake deployみたいな感じで呼び出してます)

$ npx serverless deploy --verbose

今回はIAMの権限回りのエラーは出ず、1発でAWSにLambda関数が出来上がりました((^^))

↑デプロイされた serverless-python-example-dev-hello 関数についてTestを実行したときの結果

  • DevContainer環境と同じようなメッセージが返っていることが確認できる。
  • コードソースのエクスプローラにも余計なファイルが上がってないことが確認できました。

まとめ

  • 今回の構成を覚えることでServerless FrameworkでPythonを使ったコードをLambdaにデプロイできる
  • pipモジュールを使ったアプリもserverless-python-requirementsプラグインを利用することでレイヤーを活用した形で簡単にデプロイできる

参考サイト

タイトルとURLをコピーしました