4
2

More than 1 year has passed since last update.

CDK で Hello Lambda

Posted at

小話

最近見つけた grape の「缶詰ウォーマー」が気になって仕方がないこの頃です。何より小鍋も嵌るサイズ感なのが良い。ミニおでんでも仕立てて 日本酒をしっぽり飲みながら夜を過ごすのもいいじゃないですか。

めっちゃ使ってみたい反面、お試しとばかりに色々な缶詰をスタックしそうで…。うーん、、気分は商品棚の前を何往復もウロウロしてるような感じです。

さて今回は缶詰をスタック、、、ではなく、AWS CDK を使って CloudFormation のスタックで管理された Lambda を作ってみるチュートリアルをしたいと思います。

対象者と前提条件

こんな方を対象にしています。

  • AWS を触っているけど構築を自動化したい
  • EC2 以外のことにチャレンジしたい
  • CloudFormation のテンプレート直書きや SAM を使ってるけど限界を感じてきた
  • Infra as Code (IaC) に興味がある

前提として既に AWS アカウントがあり、Node.js の環境が必要です。

CDK is 何?

公式の説明はこれです。

・・・要約すると

  • プログラム実行によって超ダイナミックな CloudFormation のスタック定義ファイル (JSON) を作成できる
    • VPC ID など既にある管理外リソースでも気軽に参照できて、関連付けできます
    • 開発モード・本番モードなどでの生成分け・名前変えも柔軟にできます
  • Lambda の実行コードを自動コンパイル&自動アップロード
  • ファイルをまとめて S3 バケットに自動アップロード
    • SPA のような Web アプリ、単純な HTML
    • 動的に生成した JSON ファイルもできる - クライアントID、環境で異なる API のエンドポイント、機能スイッチとか
  • コンテナのビルドをして Amazon ECR に自動登録

みたいな事をやってくれます。太字で書いたように CloudFormation のスタックが出来上がるのが最終ゴールになります。

準備

AWS のアクセス設定

コマンドラインから AWS へアクセスするので、IAM ユーザーでアクセスキーやシークレットを発行してローカルに設定する必要があります。

  • aws configure コマンドで設定する
  • または AWS_ACCESS_KEY, AWS_ACCESS_SECRET 環境変数を設定する
  • または saml2aws のようなツールでトークン認証する

設定方法は割愛します。

AWS CDK ツールキット

npm i -g aws-cdk
# または
yarn global add aws-cdk
# または
pnpm add -g aws-cdk

でインストールします。cdk --version と打ってバージョン番号が表示できれば OK です。

AWS のリージョン準備

CDK のデプロイ時に自動で諸々のファイルをアップロードなり登録をしてくれますが、それらを格納するための枠組みをデプロイ予定のリージョンに作っておく必要があります。これは cdk bootstrap を実行します。

cdk bootstrap aws://123456789012/ap-northeast-1
cdk bootstrap aws://123456789012/us-east-1

123456789012 部分はアカウント ID です。ブラウザーで AWS コンソールにサインイン後、右上のプロフィールをクリックしてドロップダウンすると確認できます。

ひとまず、東京リージョンと何かと入り用になるバージニア北部、合計 2 つのリージョンが準備できてれば十分です。

プロジェクト作り

AWS のワークショップ記事が参考になります。同じプロジェクト構造を作っていきます。

AWS CDK > TypeScript ワークショップ > プロジェクト構造

下ごしらえ

テンプレから生成

開発用に空のディレクトリを用意して TypeScript 用のテンプレートを作ります。

mkdir cdk-workshop
cd cdk-workshop

cdk init app --language=typescript --generate-only
npm i

NOTE: --generate-only を付けることで git リポジトリ化とパッケージの自動インストールをしなくなります。yarn や pnpm によるパッケージ管理をしたい場合におすすめです。

依存パッケージの整備

各パッケージのバージョンが古いので、適宜更新します。

npm outdated

npm i typescript@latest jest@latest ts-jest@latest
npm i @types/node@latest @types/jest@latest @types/prettier@latest

CDK 関連の重要なライブラリ 2 点 aws-cdk-libconstructs は最新が入るようですが、バージョン固定した方が無難です。

npm i -E aws-cdk-lib@latest
npm i -E constructs@latest

追加パッケージ

Lambda を使うのに便利な定義と、ビルドツールを入れます。

npm i -D @types/aws-lambda esbuild

NOTE: esbuild がローカルで見つからないと、自動で Docker を使たビルド環境内でビルドされます。

ソース

NodejsFunction を使って Lamda を作ります。ついでに 関数 URL を発行して、Web API としてアクセスできるようにしてみます。

スタックの構築

既存の cdk-workshop-stack.ts ファイルを以下の内容で丸ごと書き換えます。

lib/cdk-workshop-stack.ts
import { CfnOutput, Stack, StackProps } from 'aws-cdk-lib';
import { FunctionUrlAuthType, Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Construct } from 'constructs';
import { join } from 'path';

