CDKでPython Lambda Layerを追加する方法をまとめてみた
はじめに
みなさんこんにちは、クラウド事業本部コンサルティング部の浅野です。
CDKやSAMなどのIaCフレームワークを使用する際、Lambda Layerの追加方法には複数のアプローチが存在しますが、どの方法を選択すべきかで悩んだことはありませんか?
今回は、Python環境のLambdaにおけるCDKを活用したLayer追加の実践的な手法を、5つの異なる方法で実装し、それぞれの特徴や適用場面を自分なりにまとめたので解説します。
構成
環境
- Lambda ランタイム: Python 3.13 (x86_64)
- ビルド環境: Mac OS Apple silicon (M3)
- CDK Typescript (aws-cdk-lib: 2.208.0 )
※注意: 2025年7月末時点での情報に基づいています。
プロジェクト構成
以下のプロジェクト構成にて環境を作成しています。
cdk/
にcdk プロジェクト構成、 src/
配下にアプリケーションコードを格納してます。
cdk-lambda-python-layer/
├── cdk/
│ ├── lib/cdk-lambda-python-layer-stack.ts
│ ├── package.json
│ └── cdk.json
│ .
│ .
│ .
└── src/lambda/
├── functions/main.py # Lambda mainハンドラー
└── layers/
├── requirements.txt # ライブラリ記載
└── python/
├── myutils/__init__.py # 自作Utilsパッケージ
└── lib/python3.13/site-packages/ # 事前にパッケージ追加
├── _cffi_backend.cpython-312-darwin.so
├── cffi/
├── cffi-1.17.1.dist-info/
├── cryptography/
├── cryptography-45.0.5.dist-info/
├── pycparser/
└── pycparser-2.22.dist-info/
重要: Pythonレイヤーのディレクトリ構成
Python環境のLambdaでは、レイヤー内のライブラリを認識させるために決められた以下どちらかのパス構造に従う必要があります。
/opt/python/
/opt/python/lib/python3.x/site-packages/
参考: レイヤーコンテンツのパッケージング - AWS Lambda
具体的には、レイヤー用フォルダ内で以下のような構造を作成する必要があります。
# レイヤー用フォルダ構成例
src/lambda/layers/
└── python/ # ← この「python」フォルダが /opt/python として配置される
├── myutils/ # 自作パッケージ
│ └── __init__.py
└── lib/python3.13/site-packages/ # pip/poetryライブラリ
├── _cffi_backend.cpython-312-darwin.so
├── cffi/
├── cffi-1.17.1.dist-info/
├── cryptography/
├── cryptography-45.0.5.dist-info/
├── pycparser/
└── pycparser-2.22.dist-info/
重要なポイント
- レイヤーのzip/フォルダのトップレベルに
python
フォルダを配置 - Lambda実行時に
python
フォルダが/opt/python
として自動マウント - 自作パッケージは直接
python
配下に、外部ライブラリはpython/lib/python3.13/site-packages/
配下に配置
この構造を守らないと、Lambdaがライブラリを認識してくれません。
追加方法
今回は以下5つの方法でLambda Layerの作成方法を解説します。
- 方法1: ARNにて追加(AWS公式レイヤーや作成済みレイヤー)
- 方法2: requirements.txtからCDK bundlingを行う
- 方法3: @aws-cdk/aws-lambda-python-alphaを使用してrequirements.txtからbundling (方法2とアプローチは同じだが記述が異なる)
- 方法4: 手動で準備したライブラリをCDKにて追加(自前Utilsなど)
- 方法5: Docker事前ビルドでzip化してCDKにて追加(プラットフォーム間の調整可能)
方法1: ARNから参照
最もシンプルな方法です。AWS公式や既存のレイヤーが利用できる場合は、これを優先的に使用するのがおすすめです。
AWS公式レイヤーは「Serverless Application Repository」で確認できます。その他サードパーティの便利ツールなども含まれています。
AWS Serverless Application Repository
実装はLayerVersion.fromLayerVersionArn
を使用するだけです。注意点はバージョン番号まで含める必要があることです。
LayerVersion.fromLayerVersionArn - AWS CDK
// ファイル: cdk/lib/cdk-lambda-python-layer-stack.ts
// AWS公式のPandasレイヤーを参照
const awsOfficialLayers: lambda.ILayerVersion[] = [
lambda.LayerVersion.fromLayerVersionArn(this, 'PandasLayer',
'arn:aws:lambda:ap-northeast-1:************:layer:AWSSDKPandas-Python313:3'
)
];
// Lambda関数で使用
const lambdaFunction = new lambda.Function(this, 'MainLambdaFunction', {
functionName: 'cdk-lambda-python-layer-demo',
runtime: lambda.Runtime.PYTHON_3_13,
handler: 'main.lambda_handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../../src/lambda/functions')),
layers: awsOfficialLayers,
architecture: lambda.Architecture.X86_64,
});
メリット:
- 実装が簡単
- AWS側でメンテナンス済み
- 最適化されたパフォーマンス
デメリット:
- 提供されているパッケージに限定
- バージョン管理がAWS依存
方法2: CDK Bundling(ビルド時自動作成)
requirements.txt等に記載したライブラリを、CDKデプロイ時に自動でビルドしてレイヤーを作成する方法です。
この方法を使用すればCDKが内部でDockerコンテナを起動し、Python環境でpip installなどを実行してライブラリをビルドしてくれます。ビルドされたzipファイルは自動的にS3にアップロードされ、レイヤーとして使用できます。
bundlingオプションでDockerイメージを選択できたり、ビルド処理をカスタマイズできます。
LayerVersion - AWS CDK
BundlingOptions - AWS CDK
// ファイル: cdk/lib/cdk-lambda-python-layer-stack.ts
// requirements.txtが配置してあるパスを参照
const requirementsPath = path.join(__dirname, '../../src/lambda/layers/python');
const requirementsLayer = new lambda.LayerVersion(this, 'RequirementsLayer', {
code: lambda.Code.fromAsset(requirementsPath, {
bundling: {
image: lambda.Runtime.PYTHON_3_13.bundlingImage, // Python 3.13のイメージを使用
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output/python/' // CDKが/asset-outputをzip化してアップロードしてくれるため
],
},
}),
compatibleRuntimes: [lambda.Runtime.PYTHON_3_13],
compatibleArchitectures: [lambda.Architecture.X86_64],
});
※ /asset-output
周りの動作はこちらを参考
AWS CDK による AWS Lambda コードの管理 | Amazon Web Services ブログ
メリット:
- requirements.txtを更新するだけで自動ビルド
- プラットフォーム環境差異を気にしなくて良い(Dockerで統一)
- ビルド環境の準備不要
デメリット:
- 毎回デプロイ時にビルドするため時間がかかる(キャッシュ利用可)
- ビルドエラー時のデバッグが少し面倒(CLI上のCDKエラーで確認)
方法3: python-alphaによるbundling
方法2と基本的な仕組みは同じですが、「@aws-cdk/aws-lambda-python-alpha」パッケージを使用することで、より簡潔に記述できます。
重要な特徴: PythonLayerVersion
は様々なPython依存関係管理ファイルに対応しており、以下のファイルを自動で検出・処理できます。
requirements.txt
(pip)Pipfile
(pipenv)poetry.lock
(Poetry)pyproject.toml
(Poetry/その他)
これにより、「pip install ****」等記載せずともプロジェクトで使用している依存関係管理ツールに関係なく、統一された方法でレイヤーを作成できます。
aws-lambda-python-alpha module · AWS CDK
PythonLayerVersion - AWS CDK
最新アプデで「uv.lock」ファイルも読み取ってくれるようになったらしいです。
AWS CDKのLambda Pythonがuvをサポートしました | DevelopersIO
// ファイル: cdk/lib/cdk-lambda-python-layer-stack.ts
import { PythonLayerVersion } from '@aws-cdk/aws-lambda-python-alpha';
const layersPath = path.join(__dirname, '../../src/lambda/layers');
const pythonAlphaLayer = new PythonLayerVersion(this, 'PythonAlphaLayer', {
entry: layersPath,
compatibleRuntimes: [lambda.Runtime.PYTHON_3_13],
compatibleArchitectures: [lambda.Architecture.X86_64]
});
個人的にはこちらの方が記述がシンプルで好みです。
メリット:
- 記述がシンプル
- 複数の依存関係管理ファイルに対応(requirements.txt、Pipfile、poetry.lock、pyproject.toml、uv.lockなど)
- 自動でファイルを検出・処理
デメリット:
- 別途パッケージ管理が必要
方法4: 手動準備
自作パッケージや、事前にpipでインストールしたライブラリを配置したフォルダをそのままレイヤーにする方法です。一番シンプルな方法で、デプロイも高速です。
LayerVersion - AWS CDK
Code.fromAsset - AWS CDK
// ファイル: cdk/lib/cdk-lambda-python-layer-stack.ts
const preBuiltLayerPath = path.join(__dirname, '../../src/lambda/layers');
const preBuiltLayer = new lambda.LayerVersion(this, 'PreBuiltLayer', {
code: lambda.Code.fromAsset(preBuiltLayerPath),
compatibleRuntimes: [lambda.Runtime.PYTHON_3_13],
compatibleArchitectures: [lambda.Architecture.X86_64]
});
例ではmyutils
という自作パッケージと、事前にローカルでpipインストールしたライブラリ(cryptography)を配置しています。
myutils
の中身はシンプルな関数です
# ファイル: src/lambda/layers/python/myutils/__init__.py
def add(a, b):
return a + b
このようにカスタムのユーティリティ関数を作成して、複数のLambda関数で共有したい場合に便利です。
注意: プラットフォーム依存ライブラリの問題
今回のプロジェクトにはcryptography
もインストールしていますが、これはプラットフォーム依存のライブラリです。
# src/lambda/layers/python/lib/python3.13/site-packages/ 内の構造
# Mac OS (Darwin)でインストールされたcryptographyの例
.
├── _cffi_backend.cpython-312-darwin.so # ← darwin.so (Mac OS用)
├── cffi
├── cffi-1.17.1.dist-info
├── cryptography
├── cryptography-45.0.5.dist-info
├── pycparser
└── pycparser-2.22.dist-info
_cffi_backend.cpython-312-darwin.so
のように、Mac OS用にコンパイルされたバイナリファイルが含まれています。これをそのままLambda(Linux x86_64)で実行するとImportErrorが発生します。
このような場合は、方法2(CDK Bundling)や方法5(Docker事前ビルド)を使用して、Lambda環境に合わせたバイナリをビルドする必要があります。
一方で、requests
やmyutils
のようなプラットフォーム非依存のライブラリであれば、この方法4でも問題なく動作します。純粋なPythonライブラリ(バイナリファイルを含まない)は、Mac OSでインストールしたものでもLambda環境で正常に使用できます。
メリット:
- 自作共通関数などの追加に向いている
- デプロイがDockerに依存しないため比較的早い
デメリット:
- 依存関係の管理を手動で行う必要あり
- プラットフォーム依存ライブラリは動作しない
方法5: Docker事前ビルド
「方法2や3でもDockerを使ってるのに何が違うの?」と思われるかもしれませんが、この方法は、複雑なビルド要件があるパッケージ、CI/CDパイプラインでアーティファクト管理したい場合やローカルでの動作確認を重視する場合に特に有効です。
例えばCI/CDパイプラインでライブラリを含むレイヤーを管理する場合、方法2・3ではCDKデプロイ時にビルドエラーが発生してパイプラインが止まる可能性があります。方法5なら事前にビルドを検証し、成功したアーティファクトのみをデプロイに使用できます。
方法2・3(CDK Bundling) | 方法5(Docker事前ビルド) | |
---|---|---|
実行タイミング | CDKデプロイ時に毎回Docker実行 | 事前にDocker実行してzipファイル作成 |
デプロイ時間 | 長い(毎回ビルド) | 高速(zipファイルをアップロードのみ) |
環境制御 | CDKのbundlingオプションに依存 | 完全に自由なDockerfile |
デバッグ | エラー時にCDKログを確認 | ローカルでDockerビルド結果を直接確認 |
# ファイル: docker/Dockerfile
# Amazon Linux 2023でPython 3.13をインストール
FROM amazonlinux:2023
# Python 3とzipをインストール
RUN dnf update -y && \
dnf install -y python3 python3-pip zip && \
dnf clean all
# 作業ディレクトリを設定
WORKDIR /tmp
# pythonディレクトリを作成してパッケージをインストール
RUN mkdir -p python && \
python3 -m pip install psutil -t python/ --no-cache-dir
# layer.zipを作成して権限設定
RUN zip -r layer.zip python/ && chmod 644 layer.zip
# /outputディレクトリを事前作成して権限設定
RUN mkdir -p /output && chmod 644 /output
# layer.zipをコピー
CMD ["sh", "-c", "cp layer.zip /output/"]
方法5の具体的な利点:
- ビルド結果の検証: デプロイ前にローカルでライブラリが正常に動作するか確認可能
- アーティファクト管理: zipファイルをGitやS3で管理し、チーム内で共有可能
- 高速デプロイ: 一度ビルドすれば、以降のデプロイは瞬時
- 完全制御: Amazon Linux環境を完全に再現し、必要なシステムツールを自由にインストール
# ローカルでビルド実行(linux x86_64環境に合わせる)
cd docker
docker build --platform linux/amd64 -t lambda-layer-builder .
docker run --rm -v $(pwd)/output:/output lambda-layer-builder
LayerVersion - AWS CDK
Code.fromAsset - AWS CDK
// ファイル: cdk/lib/cdk-lambda-python-layer-stack.ts
const dockerLayerPath = path.join(__dirname, '../../docker/output');
const dockerBuiltLayer = new lambda.LayerVersion(this, 'DockerBuiltLayer', {
code: lambda.Code.fromAsset(dockerLayerPath),
compatibleRuntimes: [lambda.Runtime.PYTHON_3_13],
compatibleArchitectures: [lambda.Architecture.X86_64],
});
メリット:
- デプロイが高速(事前ビルド済みzipファイルのアップロードのみ)
- ビルド環境を完全制御(Amazon Linuxの完全再現、必要なツール自由インストール)
- 確実な動作保証(事前にローカルで動作確認済み)
- CI/CD対応(CI/CDにてビルドを検証しアーティファクトとして管理可)
- デバッグ容易(CDKに依存しない)
デメリット:
- 事前準備が複雑
- ライブラリ更新時は手動でリビルドが必要
- Docker環境が必要
まとめ
今回の実装方法では以下のような使い分けを解説しました。
方法 | 使用場面 | 具体例 |
---|---|---|
ARN参照 | AWS公式やよく使われるライブラリ | pandas, PowerTools for AWS Lambda |
CDK Bundling | 軽量なサードパーティライブラリ | cryptography, pydantic |
aws-lambda-python-alpha | Pythonプロジェクトで簡潔に書きたい時 | 上記同様 |
手動準備 | 自作ライブラリを使用したい | 独自ユーティリティ |
Docker事前ビルド | CDKに依存せず、ローカルで検証したい時 | cryptography, pydantic, psutilなどプラットフォームに依存するライブラリ全般 |
最後に
意外と深ぼってみるとレイヤーを追加するだけでも様々な方法があり、シチュエーションに合わせて最適化された方法で無駄なく実装するにはCDK内部の実装とLambdaの仕組みまで勉強が必要だと感じました。今回は以上です。