はじめに
この記事のターゲットは「NumPy, OpenAI とか外部モジュールに依存する Python コードを書いたけど、AWS Lambda 上で動かしたいな!」という人向けです。
※特に最近の話題ではありません & 「別に AWS Lambda とか触らないなぁ」、という人は無視して下さい〜。
解決したい課題
AWS Lambda って、サーバーレスなので繰り返し実行するコードを実行するランタイム環境としてはすごく便利ですよね。 Lambda コンソール上で直接コードも書けますし。
ただし、多くの人が一度は ↓ で躓きます。
あれ? 外部モジュール (pip) ってどうやって使うんだ?
Lambda 標準のやり方
次の2つのやり方が AWS Lambda 標準であります。どちらも手順が煩雑になりがちなので、開発でも自動化はしたいですね。
① Zip アップロード | ② Docker イメージ | |
---|---|---|
説明 |
pip install --target {path} で事前にローカルインストールしたのを *.zip で固めてアップロード |
Docker Image を ECR にアップロードして利用 |
✅ メリット | (Docker イメージ方式に比べれば) 簡単で高速 | コード実行上のほぼ全ての要件を Dockerfile 内で解決できる |
❌ デメリット | ImageMagick とか、native の動的ライブラリ、コマンドが実行環境に必要なケースに対応できない | イメージをビルドして、ECR にアップロードして... 大変! |
特によくハマるのが、 「numpy の様に コンパイルが必要な Native code (C/C++) を含む pip module」 です。
実際に numpy のコードを見てみると、主要なコードは C で書かれています。
この場合、Mac (arm64) で pip install numpy
して入るのは、 「Mac 用かつ arm64 CPU 用のバイナリ」 です。
つまり、Mac 等のローカルで Packaging して Zip アップロードした場合、Lambda のランタイムイメージが Linux / x86_64 では動きません。
Serverless Framework を使って自動化しよう!
ここでは、 方式 ②「Docker イメージ」 を Serverless Framework を使ってやる方法を説明します。
方式 ①「Zip アップロード」を Serverless Framework でするケースはクラメソさんの ↓ が参考になります。
たぶんそのまま使える、方式 ②「Docker イメージ」のサンプルコード。
ローカル環境
ローカルでは pipenv で開発し、Lambda へのデプロイは Serverless Framework で行うとします。まずは開発 PC に以下が必要です。
ランタイム環境 | インストール手順 |
---|---|
Node.js (v14.21.3) | 直接インストールするか、nvm 等を使うか |
pipenv | https://pipenv-ja.readthedocs.io/ja/translate-ja/install.html |
Docker | Docker Desktop か、個人的には無償の Rancher Desktop がオススメ |
ディレクトリ構造
最終的にこんな感じになります。
.
├── .gitignore
├── README.md
├── Dockerfile
├── Pipfile
├── Pipfile.lock
├── package.json
├── package-lock.json
├── serverless.yml
└── app.py
Serverless Framework が Node.js 製なので、
package.json
があります。
手順
.gitignore
本サンプルプロジェクトの .gitignore
はこんな感じです。プロジェクトルートに作成して下さい。
# Distribution / packaging
.Python
env/
.env
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Node.js
node_modules/
# Serverless directories
.serverless
Python code
Python 3.10 ベースで Pipfile ファイルを作ります。
pipenv --python 3.10
外部ライブラリ (e.g. numpy) をインストールします。
pipenv install numpy
Lambda ハンドラコード app.py
を用意します。 (※申し訳程度に numpy を使用してます笑)
#!/usr/bin/env python3
import numpy as np
def handler(event: dict, context: dict) -> dict:
arr1 = np.array([10, 20], dtype=np.int32)
arr2 = np.array([30, 40], dtype=np.int32)
return {
'statusCode': 200,
'body': str(arr1 + arr2)
}
if __name__ == '__main__':
ret = handler({}, {})
print(ret)
Install Serverless Framework
まずは Serverless Framework を global install します。
npm install -g serverless
とは言え、可能な限り global install したものは使いたくない (チームで開発する場合は特に) ので、package.json
を作成します。
{
"name": "sample-project",
"dependencies": {
"serverless": "^3.31.0"
},
"devDependencies": {},
"scripts": {
"deploy": "npx sls deploy"
}
}
npm install しておきましょう。
npm install
Serverless Framework を使ったデプロイ設定 serverless.yml
を作成します。
service: sample-project
provider:
name: aws
runtime: python3.10
memorySize: 1024
region: ap-northeast-1
architecture: x86_64
ecr:
images:
image_sample_project:
platform: linux/amd64
path: ./
functions:
sample-project:
name: sample-project
timeout: 900
image:
name: image_sample_project
# environment:
# ENV_NAME: ENV_VALUE
最後に実行環境となる Docker イメージの Dockerfile
を作成しましょう。
FROM public.ecr.aws/lambda/python:3.10
# 環境にインストールが必要な物があれば、ここで...
# RUN yum update -y && yum install -y gcc make ...
# pip: install python modules.
COPY Pipfile ./
COPY Pipfile.lock ./
RUN pip install pipenv && \
pipenv requirements > requirements.txt && \
pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
# Copy function code
COPY *.py ${LAMBDA_TASK_ROOT}
# Compatible with initial base image
CMD ["app.handler"]
それでは早速デプロイしてみましょう。
ECR にイメージをアップロードする為に、環境変数に AWS の認証情報 AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
が必要です。 awsume 等を使うのも良いでしょう。
Docker 環境の起動も忘れずに!
npm run deploy
実行してみる
AWS Console >> Lambda >> Functions >> sample-project を開いてみて下さい。
「Test」タブでテスト実行をしてみましょう。
トラブルシューティング
色々やってて、なんか動かなくなった場合はキャッシュ要素の初期化を試みましょう。
- sls コマンドを使う
-
.serverless
ディレクトリを消す