AWS CDKを使ったEC2 Instance Connect Endpointの実装

はじめに

プライベートサブネットにおいてあるEC2に対して、Instance Connect Endpointを使って接続するということを実施しています。

docs.aws.amazon.com

ただ、セキュリティグループの設定などを毎回誤ったりするので、同じミスを繰り返さないようにCDKをもって実装することにしました。

実装

まずは、aws-cdkのバージョンを最新に上げておきます。これは、EC2 Instance Connect Endpointをつくるためのコンストラクタを使えるようにするためです。この記事を書いている段階でaws-cdkのバージョンは2.130.0なので、npm -g install aws-cdk@2.130.0としておきます。

その後、cdk init app --language typescriptでテンプレートを作成し、libディレクトリ以下にできているTypeScriptファイルを編集します。

まずはVPCを作成します。サンプルとして、プライベートサブネットだけにします。

    const vpc = new Vpc(this, 'VPC', {
      maxAzs: 1,
      subnetConfiguration: [
        {
          name: 'private',
          subnetType: SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });

セキュリティグループを作成します。SSHだけを許可します。

    // EC2用のセキュリティグループ
    const securityGroupForEC2 = new SecurityGroup(this, 'SecurityGroupForEC2', {
      vpc,
    });
    // EIC用のセキュリティグループ
    const securityGroupForEIC = new SecurityGroup(this, 'SecurityGroupForEIC', {
      vpc,
      allowAllOutbound: false, // 指定のEC2のみに通信を許可するためfalseを指定
    });
    securityGroupForEC2.addIngressRule(securityGroupForEIC, Port.tcp(22));
    securityGroupForEIC.addEgressRule(securityGroupForEC2, Port.tcp(22));

Instance Connect Endpointを作成します。

    // EC2 Instance Connectを作成する
    new CfnInstanceConnectEndpoint(this, 'InstanceConnect', {
      subnetId: vpc.selectSubnets({ subnetType: SubnetType.PRIVATE_ISOLATED }).subnetIds[0],
      securityGroupIds: [securityGroupForEIC.securityGroupId],
    });

使用したL1コンストラクタのドキュメントは以下を。

docs.aws.amazon.com

実装が終わればcdk deployをすればOKです。

EC2の作成

EC2を作成します。ネットワークの設定で、サブネットを上記のCDKで作成したプライベートサブネットに配置することが注意点です。

確認

cdk deployを実行後、コンソールで確認します。

VPCが作成されています。

EC2の接続から「EC2 Instance Connect」を選択すると、エンドポイントも作成されていることが確認できます。

接続してみると、無事接続できます。

考察

CDKはAWSの更新に伴い、頻繁にバージョンアップされます。そのため、aws-cdkを定期的にバージョンアップしておかないと使用したいコンストラクタが使用できないということに陥ります。

Node.jsではnpm-check-updatesというパッケージが存在し、ncuコマンドでパッケージの更新有無が確認できるので、利用すると良いかなと思います。

www.npmjs.com

今回はEC2 Instance Connect EndpointをCDKを使って実装することをやりました。EC2 Instance Connect Endpointは簡単に設定できますが、VPCやサブネットごとに1つしか作成できないというクォーターが存在します(2024年2月現在)。

docs.aws.amazon.com

今回みたいなサンプルですと十分ですが、より複雑なネットワーク構成ですとEC2 Instance Connect Endpointのこのクォーター制限はかなり厳しいのかなと思われます。その際は別の接続手段を取ることが必要になると思われます。

Container Insightsを活用したECSクラスターの監視

はじめに

先日、Amazon Elastic Container ServiceにてSpring Bootで作ったアプリを動かすことをしました。

miyohide.hatenablog.com

今回は、このアプリに対してContainer Insights機能を試してみます。

Container Insightsとは

コンテナ化されたアプリのメトリクスやログを収集・集計・要約できるCloudWatchの機能のようです。詳細は以下を参照。

docs.aws.amazon.com

タスクやコンテナレベルでメトリクスやログを収集することが可能ということで、実際にやっています。

実装

実装と言っても、Amazon Elastic Container Serviceのクラスタを作成時にContainer Insightsのチェックを入れるだけです。

これだけでCloudWatchのContainer InsightsにてCPU使用率やメモリ使用率などが表示可能となります。

「コンテナマップ」ではクラスタのしたにサービスとタスク定義が表現されます。今回は大変単純なのであまりありがたみがわかりませんが、数が多くなると有用なのかもしれません。

リソースごとの表示をするとそれぞれのCPU利用率やメモリ使用率が表示できるので、これは有用なのかもしれません。

CloudWatchのロググループを見ると、「/aws/ecs/containerinsights/クラスタ名/performance」というロググループが作られていました。この中身がContainer Insightsの実態なのかなと思われます。

料金

お手軽に導入できるContainer Insightsですが、料金については以下のページを参照します。

aws.amazon.com

ただ、あまりわかりやすいものではないので、同ページにある例に目を通しておきます。この記事を書いているときには例12が該当します。

メトリクスの量に依存するので、以下のページに記載されているメトリクス一覧で数を確認します。

docs.aws.amazon.com

何かの方法で取得するメトリクスを選択できれば節約には良さそうなのですが、どうも設定できなさそうです。

メトリクスの月額コストは2024年2月現在、0.30USD(東京リージョン)です。結構高額です。

あわせてServiceが少なめのECSクラスターにて、Container Insightsをオンにして料金を確認すると良いかなと思います。

考察

Container Insightsの利用にアプリケーション上の設定などは必要ないので、利用できるものであれば利用すれば良いかなと思います。

唯一の懸念点は料金です。Serviceの数が増えると必然的にメトリクスの数が増えてしまうので、最初は少ないServiceしかないクラスターで有効化するとよいのかなと思います。

Spring BootのコンテナアプリをAmazon Elastic Container Serviceで動かす

はじめに

先日まではApp Runner上でコンテナアプリを動かすことをしていました。今日からは、Amazon Elastic Container Serviceを使ってX-Rayなどを試してみようかと思います。

まずはSpring Bootで作ったコンテナアプリをAmazon Elastic Container Serviceで動かすことをやってみます。

参考資料

Amazon Elastic Container Serviceを使うのは久しぶりだったので、何かよい題材はないか探してみます。「AWS Hands-on for Beginners シリーズ一覧」にAmazon Elastic Container Serviceのハンズオンがあったのでそれに目を通します。

pages.awscloud.com

このハンズオンではApache HTTPdを使ったコンテナアプリを動かしています。

Spring Bootアプリをコンテナ化する

まずはSpring Bootアプリをコンテナ化します。コンテナ化する方法は色々とあります。詳細は以下を参照してください。

spring.io

今回は、私が慣れているという理由でJibを使います。

github.com

コンテナ化については、上記のリンク先を参照してください。アプリは8080ポートで動きます。このポート番号が後々重要です。

Amazon Elastic Container Serviceの設定

Amazon Elastic Container Serviceの設定は上記ハンズオンをもとに実施します。コンソール画面が少々違いますが、2024年2月時点で実施しても十分通じる内容でした。

ハンズオンとの1番の違いはタスク定義です。Spring Bootで作ったコンテナアプリは8080ポートで動くので、「ポートマッピング」の「コンテナポート」に8080を設定することが注意点です。

これさえ間違わなければ、あとはハンズオン通りに進めていけばSpring Bootで作ったコンテナアプリは動きました。

考察

久しぶりにAmazon Elastic Container Serviceを触ることになり若干不安を感じましたが、AWSのハンズオン資料が充実していたのでかなり参考になりました。

aws.amazon.com

一方で、あくまでスムーズに行くように作られたハンズオンですので、いくつか説明が省略されているところやデフォルト値でうまく行くように設定されているところがあります。このため、ちょっと応用しようとするとつまづくところがあるのは注意点です。今回で言えばポートマッピングの部分でした。

これでSpring Bootで作ったコンテナアプリをAmazon Elastic Container Service上で動かすことができましたので、次からはX-Rayなどを組み込んでいきます。

App Runner上でのメトリックス監視のためのSpring BootとCloudWatchの設定

はじめに

先日、AWS App Runner上で動くコンテナ化したSpring Bootアプリケーションの可観測性に関する実装の一環でトレースをX-Rayに送ることを実装しました。

miyohide.hatenablog.com

ただ本番環境においてはトレースだけでは足りず、リクエスト数やJVMの状態などさまざまな情報が必要になります。App Runnerにおいては、リクエスト数などは特に何もしなくてもメトリックスタブから参照できます。

今回はJVMなどの各種情報をCloudWatchに送信するようにしてみます。

前提

今回の検証では以下のバージョンを使いました。

  • Java 21
  • Gradle 8.5
  • Spring Boot 3.2.2
  • Jib 3.4.0
  • AWS OpenTelemetry Agent 1.32.0

ライブラリの選定

Spring関連プロジェクトにて、AWSのマネージドサービスを扱うためのライブラリとしてSpring Cloud AWSというものがあります。

awspring.io

今回はこのSpring Cloud AWSを使います。

Spring Cloud AWSはSpring BootやSpring Frameworkのバージョンに応じて使用するバージョンが異なります。詳細は以下GitHubに記載があるので確認します。

github.com

今回はSpring Boot 3.2.2を使っているので、Spring Cloud AWS 3.1.xを使います。

実装

build.gradleでの実装方法はSpring Cloud AWSのドキュメントに書いている通りに従います。

docs.awspring.io

CloudWatchにメトリックスを送信するためには下記ドキュメントの通りio.micrometer:micrometer-registry-cloudwatch2も必要となります。

docs.awspring.io

具体的な実装は以下の通り。

// 省略
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    // Spring Cloud AWSを使用するための記述 ここから
    implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0")
    implementation 'io.awspring.cloud:spring-cloud-aws-starter'
    implementation 'io.micrometer:micrometer-registry-cloudwatch2'
    // Spring Cloud AWSを使用するための記述 ここまで
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    javaAgent('software.amazon.opentelemetry:aws-opentelemetry-agent:1.32.0')
}

CloudWatchにメトリックスを送信するためには以下のドキュメントを参考にしながらapplication.propertiesに設定を記述します。

docs.awspring.io

今回は以下のように記述しました。

management.cloudwatch.metrics.export.enabled=true
management.cloudwatch.metrics.export.namespace=app/micrometer

なお、ネット上ではSpring Cloud AWS 2.xの情報が目につくのですが、Spring Cloud AWS 2.xと3.xにて幾らか変更点があるようです。以下のMigration Guideを確認すると良いかと思われます。

docs.awspring.io

AWSの設定

AWS App RunnerからCloudWatchにメトリックスを送信するためには、cloudwatch:PutMetricData権限が必要となります。今回、AWS App Runnerに付与しているIAMロールにインラインポリシーにて追加しました。

動作

以上の内容で各種メトリックスがCloudWatchに送信されます。「カスタム名前空間」にmanagement.cloudwatch.metrics.export.namespaceで設定していた値のものが作られていることが確認できます。

中身を確認すると、いろいろな情報が送信されています。

Javaアプリケーションに欠かせないヒープなどの情報は「area,id」のところにでていました。

GCの情報などは「action,cause,gc」のところにありました。

考察

ライブラリと少しの設定で各種メトリックスをCloudWatchに送信できるのは非常に有用です。何かトラブルが発生した時に情報が足りないとトラブル解決ができなくなることもあるので、設定しておいて損はないかと考えます。

一方で、CloudWatchは無料で使えるわけではないです。

aws.amazon.com

料金を踏まえて、取るメトリックを制限したい場合はapplication.propertiesに以下の記述をします。

# いったん全てのメトリックスの送信を止める
management.metrics.enable.all=false
# JVM関連だけを送信する
management.metrics.enable.jvm=true

また、Spring Cloud AWSの設定が間違っていてもSpring Bootアプリケーション自身は正常に動くのも注意が必要です。正常に動いているのでメトリックスも取れているだろうと思い込まずに実際に画面を見た方が良いと考えます。

AWS App Runner上での可観測性向上に向けた取り組み(Spring Bootを使った実装例)

はじめに

SREとして語られるものの一つに可観測性というものがあります。単純にログを吐けば良いというものから、どのようなリクエストが行われているのかや各種メトリックスなどの情報を取得することが必要となってきています。ここではAWS App Runner上で動くコンテナ化したSpring Bootアプリケーションの可観測性に関する実装を行なっていきます。

前提事項

今回の検証では以下のバージョンを使いました。

  • Java 21
  • Gradle 8.5
  • Spring Boot 3.2.2
  • Jib 3.4.0
  • AWS OpenTelemetry Agent 1.32.0

アプリケーションの実装

まずは、ライブラリとしてAWS OpenTelemetry Agentを使用します。

aws-otel.github.io

build.gradleに以下の記述を追加します。

dependencies {
    // 省略
    javaAgent 'software.amazon.opentelemetry:aws-opentelemetry-agent:1.32.0'
}

javaAgentという記述は、Gradleのconfigurationを使って依存性の管理をするように宣言しておきます。

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    javaAgent
}

