Lambdaの開発環境構築の属人化をなくしたい!
AWS Lambda をPythonで実装することは、SQLのみでは難しいデータ処理など様々なユースケースにおいて標準的な解決策で、私のチームでも多く利用されています。
チーム内でCDKによるIaC化など実装環境の仕組み化が最近進んでいますが、どのような方法でLambda(Python)を実装するのが最善であるかのナレッジは蓄積されていない課題がありました。
例えば、Pythonの環境構築については、pip + venv がメジャーで、細かい依存関係の記述やバージョンの整合性チェックなどの管理が煩雑になりがちでした。
Python プロジェクトの統一的な管理に向けて
上記の課題に対して、uv を導入することで開発環境の再現性を高めることにしました。
uv をチーム開発(特にLambda実装)で導入するメリットは主に以下の2つであると感じています。
- OS や作業環境に依存しない開発環境の構築
a. uv.lock による 開発環境の「再現性」の担保 - ローカル環境とLambda実装環境の統一的・宣言的な管理
a. pyproject.toml の dependency-groups による 依存定義の構造化
さらに2025/07 にuv がCDK(aws-lambda-python-alpha)にて、サポートされました1。これにより、ローカルでuv によって構築した環境を最大限活用しながら、Lambdaを実装することが可能になりました。
そこで、この記事では uv × CDK を活用することで、Lambda 開発環境構築 において開発者が意識するべきことを減らし、再現性を高める方法についてまとめたいと思います。
比較早見表
| Before | After | |
|---|---|---|
| 仮想環境 |
pip + venv
|
uv |
| 依存パッケージの定義 | requirements.txt |
pyproject.toml |
| 依存関係の 再現性 |
pip freeze で都度出力 |
uv.lock による自動生成・更新 |
| 本番/開発の 依存分離 |
複数の requirements.txt管理 / Lambda layer の暗黙的利用 |
[dependency-groups] による宣言的な分離 |
| ローカルでの タスク実行 |
仮想環境を有効化後に手動実行 |
taskipy でコマンド統一 |
| Lambdaへの CDKデプロイ |
バンドル作成スクリプトを自前実装 |
aws-lambda-python-alpha で自動バンドル |
なぜ uv か
uv が チーム開発において優れている点は、主に「依存解決の高速化」と「パッケージ管理の統一化」の2点にあります。
依存解決の高速化
uv は pip よりも10 - 100倍ほど高速であるとされていますが、主に以下の理由からだと考えています。
- Rust製 - メモリ管理や並列処理が優れている
- PubGrubアルゴリズム2を用いた依存関係解決
- グローバルキャッシュの利用
pyproject.toml と uv.lock によるパッケージ管理の統一化
それぞれ以下のような役割があります。
-
pyproject.toml: 抽象的な依存関係の記述- 「Pythonのバージョンに制約があるか」、「どのモジュールを使用するか」など
- PEP 621に準拠
-
uv.lock: 具体的な依存解決の結果の詳細- 「実際にどのモジュールを使うか」についての記述
- 複数のOSでも問題ないユニバーサルなロックファイル
-
uv lock/uv syncで作成
Lambda とローカルの環境の統一的管理
Lambdaの実装においては、上記 uvの優位性に加え、pyproject.toml の [dependency-groups] を活用することで以下のようなモジュールを宣言的に分離することができます。
- ローカルでのみ必要なモジュール(テストモジュール)
a. pytest や mypy など - Lambda Layer として管理するファイルサイズの大きいモジュール
a. psycopg2 や公式SDKから出ている pandas など
具体的には、dependency-groups に定義したライブラリは、CDKデプロイ時のバンドル処理に含まれないため、Lambda Layerで提供するライブラリなどをメインコードから除外することができます。
例えば、「リクエストした結果をPandasで集約してS3に保存する」といったシナリオをローカルのモックでテストした後にLambdaデプロイするといったことを想定した場合は、以下のような pyproject.toml になります。
[project]
name = "lambda"
version = "0.1.0"
description = "Test AWS Lambda with CDK Project"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"requests>=2.32.5",
]
[dependency-groups]
dev = [
{include-group = "test"},
{include-group = "layer"}
]
test = [
"boto3>=1.42.9",
"moto>=5.1.18",
"pytest>=8.0.0",
"taskipy>=1.14.1"
]
layer = [
"pandas>=2.3.3",
]
ローカルテスト
uv sync
uv run pytest tests/unit/ -v
(さらにテストを簡易的に行うために)
余談ですが、taskipy3 を用いて、
[tool.taskipy.tasks]
test = "uv sync && uv run pytest tests/unit/ -v"
と定義して、uv run task dev とすることで、npm testのようにテストを簡単に実行することも可能です。
CDK によるLambda デプロイ
上述通りaws-lambda-python-alphaにおいてuv がサポートされているので、以下のCDKを実装して、cdk deploy するのみです。
サンプルCDKコード
// lib\stack.ts
import * as lambda from '@aws-cdk/aws-lambda-python-alpha';
import * as lambdaCore from 'aws-cdk-lib/aws-lambda';
export class UvTestCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 共通のバンドル設定
const commonBundlingOptions = {
assetExcludes: [
'tests',
'.pytest_cache',
'.venv',
'.git',
'__pycache__',
'*.pyc',
'.gitignore',
'.python-version',
'README.md',
],
};
const sampleLambda = new lambda.PythonFunction(this, 'SampleLambdaHandler', {
functionName: 'SampleLambdaFunction',
entry: '../lambda', // Lambda関数のコードが格納されているディレクトリへのパス
runtime: lambdaCore.Runtime.PYTHON_3_12, // Pythonランタイムのバージョン
index: 'function_sample.py', // エントリーポイントとなるPythonファイル
handler: 'lambda_handler', // ハンドラ関数の名前
bundling: commonBundlingOptions, // 除外したいファイルやフォルダの共通設定
environment: {
BUCKET_NAME: s3Bucket.bucketName,
},
layers: [
// pandas
lambda.PythonLayerVersion.fromLayerVersionArn(
this,
'AWSSDKPandasLayer',
`arn:aws:lambda:${this.region}:336392948345:layer:AWSSDKPandas-Python312:20`
),
],
});
(補足) aws-lambda-python-alpha の内部的な挙動について
Githubを参照すると内部的には以下のような挙動になっていることがわかります。
-
public.ecr.aws/sam/build-python3.xを使って Docker コンテナを立ち上げ、ローカルのソースコードディレクトリをコンテナ内にマウントする -
uv.lockを検出 - uv によるパッケージングを決定 -
uv.lockから互換性のあるrequirement.txtをエクスポート - 依存関係を uv を用いてインストール
- Lambdaパッケージ用のアセットにコピー
rsync -rLv --exclude='.python-version' /asset-input/ /asset-output/python &&
cd /asset-output/python &&
uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt &&
uv pip install -r requirements.txt --target /asset-output/python
ローカル実行時との明確な違いは uv export --no-dev によって dev で定義された依存関係を明示的に含めないようになっていることです。
これにより、ローカルテストでは dev に含めていた依存関係をLambdaデプロイ時には含めない実装にすることができます。
開発者が意識すること
上記の環境構築を行うことで、開発者が意識すべきことは以下のみになります。
- モジュールインポート時に、「開発環境でのみ必要か」や「Lambda Layerで管理すべきか」を意識する
-
uv add/uv add --group devの使い分け
-
他は従来と同様、
- Python処理実装
- ローカルでの単体テスト
- AWSリソースをCDKで新しく作成する場合は
motoなどのモックデータでテスト- CDKの実装(Layer作成は必要に応じて)
- CDKデプロイ
の手順を踏むのみで、開発者が意識するべき点が少ないことがわかります。
補足
以下のようなシェルスクリプトを定義しておくことで、新規プロジェクトの構築が init-lambda hogehoge で完了します。
function init-lambda() {
if [ -z "$1" ]; then
echo "Error: プロジェクト名を指定してください。"
return 1
fi
PROJECT_NAME=$1
cd ~/xxx/ # 任意のディレクトリに移動
# すでに同名ディレクトリが存在する場合はエラー
if [ -d "$PROJECT_NAME" ]; then
echo "Error: ディレクトリ '$PROJECT_NAME' は既に存在します。"
return 1
fi
mkdir -p "$PROJECT_NAME" && cd "$PROJECT_NAME"
# --- 1. CDK Setup ---
echo "🚀 Initializing CDK..."
mkdir cdk && cd cdk
cdk init app --language typescript
npm install aws-cdk-lib@^2.232.2 @aws-cdk/aws-lambda-python-alpha
# --- 2. Python (uv) Setup ---
echo "🐍 Initializing Python (uv)..."
cd ../
uv init lambda --python 3.12
cd lambda/ && uv add --group dev boto3 pytest taskipy ruff
# --- 3. Write taskipy settings to pyproject.toml ---
echo "📝 Configuring pyproject.toml..."
cat <<EOF >> pyproject.toml
[tool.taskipy.tasks]
test = "uv sync && uv run pytest tests/unit/ -v"
e2e = "uv sync && uv run pytest tests/e2e/ -v"
EOF
# --- 4. Create tests directories ---
echo "🧪 Creating test directories..."
mkdir -p tests/unit tests/e2e
echo "# Unit tests go here" > tests/unit/__init__.py
echo "# E2E tests go here" > tests/e2e/__init__.py
# --- 5. Open VSCode ---
echo "🖥️ Opening VSCode..."
cd ../ && code .
echo "✅ Project '$PROJECT_NAME' created and configured!"
}
まとめ
この記事では、uv × CDK によって、Python Lambda 開発環境の再現性を高める方法についてまとめました。
Python でLambdaを実装する機会が多い人にとって有益な記事になれば幸いです。
また、この仕組みがチームに以下のような効果をもたらすといいなと期待しています。
- プロジェクト管理がチーム内で仕組み化される
- メンバーの環境依存性の排除
- テストによるコードの品質の担保
- AIコーディングに対して、テスト駆動での実装を社内で浸透させる
- 高い環境再現性により、ローカルテストの信頼性を向上
参考
-
https://github.com/aws/aws-cdk/pull/33880
aws-lambda-python-alphaはビルド時のバンドリングを自動で行ってくれるaws-lambdaをラップしたモジュール ↩ -
https://qiita.com/s_taki/items/1d94e5e9544ebbf32778#pubgrub
PubGrub アルゴリズムはグラフ理論に基づくアルゴリズムで、複数の非互換性を連鎖的に組み合わせることで解決しているそうです ↩ -
https://github.com/taskipy/taskipy
uv で使用する場合は、uv add --group dev taskipyでtest 用モジュールに加えるのがよさそうです ↩