9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

uv × CDK を活用して、Python Lambda 開発環境構築を仕組み化する

Last updated at Posted at 2025-12-15

Lambdaの開発環境構築の属人化をなくしたい!

AWS Lambda をPythonで実装することは、SQLのみでは難しいデータ処理など様々なユースケースにおいて標準的な解決策で、私のチームでも多く利用されています。

チーム内でCDKによるIaC化など実装環境の仕組み化が最近進んでいますが、どのような方法でLambda(Python)を実装するのが最善であるかのナレッジは蓄積されていない課題がありました。

例えば、Pythonの環境構築については、pip + venv がメジャーで、細かい依存関係の記述やバージョンの整合性チェックなどの管理が煩雑になりがちでした。

Python プロジェクトの統一的な管理に向けて

上記の課題に対して、uv を導入することで開発環境の再現性を高めることにしました。
uv をチーム開発(特にLambda実装)で導入するメリットは主に以下の2つであると感じています。

  1. OS や作業環境に依存しない開発環境の構築
    a. uv.lock による 開発環境の「再現性」の担保
  2. ローカル環境と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点にあります。

依存解決の高速化

uvpip よりも10 - 100倍ほど高速であるとされていますが、主に以下の理由からだと考えています。

  • Rust製 - メモリ管理や並列処理が優れている
  • PubGrubアルゴリズム2を用いた依存関係解決
  • グローバルキャッシュの利用

pyproject.tomluv.lock によるパッケージ管理の統一化

それぞれ以下のような役割があります。

  • pyproject.toml: 抽象的な依存関係の記述

    • 「Pythonのバージョンに制約があるか」、「どのモジュールを使用するか」など
    • PEP 621に準拠
  • uv.lock : 具体的な依存解決の結果の詳細

    • 「実際にどのモジュールを使うか」についての記述
    • 複数のOSでも問題ないユニバーサルなロックファイル
    • uv lock / uv sync で作成

Lambda とローカルの環境の統一的管理

Lambdaの実装においては、上記 uvの優位性に加え、pyproject.toml[dependency-groups] を活用することで以下のようなモジュールを宣言的に分離することができます。

  1. ローカルでのみ必要なモジュール(テストモジュール)
    a. pytestmypy など
  2. 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を参照すると内部的には以下のような挙動になっていることがわかります。

  1. public.ecr.aws/sam/build-python3.x を使って Docker コンテナを立ち上げ、ローカルのソースコードディレクトリをコンテナ内にマウントする
  2. uv.lock を検出 - uv によるパッケージングを決定
  3. uv.lock から互換性のあるrequirement.txt をエクスポート
  4. 依存関係を uv を用いてインストール
  5. 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コーディングに対して、テスト駆動での実装を社内で浸透させる
    • 高い環境再現性により、ローカルテストの信頼性を向上

参考

  1. https://github.com/aws/aws-cdk/pull/33880
    aws-lambda-python-alpha はビルド時のバンドリングを自動で行ってくれるaws-lambda をラップしたモジュール

  2. https://qiita.com/s_taki/items/1d94e5e9544ebbf32778#pubgrub
    PubGrub アルゴリズムはグラフ理論に基づくアルゴリズムで、複数の非互換性を連鎖的に組み合わせることで解決しているそうです

  3. https://github.com/taskipy/taskipy
    uv で使用する場合は、uv add --group dev taskipy でtest 用モジュールに加えるのがよさそうです

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?