- 宮永崇史
- 約 5,400 文字
- 2,000 View
目次
はじめに
こんにちは、TIG/DXユニット所属の宮永です。
PipenvとLocalStackを使用したLambda開発環境の構築を紹介します。
本記事で作成するデモアプリは以下のGitHubリポジトリに格納しています。ご参考にしてください。
https://github.com/orangekame3/pipenv-lambda
【本記事で伝えたいこと】
本記事で最も伝えたいことはデプロイパッケージと開発パッケージの分離です。 Pipenvを使用することでzipの容量を節約しながらLambdaをデプロイすることができます。 やや長い記事となっていますので、「LocalStackへのデプロイ」の章だけでも見ていただけると幸いです。
Pipenvとは
Pipenvはパッケージ管理ツールです。似たようなツールにPoetry等があります。
Poetryを使用したPython開発環境の構築は澁川さんの記事がとても参考になりますのでぜひご覧ください。
開発環境
開発に取り組む前に筆者の開発環境を記載します。記事中Linuxコマンドを使用している箇所があります。Windowsで開発される方はWSLを使用することをおすすめいたします。
- OS Ubuntu 20.04
- Python(pyenv) 3.9
- Pipenv
- Docker
- docker compose v2
- AWS CLI v2
プロジェクトの作成
まずはPipenvをダウンロードしましょう。
pip install pipenv |
次にプロジェクトを作成します。
プロジェクトのルートディレクトリで以下コマンドを実行してPythonプロジェクトを作成します。
ランタイムはLambdaでサポートしている最新環境のPython 3.9を使用します。
参考:Lambda ランタイム
pipenv --python 3.9 |
~/git/src/pipenv-lambda main |
プロジェクトの作成ができました 🎉
この状態でtreeコマンドを実行するとPipfileが作成されていることを確認できます。
~/git/src/pipenv-lambda main* |
PipfileにプロジェクトのPythonのバージョンや使用するパッケージ等が記載されています。catコマンドで中身を確認します。
~/git/src/pipenv-lambda main* |
Pipenvでパッケージをインストールする際はpipenv installコマンドを使用します。pipenv installでインストールしたパッケージは[packages]で管理されます。
一方、このコマンドに--devオプションをつけてインストールした際は[dev-packages]でパッケージ管理されます。
この点については後ほど「LocalStackへのデプロイ」で説明します。
開発パッケージのインストール
続いてテスト環境を構築します。以下のコマンドでpytestをインストールします。
~/git/src/pipenv-lambda main* 10s |
同様にして静的型チェックツールであるmypyもインストールします。
pipenv install mypy --dev |
デプロイパッケージのインストール
Pythonの標準パッケージ以外にも使用したいパッケージはあると思います。
Lambdaを実行する上で必要となる外部パッケージは--devオプションは付けずにインストールします。
今回はpandasをインストールします。
またpandasでExcelファイルを扱うため、xlwtとxlsxwriterもインストールします。
pipenv install pandas xlwt xlsxwriter |
ここまででPythonプロジェクトの準備は一旦完了です。
ローカル環境でLambdaのデプロイと実行を確認するためLocalStackを使用します。
次の章でLocalStackの準備をします。
LocalStackの準備
LocalStackを使用して、Lambdaのデプロイと動作検証を行います。
docker-compose.ymlの作成
以下のようなdocker-compose.ymlを用意してください。
version: "3.8" |
docker-compose.ymlの準備ができたらビルドします。
docker compose up --build |
起動が確認できたらLocalStackの準備も完了です。
次にAWS CLIを設定します。
AWS CLIの設定
AWS CLIでは認証情報などをプロファイルとして保存できます。
AWS CLIをインストールされた方はご自身が使用しているOSのhomeディレクトリに.awsの隠しファルダがあります。(エクスプローラーなどで確認する場合は隠しフォルダを表示するように設定してください。).awsフォルダ配下には.configと.credentials2つのファイルがありますのでそれぞれ以下のように設定してください。
参考:名前付きプロファイル
今回は以下のようにlocalというプロファイルを作成しました。
[local] |
[local] |
デモアプリの実装
最終的なディレクトリ構成
以降、複数のファイルを作成します。最終的なディレクトリ構成を記載しますので、適宜参考にしてください。
. |
全体構成
今回作成するのはS3バケットからJSONファイルを取得し、ETL処理後にExcelファイルとして再度S3バケットに格納するアプリです。
S3バケットに格納したExcelファイルはAWS CLIコマンドでファイルをダウンロードして想定通りの挙動をしているか検証します。
以下、構成図です。
アプリ機能詳細
JSON→ExcelのETL処理について以下記載します。
S3バケットには予め以下の構造をもつJSONファイルを配置しておきます。
実装するLambdaには大きく2つの機能をもたせます。
- JSON→Excelへの変換
- 「ボーナスポイント」カラムの追加
「ボーナスポイント」は以下の条件で決定します。
【条件】
会員ランクが「4,5」の会員には「ポイント」×1.25倍のボーナスポイントを、会員ランク「1,2,3」の会員には「ポイント」と同等のボーナスポイントを付与することします。
[ |
例えば上記のようなJSONファイルを取り込んだ場合、Lambdaは以下のExelファイルを出力することとします。
| 会員番号 | 名前 | 会員ランク | ポイント | タイムスタンプ | ボーナスポイント |
|---|---|---|---|---|---|
| 000 | 長野原 ひろし | 4 | 58 | 2021-05-16 | 72.5 |
| 001 | 般若 竜門 | 2 | 75 | 2021-07-19 | 75 |
| 002 | 十河 アンナ | 2 | 57 | 2021-09-06 | 57 |
ハンドラの実装
それではアプリ本体を実装します。
Lambdaはlambda.pyとmodel.pyの2つで構成します。
機能のほとんどはmodel.pyに記述し、lambda.pyではハンドラを呼び出すのみにします。
以下、lambda.pyです。
from model import Handler |
次にmodel.pyです。
import logging |
実装では型アノテーションを付けています。
正しく型アノテーションを付与できているか、mypyを使ってチェックできます。Pipfileに以下を追記します。
[scripts] |
Pipfileへ追記したら以下コマンドを実行します。
pipenv run mypy |
型付けに問題がなければ以下の結果を得られるはずです。
Success: no issues found in 2 source files |
次にテストコードを実装します。
テストコードの実装
テストにはpytestを使用します。プロジェクトルートにtestsフォルダを作成し、model.pyをテストするtest_model.pyを実装します。
以下testsディレクトリのファイル構成です。
. |
__init__.pyファイルがないとテストに失敗するので忘れずに作成してください。
pytestを使用すれば簡単にテーブルドリブンテストを実装できます。
fixtureを使用することでhandlerの初期値を入力でき、各メソッドテストで使い回しが可能です。今回pandasを使用したテストを行うため、assert部にはpandasのDataFrame比較メソッドであるtesting.assert_frame_equalを使用しました。
以下はボーナスポイント付与のメソッドであるprocessをテストしたtest_processの例です。
import pytest |
テストもPipfileでコマンド化できます。以下をPipfileに追記してください。
[scripts] |
ファイルへの追記が完了したら、テストを実行します。
|
無事テストを通過しました🎉
LocalStackへのデプロイ
Lambdaのzip化やLocalStackへのデプロイはMakefileで管理します。
MakefileはPipfileに追加したコマンドやAWS CLIコマンドで構成されています。筆者の環境はAWS CLI v2なのでinvokeコマンドでpayloadを指定時に--cli-binary-format raw-in-base64-outオプションを付与しています。
参考:破壊的変更 - AWS CLI バージョン 1 からバージョン 2 への移行
Makefile全貌
以下作成したMakefileです。
|
デプロイパッケージのzip化
ポイントはzipコマンド部です。
zip:clean |
前提として、Lambda上でPythonの外部パッケージを使用する際は外部パッケージを含んだzipファイルを作成する必要があります。直接CLI等からアップロードする場合は50MBの上限が存在します。
今回開発環境の補助ツールとしてpytestとmypyを、デプロイ時に必要なパッケージとしてpandasとxlwt、xlsxwriterをインストールしました。
開発に使用するpytestとmypyはLambdaの機能として不要です。そこでまずはPipfileからrequirements.txtを作成します。
pipenv lock -r -> requirements.txt |
requirements.txtには–devオプションでインストールしたパッケージは含まれません。開発パッケージとデプロイパッケージを分離できました。
次にrequirements.txtを元にdeploy-packagesというフォルダを作成します。事前にmkdirコマンドでdeploy-packagesを作成しておきます。pipコマンドは--targetオプションを付与することでインストール先を指定できます。
コマンド冒頭でpipenv runを付与することで、プロジェクトの仮想環境上で実行できます。
pipenv run pip install -r requirements.txt --target $(DEPLOY_PACKAGES_DIR) |
あとはzipコマンドを使用して実装したlambda.pyとmodel.pyを追加するだけです。
なお、このzipコマンドを作成する際に以下の記事と
https://pyteyon.hatenablog.com/entry/2019/08/04/204704
pipenv公式の以下のIssue
https://github.com/pypa/pipenv/issues/746
を参考にさせていただきました。
zipコマンドを実行します。
make zip |
正常に動作していればルートディレクトリのbinフォルダにlambda.zipが生成されます。
続いてLocalStackが起動していることを確認した上でS3上に新規バケットを作成します。
make bucket |
バケットの作成が完了したらLocalStackにアプリをデプロイします。
make create |
Lambdaのデプロイに成功していれば以下のレスポンスが返ってきます。
{ |
LocalStackへのLambdaデプロイに成功しました🎉
最後に動作検証をします。
動作検証
テストデータの作成
まずはETL処理元のテストデータを作成します。
ルートディレクトリにutils/utils.pyを作成し、以下のコードを実装します。
import datetime |
テストデータを作成するにあたってmimesisとfireの2つの外部パッケージを使用しました。
mimesisはダミーデータを作成するパッケージ、fireはPythonスクリプトにコマンドライン引数を渡すパッケージです。fireは社内チャットで話題になっていたので今回使用してみました。非常に便利でした。皆さんぜひ、使ってみてください。utils直下で以下コマンドを実行することで先程作成したtest-bucketにtest.jsonを任意のデータ量で格納できます。
今回は100行のダミーデータを作成しました。
python utils.py 100 |
Lambdaの実行
それではデプロイしたLambdaを呼び出します。AWS CLIのinvoke実行時に--payload '{ "input_obj": "test.json" }'を付与することでLambdaにtest.jsonの場所を渡します。
make invoke |
Lambdaの実行が完了していれば次のレスポンスが返ってきます。
{ |
それではS3からLambdaの実行により生成されたExcelファイルをダウンロードします。
make download |
以下の画像はETL処理元のJSONファイルとETL処理後のExcelファイルを比較したものです。
想定通りの結果が得られました!!🎉
本記事で使用したソースコードは以下のGitHubリポジトリにまとめています。
https://github.com/orangekame3/pipenv-lambda
さいごに
今回はPipenvとLocalStackを使用してLambdaの開発環境を構築しました。
普段はGo言語を使用してLambdaを作成しており、外部パッケージの依存を気にすることがありませんでした。今回Pythonを使用するにあたり、公式で紹介されているデプロイ方法をもう少しスマートに行いたいと考えて試行錯誤しました。ひとつ自分として満足の行く形にたどり着けて良かったと思っています。
調べて見るとLambdaの開発環境としてはServerless Application ModelやServerless Fameworkなどのテンプレートがあるようです。こうしたフレームワークも今後触ってみて自分なりのベストな開発環境を模索していきたいです。
最後までお付き合いいただきありがとうございました。