export class CdkWorkshopStack extends Stack {
  private helloUrl: string;

  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    this.buildHelloLambda();
    new CfnOutput(this, "HelloUrl", {value: this.helloUrl});
  }

  private buildHelloLambda() {
    const lambda = new NodejsFunction(this, "HelloLambda", {
      runtime: Runtime.NODEJS_16_X,
      entry: join(__dirname, "hello-lambda.ts"), // 実行するプログラム
      handler: "handler", // Lambda が呼び出されたときに実行する関数名 (既定は handler)
      environment: { // 環境変数として渡すパラメーター (process.env.XXX で参照できる)
        SERVICE_NAME: "Foo Bar"
      }
    });

    const funcUrl = lambda.addFunctionUrl({
      authType: FunctionUrlAuthType.NONE
    });
    this.helloUrl = funcUrl.url;
  }
}

Lambda の作成

NodejsFunction の entry で指定している hello-lambda.ts を新規作成します。
折角なので、環境変数の参照とリクエストで受け取った情報のうちソース IP アドレスを利用してみようと思います。

lib/hello-lambda.ts
import { APIGatewayProxyEventV2, APIGatewayProxyHandlerV2 } from "aws-lambda";

const handler: APIGatewayProxyHandlerV2 = async (event: APIGatewayProxyEventV2) => {
    const serviceName = process.env.SERVICE_NAME;
    const sourceIp = event.requestContext.http.sourceIp
    return {
        statusCode: 200,
        body: JSON.stringify({
            message: `ようこそ ${serviceName} へ!`,
            sourceIp: `接続元IPアドレスは '${sourceIp}' です`,
        }),
    };
};

export { handler };

デプロイ

cdk synth --quiet
cdk deploy

途中でデプロイの確認が入るので y を押して続行します。

Do you wish to deploy these changes (y/n)? y

成功するとデプロイの最後にある Output の項目に作っておいた HelloUrl が表示されます。

CdkWorkshopStack: deploying...
[0%] start: Publishing 1e5cdcd7531be9776328964c60cd8edfa8b49c660f1fba2b562af17751d4afb5:current_account-current_region
<<省略>>
CdkWorkshopStack | 6/6 | 10:06:12 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CdkWorkshopStack

 ✅  CdkWorkshopStack

✨  Deployment time: 80.55s

Outputs:  <<ココ☟>>
CdkWorkshopStack.HelloUrl = https://bcfw6ckna3qoxaairv4bgsowx40pjefx.lambda-url.ap-northeast-1.on.aws/
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:123456789012:stack/CdkWorkshopStack/c23ba720-5bdc-11ee-8ee0-0e3f14b4b7ef

✨  Total time: 88.65s

レッツアクセス!

ブラウザー等で生成された URL https://{ランダム}.lambda-url.ap-northeast-1.on.aws/ にアクセスします。
すると、ウェルカムメッセージとIPアドレスが返ってきます。

curl https://bcfw6ckna3qoxaairv4bgsowx40pjefx.lambda-url.ap-northeast-1.on.aws/
{"message":"ようこそ Foo Bar へ!","sourceIp":"接続元IPアドレスは '1.2.3.4' です"}

まとめ

AWS に何が生成されたか?

  • CloudFormation スタック にスタックが 2 個できてます
    • CDKToolkit - bootstrap で作られたもの
    • CdkWorkshopStack - 今回のサンプルで作ったスタック
      • リソースタブで Lambda 等の生成物を見れます
      • 出力タグで HelloUrl の出力値が見れます
  • Lambda アプリケーション
    • CdkWorkshopStack こちらでも見えます
  • Lambda 関数
    • CdkWorkshopStack-HelloLambda{ランダム}-{ランダム}
      • 作成した hello-lambda.ts が実行される Lambda です
      • 「設定>環境変数」で定義した SERVICE_NAME があるのを確認できます
  • IAM ロール
    • CdkWorkshopStack-HelloLambdaServiceRole{ランダム}-{ランダム}
      • Lambda にアタッチされるロールです
      • Lambda 側の「設定>アクセス権限」で確認できます

CDK でデプロイすることで以上のようなリソースが AWS に展開され、準備完了の状態まで一気にできました。便利ですね。

しかも、実際にはコードとして記述していない IAM ロールがあるように必要なものも適宜作ってくれます。
この辺りを AWS の膨大なリファレンス、時には長い英語のみの文章を読み込んで理解して正確に記述・連携するだけでも膨大な時間が掛かる作業です。それだけでも CDK を導入する価値があると私は思います。

スタック定義のソース内で、buildHelloLambda() のように生成するリソースのグループ・役割ごとに関数分けしておけるというのも便利です。もっと汎用化してライブラリ化・共通部品化する事も出来るわけです。夢が広がリングですね!

後始末

cdk destroy

でデプロイしたリソースを削除できます。または CloudFormation スタックから直接削除することでもできます。

缶詰ウォーマーどうしよっかな…。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2