今回Jibを使ってコンテナイメージを作成するので、AWS OpenTelemetry Agentをコンテナイメージに取り込み、起動時にjavaagentオプションで指定する記述を追加してあげます。

task agentLibs(type: Copy) {
    from configurations.javaAgent.singleFile
    into 'myagent'
    rename 'aws-opentelemetry-agent-.*\\.jar', 'aws-opentelemetry-agent.jar'
}
jib.from.image = 'eclipse-temurin:21-jre'
jib.container.jvmFlags = ['-javaagent:./myagent/aws-opentelemetry-agent.jar']

jib {
    container {
        appRoot = '/app'
        workingDirectory = '/app'
        extraDirectories {
            paths {
                path {
                    from = './myagent'
                    into = '/app/myagent'
                }
            }
        }
    }
}

tasks.jibDockerBuild.dependsOn agentLibs

これでアプリケーション側の準備はOKです。

App Runnerの設定

App Runnerは2022年にX-Rayのサポートを開始しました。

aws.amazon.com

具体的な実装は、英語&Pythonの例ですが以下に記述があります。

aws.amazon.com

ポイントは、IAM Roleです。AWSXRayDaemonWriteAccessが付与されていないとX-Rayには何も表示されないので注意が必要です。

今回、Spring Boot 3.2を使うためにJava 21を使用しました。2024年1月時点でApp RunnerはJava 11までしかサポートしていないので、最新のSpring Bootを使いたい場合はアプリをコンテナ化する必要があります。

