Zenn
🗃️

AWS CDK で実現するPython Lambda レイヤー構築アーキテクチャ

2025/03/21に公開

本記事では、AWS CDK を利用して Python ランタイムの Lambda 関数に必要な依存パッケージをLambda レイヤーとしてStack内で作成・管理し、Lambdaのレイヤーに紐づけまでをGUIやコマンド操作なしで実現する仕組みについてまとめます。レイヤーのビルドから S3 へのアップロード、そして各 Lambda 関数への割当てまでのプロセスをすべてコード管理することで、手動作業を一切排除し、CI/CD パイプラインに組み込みやすいアーキテクチャを実現しています。

プロジェクト概要

今回のプロジェクトは、以下の構成で管理されているS3へファイルをアップロードし公開できるシステムのレポジトリで行ったことを基にまとめています。具体的なコードは/lib/s3-public-contents-stack.tsを参照してください。

https://github.com/takoyaki-3/s3-public-contents/blob/main/lib/s3-public-contents-stack.ts

今回のLambda Layerに関係する部分は次の構成となっています。

.
├── bin/s3-public-contents.ts            // CDK アプリケーションのエントリーポイント
├── lib/s3-public-contents-stack.ts      // CDK スタック定義(レイヤー作成の仕組みを含む)
├── build-layer-lambda/index.py          // Lambda レイヤーをビルドする Lambda 関数
├── scripts/copy-html.mjs                // 補助的なビルドスクリプト
└── その他設定ファイル(.npmignore, tsconfig.json など)

本記事では、Lambda レイヤーの自動生成に焦点を当て、Python ライブラリ(例: requests, PyJWT, cryptography)を含むレイヤーのビルド・アップロードプロセスを、AWS CDK のカスタムリソースと Lambda 関数を組み合わせて実現する方法について説明します。

アーキテクチャの概要

レイヤー構築の自動化アーキテクチャは、主に以下のコンポーネントで構成されています。

  • レイヤー用 S3 バケット
    依存パッケージをビルドした ZIP ファイルを一時的に保存するための S3 バケット。CDK により自動的に作成され、Lambda 関数からの書き込み権限が付与されます。

  • レイヤー構築用 Lambda 関数
    build-layer-lambda として定義される Lambda 関数は、指定された Python パッケージのバージョンを受け取り、pip を利用してパッケージをビルドします。ビルドしたレイヤーパッケージを S3 にアップロードする役割を担います。

  • カスタムリソースプロバイダー
    AWS CDK のカスタムリソース機能を利用し、レイヤー構築用 Lambda 関数をトリガーします。これにより、CDK デプロイ時に自動で必要な Python ライブラリがパッケージングされ、S3 にアップロードされます。

  • Lambda レイヤーの作成
    カスタムリソースから返却された S3 のオブジェクトキー(ビルド済みの ZIP ファイル)をもとに、lambda.LayerVersion を作成。これにより、各 Lambda 関数で必要な依存パッケージを自動的に読み込めるようになります。

CDK によるレイヤー構築の自動化

1. レイヤー用 S3 バケットの作成

CDK スタック内で、レイヤーの ZIP ファイルを保存するための専用 S3 バケットを作成します。バケット名には一意性を持たせ、リソースの削除時には自動削除されるように設定しています。

const layerBucket = new s3.Bucket(this, 'LayerBucket', {
  bucketName: `${prefix}-layer-bucket-${this.stackName}`.toLowerCase(),
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  autoDeleteObjects: true,
});

2. レイヤー構築用 Lambda 関数の定義

Python 3.9 をランタイムとし、ARM アーキテクチャで動作する buildLayerLambda を作成します。タイムアウトを 5 分に設定しているため、パッケージのビルド時間を十分に確保できます。

const buildLayerLambda = new lambda.Function(this, 'BuildLayerLambda', {
  functionName: `${prefix}-build-layer-${this.stackName}`,
  runtime: lambda.Runtime.PYTHON_3_9,
  handler: 'index.handler',
  code: lambda.Code.fromAsset(path.join(__dirname, '../build-layer-lambda')),
  architecture: lambda.Architecture.ARM_64,
  timeout: cdk.Duration.minutes(5),
  memorySize: 1024,
});

