はじめに
AWS CDKを使ってLambda関数をデプロイする際、ソースコードの管理方法に悩んだことはありませんか?
本記事では、Lambda関数のデプロイ用に別フォルダへコードをコピーして管理していた運用を見直し、元のソースコードを直接参照する方法に改善した事例を紹介します。この改善により、コードの二重管理が不要になり、メンテナンス性が大幅に向上しました。
AWS CDKとは
AWS CDK(Cloud Development Kit)は、プログラミング言語を使ってAWSのインフラをコードで定義できるフレームワークです。Python、TypeScript、Javaなどの言語でインフラを記述でき、CloudFormationテンプレートを自動生成してくれます。
Lambda関数をデプロイする際は、Code.from_asset()メソッドを使ってローカルのディレクトリを指定することで、そのディレクトリ配下のファイルがLambda関数としてデプロイされます。
今回の課題
従来の構成
プロジェクトでは以下のようなディレクトリ構成でLambda関数を管理していました。
project/
├── functions/ # 実際の開発用ソースコード
│ ├── commonUtils.py # 共通ユーティリティ
│ ├── functionProcessData.py # Lambda関数A
│ ├── functionGetData.py # Lambda関数B
│ └── functionPostData.py # Lambda関数C
│
└── cdk_deploy/
└── assets/ # デプロイ用コピー先
├── lambdaProcessData/
│ ├── commonUtils.py
│ └── functionProcessData.py
├── lambdaGetData/
│ ├── commonUtils.py
│ └── functionGetData.py
└── lambdaPostData/
├── commonUtils.py
└── functionPostData.py
問題点
この構成には以下の問題がありました。
-
二重管理の手間:
functions/フォルダで開発したコードを、デプロイ前に毎回assets/フォルダにコピーする必要がある - コピー忘れのリスク: コピーを忘れると古いコードがデプロイされてしまう(つまり、更新されない)
- ストレージの無駄: 同じコードが複数箇所に存在する
- メンテナンス性の低下: Lambda関数が増えるたびにコピースクリプトの修正が必要
解決策
改善後の構成
functions/フォルダのコードを直接参照するように変更しました。
project/
├── functions/ # 実際の開発用ソースコード
│ ├── commonUtils.py # 共通ユーティリティ
│ ├── functionProcessData.py # Lambda関数A
│ ├── functionGetData.py # Lambda関数B
│ └── functionPostData.py # Lambda関数C
│
└── cdk_deploy/
└── cdk/
└── lambda_stack.py # CDKスタック定義
assets/フォルダとdeploy_copy.pyは不要になりました。
実装方法
CDKのLambda Stackに、デプロイ時に必要なファイルを動的に集める処理を追加しました。
# lambda_stack.py
import os
import shutil
from aws_cdk import Duration, RemovalPolicy, Stack
from aws_cdk import aws_iam as _iam
from aws_cdk import aws_lambda as _lambda
from constructs import Construct
class LambdaFunctionStack(Stack):
"""Lambda関数をデプロイするCDKスタック"""
def __init__(self, scope: Construct, construct_id: str,
lambda_role: _iam.Role,
layer_version: object,
**kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
self.lambda_role = lambda_role
# Lambda関数のデプロイ
lambda_process_data = self.deploy_lambda_function(
"ProcessData",
layers=[],
role=self.lambda_role
)
def deploy_lambda_function(self, name, layers, role):
"""Lambda関数デプロイメソッド
:param name: 関数名(例: ProcessData)
:param layers: Lambdaレイヤー
:param role: IAMロール
:return: Lambda関数オブジェクト
"""
lambda_name = 'lambda' + name
function_name = 'function' + name
# functionsフォルダから直接参照するための一時ディレクトリを作成
asset_path = self._create_lambda_asset(function_name)
lambda_function = _lambda.Function(
self,
lambda_name,
handler=function_name + '.lambda_handler',
function_name=lambda_name,
architecture=_lambda.Architecture.ARM_64,
runtime=_lambda.Runtime.PYTHON_3_10,
code=_lambda.Code.from_asset(asset_path),
role=role,
timeout=Duration.seconds(30),
memory_size=1024,
layers=layers,
)
lambda_function.apply_removal_policy(RemovalPolicy.DESTROY)
return lambda_function
def _create_lambda_asset(self, function_name):
"""functionsフォルダから必要なファイルを集めた一時assetディレクトリを作成
:param function_name: 関数名(例: functionProcessData)
:return: assetディレクトリのパス
"""
# プロジェクトルートのfunctionsフォルダへのパス
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(current_dir, '..', '..'))
functions_dir = os.path.join(project_root, 'functions')
# cdk.outディレクトリ内に一時ディレクトリを作成
asset_dir = os.path.join(
project_root,
'cdk_deploy',
'cdk.out',
'assets',
function_name
)
os.makedirs(asset_dir, exist_ok=True)
# 必要なファイルをコピー
# 1. メインの関数ファイル
main_function = os.path.join(functions_dir, f'{function_name}.py')
if os.path.exists(main_function):
shutil.copy2(main_function, asset_dir)
# 2. 共通ユーティリティファイル
common_utils = os.path.join(functions_dir, 'commonUtils.py')
if os.path.exists(common_utils):
shutil.copy2(common_utils, asset_dir)
return asset_dir
ポイント解説
1. 一時ディレクトリの作成
cdk.out/assets/配下に関数ごとの一時ディレクトリを作成します。このディレクトリはCDKのビルド成果物として扱われ、デプロイ後も残るため、差分デプロイが効率的に行えます。
asset_dir = os.path.join(
project_root,
'cdk_deploy',
'cdk.out',
'assets',
function_name
)
os.makedirs(asset_dir, exist_ok=True)
2. 必要なファイルのみをコピー
各Lambda関数に必要なファイル(メイン関数ファイルと共通ユーティリティ)だけを一時ディレクトリにコピーします。
# メインの関数ファイル
main_function = os.path.join(functions_dir, f'{function_name}.py')
if os.path.exists(main_function):
shutil.copy2(main_function, asset_dir)
# 共通ユーティリティファイル
common_utils = os.path.join(functions_dir, 'commonUtils.py')
if os.path.exists(common_utils):
shutil.copy2(common_utils, asset_dir)
3. CDKのassetとして指定
作成した一時ディレクトリのパスをCode.from_asset()に渡すことで、CDKが自動的にLambda関数としてデプロイします。
code=_lambda.Code.from_asset(asset_path)
改善効果
この変更により、以下の効果が得られました。
-
二重管理の解消:
functions/フォルダのコードを直接参照するため、コピー作業が不要に -
デプロイの簡素化:
cdk deployコマンドだけでデプロイが完了 - コピー忘れのリスク排除: 常に最新のコードがデプロイされる
-
メンテナンス性の向上: 新しいLambda関数を追加する際も、
deploy_lambda_function()を呼び出すだけ
まとめ
AWS CDKでLambda関数をデプロイする際、ソースコードの二重管理を解消する方法を紹介しました。
従来はデプロイ用に別フォルダへコードをコピーしていましたが、CDKスタック内で一時ディレクトリを動的に作成することで、元のソースコードを直接参照できるようになりました。
この改善により、開発効率が向上し、コピー忘れなどのヒューマンエラーも防げるようになりました。同様の課題を抱えている方の参考になれば幸いです。