docs.aws.amazon.com

環境変数として以下を設定しておきます。

項目 備考
OTEL_PROPAGATORS xray
OTEL_METRICS_EXPORTER none App RunnerのOpenTelemetryサポートはメトリックスに未対応のため
OTEL_RESOURCE_ATTRIBUTES service.name=任意の名前

App Runnerの可観測性のトレースのスイッチをオンにすることも忘れずに。

実行

App Runnerにアプリをデプロイし、いくつかアクセスを行うとX-Rayにトレースやトレースマップが表示されます。

トレースには各リクエストごとにレスポンスコードや応答時間などが取られていることがわかります。

上はトレースマップ。単純な例なのであまり面白い結果ではないですが、どのようなサービスを利用しているかが図として表示されます。

考察

可観測性の重要性はよく語られますが、実際に実装しようとすると思いのほか面倒臭かったです。今回たまたまAWS OpenTelemetry Agentの存在を知り、動作させることを思い立ったのですが、Jibの設定が手間でした。

また、App RunnerのOpenTelemetryサポートはメトリックスに未対応ということもあったのが残念でした。ここについては、Spring Boot Actuatorで取れるのかなと思いますので、今後実装してみようかなと思います。

また、App RunnerがコードビルドとしてサポートしているJavaのバージョンが2024年1月時点では若干古い(Java 11まで)ので、最新のSpring Bootを使いたい場合はコンテナ化一択となる点も注意かなと考えました。

