【AWS】 CDK V2でAPI Gawate + Lamabda + EIPを構築する

やりたいこと

以下の図のようなAPI Gatew + Lambdaで外部APIから値を取得し、加工してクライアントに返すAPIをAWS CDK V2を使って作ります。利用する外部APIはアクセス元IPアドレスによりアクセス制限を行うため、LambdaをVPC内に配置しEIPを付与したNat Gateway経由で外部APIを呼び出します。

システム構成図

AWS CDKのインストール

AWS CDKをインストールします。

# インストール
$ npm install -g aws-cdk

# インストール後にバージョン確認
$ cdk --version

プロジェクト作成

AWS CDKを利用する準備としてブートストラップを行います。ブートストラップによりAWS CDKによるデプロイに必要なファイルをアップロードするために必要なS3などが作成されます。

ブートストラップ後にプロジェクトディレクトリを作成し、プロジェクトの初期化を行います。この例では言語はTypeScriptを選択しています。

$ aws-vault exec {プロファイル名} -- cdk bootstrap aws://{アカウントID}/{リージョン名}
$ mkdir test-project
$ cd test-project
$ cdk init --language typescript

AWSリソースの作成

CDKのスタック部分のコードは以下のようになります。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {aws_apigateway, aws_ec2, aws_lambda, Duration} from "aws-cdk-lib";
import { PythonFunction } from "@aws-cdk/aws-lambda-python-alpha";
import * as path from "path";

export class TestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC
    const vpc = new aws_ec2.Vpc( this , 'test-vpc', {
      cidr: '10.1.0.0/16',
      defaultInstanceTenancy: aws_ec2.DefaultInstanceTenancy.DEFAULT,
      enableDnsSupport: true,
      enableDnsHostnames: true,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'test-public-subnet',
          subnetType: aws_ec2.SubnetType.PUBLIC
        },
        {
          cidrMask: 24,
          name: 'test-private-subnet',
          subnetType: aws_ec2.SubnetType.PRIVATE_WITH_EGRESS
        }
      ],
      natGateways: 1
    });

    // Lambda
    const testFunction = new PythonFunction(this, 'TestFunction', {
      functionName: 'TestFunction',
      handler: 'handler',
      runtime: aws_lambda.Runtime.PYTHON_3_8,
      entry: path.resolve('lambda/function/src/main'),
      memorySize: 128,
      timeout: Duration.seconds(60),
      vpc: vpc,
      vpcSubnets: {
        subnetType: aws_ec2.SubnetType.PRIVATE_WITH_EGRESS
      }
    });

    // API Gateway
    const testApiGateway = new aws_apigateway.RestApi(this, 'TestApi', {
      restApiName: 'test-api',
      deployOptions: {
        stageName: 'dev'
      },
      defaultCorsPreflightOptions: {
        allowOrigins: aws_apigateway.Cors.ALL_ORIGINS,
        allowMethods: aws_apigateway.Cors.ALL_METHODS,
        allowHeaders: aws_apigateway.Cors.DEFAULT_HEADERS
      }
    });

    // Lambda統合
    testApiGateway.root.addProxy({
      defaultIntegration: new aws_apigateway.LambdaIntegration(testFunction),
      anyMethod: true
    });
  }
}

VPC

Lambdaを配置するためのプライベートサブネットとNat Gatewayを配置するためのパブリックサブネットを作成します。Nat Gateway経由で内部からのみインターネットへアクセス可能なサブネットを作成するためにsubnetTypeにPRIVATE_WITH_EGRESSを指定します。

Nat Gatewayはパブリックサブネットに配置されるため、パブリックサブネットを作成する必要があります。特に指定しなくてもNat Gatewayに対してはEIPが付与されます。

Lambda

@aws-cdk/aws-lambda-python-alpha パッケージのPythonFunctionを使用すると、外部パッケージを使用したLambdaの作成が簡単になります。Lambda関数のソースコードを格納したディレクトリ内でpoetryプロジェクトを作成し、使いたいパッケージを追加しておけばCDKのデプロイ時に外部パッケージを含めたLambda関数が作成されます。

@aws-cdk/aws-lambda-python-alphaはAWS CDK 2.49.0時点でEXPERIMENTALですのでご注意ください。

# CDKプロジェクト内でのLambda関数のソースコードディレクトリへ移動
$ cd lambda/function/src/main

# Poetryプロジェクト作成
$ poetry init

# requestsパッケージを追加
$ poetry add requests

Lambdaの配置先サブネットをプライベートサブネットとします。

API Gateway

今回はAPI Gatewayのリソースはプロキシリソースとして作成しています。また、API Gatewayで受け取ったリクエストをLambdaで処理するためのプロキシ統合を作成します。

参考情報