小話
最近見つけた 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-lib
と constructs
は最新が入るようですが、バージョン固定した方が無難です。
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
ファイルを以下の内容で丸ごと書き換えます。
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 アドレスを利用してみようと思います。
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
があるのを確認できます
-
CdkWorkshopStack-HelloLambda{ランダム}-{ランダム}
-
IAM ロール
-
CdkWorkshopStack-HelloLambdaServiceRole{ランダム}-{ランダム}
- Lambda にアタッチされるロールです
- Lambda 側の「設定>アクセス権限」で確認できます
-
CdkWorkshopStack-HelloLambdaServiceRole{ランダム}-{ランダム}
CDK でデプロイすることで以上のようなリソースが AWS に展開され、準備完了の状態まで一気にできました。便利ですね。
しかも、実際にはコードとして記述していない IAM ロールがあるように必要なものも適宜作ってくれます。
この辺りを AWS の膨大なリファレンス、時には長い英語のみの文章を読み込んで理解して正確に記述・連携するだけでも膨大な時間が掛かる作業です。それだけでも CDK を導入する価値があると私は思います。
スタック定義のソース内で、buildHelloLambda() のように生成するリソースのグループ・役割ごとに関数分けしておけるというのも便利です。もっと汎用化してライブラリ化・共通部品化する事も出来るわけです。夢が広がリングですね!
後始末
cdk destroy
でデプロイしたリソースを削除できます。または CloudFormation スタックから直接削除することでもできます。
缶詰ウォーマーどうしよっかな…。