AWS CDKでリソースの構築とテストコードの書き方について

はじめに

最近はAWS CDKでリソースを構築することが多いのですが、AWS CDKは一般的なプログラミング言語AWSのリソースを定義するものなので、テストコードを書くことができます。そのテストコードの書き方についてまとめてみます。

前提条件

本記事は、以下の言語とAWS CDKのバージョンについて記しています。

  • TypeScript
  • AWS CDK 2.118.0

公式ドキュメント

AWS CDKの公式ドキュメントにテストについて記されたページがあるので目を通します。が、機械翻訳での提供なので、英語版を読んだ方が良いかもしれません。

docs.aws.amazon.com

テストのカテゴリ

上記公式ドキュメントに記されていますが、テストには以下2つのカテゴリがあるようです。

  • きめ細やかなアサーション(Fine-grained assertions)
    • AWS CDKが生成するCloudFormationの各種設定値が期待通りに生成されているかテストする
  • スナップショット(Snapshot test)
    • AWS CDKが生成するCloudFormationのテンプレートを以前のものと比較し、差分があるかをテストする

開発中は「きめ細やかなアサーション(Fine-grained assertions)」でテストを書いて、本番運用後は「スナップショット(Snapshot test)」を組み合わせる感じかなと考えました。

今回は「きめ細やかなアサーション(Fine-grained assertions)」の書き方について記します。