また、Lambda 関数に対してレイヤー用 S3 バケットへの書き込み権限を付与します。

layerBucket.grantWrite(buildLayerLambda);

3. カスタムリソースプロバイダーの設定

aws-cdk-lib/custom-resources を利用して、上記の buildLayerLambda をカスタムリソースプロバイダーとして登録します。これにより、CDK デプロイ時に Lambda 関数が自動的に実行され、必要な Python パッケージのレイヤーがビルドされます。

const buildLayerProvider = new cr.Provider(this, 'BuildLayerProvider', {
  onEventHandler: buildLayerLambda,
});

4. Lambda レイヤーの作成

実際にレイヤーを生成するために、カスタムリソースの出力(ビルド済みの ZIP ファイルの S3 オブジェクトキー)を利用して lambda.LayerVersion を作成します。以下の関数 createPythonLayer は、指定したパッケージ名とバージョンを基にレイヤーを生成します。

const createPythonLayer = (id: string, description: string, packageName: string): lambda.LayerVersion => {
  // カスタムリソースを作成してレイヤーのビルドをトリガー
  const buildResource = new cdk.CustomResource(this, `Build${id}Resource`, {
    serviceToken: buildLayerProvider.serviceToken,
    resourceType: 'Custom::BuildSingleLayer',
    properties: {
      PackageName: packageName,
      OutputBucket: layerBucket.bucketName,
    },
  });
  
  // カスタムリソースから出力された S3 オブジェクトキーを取得
  const builtZipKey = buildResource.getAttString('OutputKey');
  
  // 取得した ZIP ファイルを元にレイヤーを作成
  return new lambda.LayerVersion(this, id, {
    code: lambda.Code.fromBucket(layerBucket, builtZipKey),
    compatibleRuntimes: [lambda.Runtime.PYTHON_3_9, lambda.Runtime.PYTHON_3_13],
    description: description,
  });
};

この仕組みにより、依存パッケージのバージョンアップや新規追加も、コード上の設定を変更するだけで自動的に反映されるため、手動でのパッケージング作業が不要となります。

5. 実際のレイヤー作成例

例えば、以下のようにして requestsPyJWTcryptography の各ライブラリを含むレイヤーを作成し、後続の Lambda 関数に割当てることができます。

const requestsLayer = createPythonLayer(
  'RequestsLayer',
  'Layer containing the requests library',
  'requests==2.32.3'
);

const jwtLayer = createPythonLayer(
  'JwtLayer',
  'Layer containing the PyJWT library',
  'PyJWT==2.10.1'
);

const cryptographyLayer = createPythonLayer(
  'CryptographyLayer',
  'Layer containing the cryptography library',
  'cryptography==44.0.2'
);

これらのレイヤーは、各 Lambda 関数の layers プロパティに渡すことで、依存パッケージを簡単に利用可能となります。

自動デプロイの全体フロー

  1. コードのビルド
    npm run build により、TypeScript のコンパイルとその他のビルドプロセスが実行され、最新の設定値が各ファイルに反映されます。

  2. CDK のデプロイ
    npx cdk deploy を実行すると、CDK スタックが作成・更新され、以下のリソースが自動でプロビジョニングされます。

    • レイヤー用 S3 バケット
    • レイヤー構築用 Lambda 関数
    • カスタムリソースプロバイダー
    • Python ライブラリを含む Lambda レイヤー
  3. レイヤーの自動作成
    カスタムリソースプロバイダーがトリガーされ、指定された各 Python パッケージのレイヤーが自動でビルドされ S3 にアップロードされ、Lambda 関数で利用可能な状態となります。

まとめ

今回紹介したアーキテクチャでは、AWS CDK とカスタムリソースを活用して、Lambda レイヤーのビルド、S3 への保存、Lambda 関数への自動割当を実現しました。
これにより、依存パッケージのパッケージング作業が完全に自動化され、コード管理下で一元的に更新できるため、運用の効率と再現性が大幅に向上します。
CI/CD パイプラインへの統合も容易になるため、頻繁な更新や環境の再構築が必要なプロジェクトにおいて大きなメリットを提供します。

Discussion

ログインするとコメントできます