実装

cdk initにてプロジェクトを生成すると以下のようなサンプルテストコード込みで生成されるので、それを拡張するようにします。

// import * as cdk from 'aws-cdk-lib';
// import { Template } from 'aws-cdk-lib/assertions';
// import * as Cdk from '../lib/cdk-stack';

// example test. To run these tests, uncomment this file along with the
// example resource in lib/cdk-stack.ts
test('SQS Queue Created', () => {
//   const app = new cdk.App();
//     // WHEN
//   const stack = new Cdk.CdkStack(app, 'MyTestStack');
//     // THEN
//   const template = Template.fromStack(stack);

//   template.hasResourceProperties('AWS::SQS::Queue', {
//     VisibilityTimeout: 300
//   });
});

template変数を生成するまではいわゆるおまじないなので、ある程度コピペで。stack変数を生成するnew Cdk.CdkStack(app, 'MyTestStack');は適宜自分が生成したいStackに合わせて修正するぐらいです。

AWS CDKはCloudFormationを生成するので、CloudFormationの値が想定値と一致しているかどうかをテストするのが基本的な戦略になるかなと思います。AWS CDKのaws-cdk-lib.assertionsモジュールにて、各種Matcherの説明があるのでこれがテストコードの実装の拠り所になるかなと思います。

docs.aws.amazon.com

個人的には、以下のMatcherを使うことが多いです。

  • resourceCountIs
    • 対象のリソースが想定する数作成されるか
  • hasResourceProperties
    • 対象のリソースのプロパティが想定している値であるか
  • hasResource
    • 対象のリソースのプロパティ以外の設定(DeletionPolicyなど)の値が想定しているものか

繰り返しになりますが、AWS CDKはCloudFormationを生成するので、各種検証する値はCloudFormationのドキュメントも合わせて確認することになるかなと思います。

docs.aws.amazon.com

利用するサービスに対してCloudFormationの知識がないとテストも書きにくいと思います。この辺がちょっとハードルが高いかなと思いますが、一回ですべてを網羅するのは難しいので徐々に書き足す形が良いのかなと思っています。

なお、AWSの各種サービスは日々バージョンアップが行われ、AWS CDK自身も頻繁にバージョンアップされています。以下のページを見ると、1ヶ月に4回以上はザラにアップデートされています。

www.npmjs.com

CloudFormationのドキュメントにはあるのにAWS CDKにはない場合にはAWS CDKのバージョンアップを実施するとサクッと解決することが多いかなと思います。

実行

実行はnpm run testで実行できます。実行後、失敗するとどこで失敗したかがわかるように表示されます。ここらへんはJtestの機能です。

% npm run test

> cdk@0.1.0 test
> jest

 FAIL  test/cdk.test.ts
  ✕ ECRが1つ作成されること (82 ms)

  ● ECRが1つ作成されること

    Template has 1 resources with type AWS::ECR::Repository, but none match as expected.
    The 1 closest matches:
    Repository22E53BBD :: {
      "DeletionPolicy": "Delete",
      "Properties": {
    !!   Expected false but received true
        "EmptyOnDelete": true,
        "RepositoryName": "my-ruby-app"
      },
      "Type": "AWS::ECR::Repository",
      "UpdateReplacePolicy": "Delete"
    }

       9 |
      10 |   template.resourceCountIs('AWS::ECR::Repository', 1);
    > 11 |   template.hasResourceProperties('AWS::ECR::Repository', {
         |            ^
      12 |     RepositoryName: 'my-ruby-app',
      13 |     EmptyOnDelete: false,
      14 |   });

      at Template.hasResourceProperties (node_modules/aws-cdk-lib/assertions/lib/template.js:1:3101)
      at Object.<anonymous> (test/cdk.test.ts:11:12)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.696 s, estimated 2 s

成功すると、以下のように表示されます。

% npm run test            

> cdk@0.1.0 test
> jest

 PASS  test/cdk.test.ts
  ✓ ECRが1つ作成されること (83 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.712 s
Ran all test suites.

考察

AWS CDK自身は1ヶ月に複数回バージョンアップされています。クラウドの更新頻度を踏まえるとそれぐらいのバージョンアップは必然かなと思います。そのバージョンアップに追随するためにはテストコードを書くのは自然な発想と考えています。世の中、AWS CDKを使ってリソースを作ることは書かれていますが、テストコードの書き方は書かれていなかったので今回記事化しました。

実際には、AWS CDK、CloudFormation、Jestの知識がいるのでテストを書くとなるとハードルが高いなと感じます。ですが、一度に書き上げるのではなく徐々に作り上げていくとよいのかなと思いました。

ここで書いたのはあくまで基本的な記述方法で、上記のマニュアルにもいろんな検証方法が掲載されています。それらも実際の利用シーンとあわせて実装してみることが今後の課題です。

また、カバレッジを取得することはできないのかなと思ったりもしました。AWS CDKではそんなに複雑なことをしないケースがほとんどですのであまりニーズはないのかもしれません。

LambdaとRDSの連携をCDKで簡単実装する

はじめに

先日、Lambda上でコンテナイメージを動かしRDSに接続するということをやりました。

miyohide.hatenablog.com

このときは手作業でぽちぽちやりましたが、環境設定が面倒くさかったのでCDKで実装してみました。

VPCを作る

まずはVPCを作成します。こんな感じで。ちょっとでも料金節約のために、natGatewaysの数は1に設定します。

    // VPCの作成
    const vpc = new Vpc(this, "MyVPC", {
      enableDnsHostnames: true,
      enableDnsSupport: true,
      maxAzs: 2,
      natGateways: 1,
      subnetConfiguration: [
        {
          name: "PublicSubnet",
          subnetType: SubnetType.PUBLIC,
          cidrMask: 24,
          mapPublicIpOnLaunch: true,
        },
        {
          name: "PrivateSubnet",
          subnetType: SubnetType.PRIVATE_WITH_EGRESS,
          cidrMask: 24,
        },
        {
          name: "DBSubnet",
          subnetType: SubnetType.PRIVATE_ISOLATED,
          cidrMask: 24,
        }
      ]
    });

こんな感じでリソースが作られます。

作ってみて思いましたが、name属性にSubnetはいらなかったかなと思います。自動生成する名前にもSubnetがつくので冗長でした。

Lambdaを作る

コンテナイメージを作るLambdaを作成します。コンテナイメージはECRにあるので、こんな感じで実装します。

    // ECRのリポジトリを指定する
    const repository = Repository.fromRepositoryName(this, "MyRepository", "my-ruby-app");

    // Lambda用のセキュリティグループを作成する
    const lambdaSecurityGroup = new SecurityGroup(this, "LambdaSecurityGroup", {
      vpc: vpc,
      description: "Lambda Security Group",
      allowAllOutbound: true,
    });

    // ECRにあるコンテナイメージを利用してLambda関数を作成する
    const lambda = new Function(this, "Lambda", {
      code: Code.fromEcrImage(repository, {
        tag: "latest",
      }),
      functionName: "my-ruby-app",
      runtime: Runtime.FROM_IMAGE,
      handler: Handler.FROM_IMAGE,
      timeout: cdk.Duration.seconds(30),
      vpc: vpc,
      vpcSubnets: {
        subnetType: SubnetType.PRIVATE_WITH_EGRESS,
      },
      securityGroups: [lambdaSecurityGroup],
    });

既存のECRはRepository.fromRepositoryNameで取得できるので、それを使い、Functioncode属性にてCode.fromEcrImageにて対象のイメージを指定します。

RDSを作る

RDSを作成します。こんな感じで実装します。

    // RDS用のセキュリティグループの作成
    const rdsSecurityGroup = new SecurityGroup(this, "RDSSecurityGroup", {
      vpc: vpc,
      description: "RDS Security Group",
      allowAllOutbound: true,
    });

    const dbSubnetGroup = new SubnetGroup(this, "MyDBSubnetGroup", {
      vpc: vpc,
      description: "My DB Subnet Group",
      vpcSubnets: {
        subnetType: SubnetType.PRIVATE_ISOLATED,
        onePerAz: true,
      }
    });

    // RDSインスタンスの作成と設定を行う。今回はPostgreSQLを使用しているため、
    // DatabaseInstanceEngine.POSTGRESを指定する。
    const rdsInstance = new DatabaseInstance(this, "MyRDSInstance", {
      engine: DatabaseInstanceEngine.POSTGRES,
      instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO),
      vpc: vpc,
      databaseName: "mypostgresdb",
      multiAz: false,
      subnetGroup: dbSubnetGroup,
      securityGroups: [rdsSecurityGroup],
    });

上記のCDK内でRDSのパスワードなどは指定していませんが、これらはSecret Managerに自動的に登録されます。

前回でのプログラムでは環境変数経由でユーザー名やパスワードの取得をしていましたが、Secret Managerから読み取らせる必要があります。実装についてはまた後日。

RDSの作成は、そこそこ時間がかかるのでのんびり待ちます。

セキュリティグループの設定

セキュリティグループの指定は以下の実装で適切なものが実装されます。

rdsInstance.connections.allowDefaultPortFrom(lambda, "Lambda to RDS");

おまけ

これでCDKで前回の環境を実装することができました。今回はテスト用だったので、一度動作確認をしたらcdk destroyで削除しようとしたのですが、実際の削除には30分ぐらい時間がかかりました。主な要因は、Lambdaが生成するネットワークインターフェースが20分ぐらい使っているという状況のまま消えなかったため。あまり気にせずにのんびり待ちます。