はじめに
AWS CDKってドキュメント見てもよくわからん...ってなったので、実際に動かしてみた。
すると、CDKすげええええぇってなったので、その感動を共有したくて記事を書いてみた。
対象とする読者
- AWS CDKとはなんぞやって人
- AWS CDKをお金をかけずに動かしてみたい人
AWS CDK のこれだけ知っておけばOK
AWSのIaC (コードでインフラを管理)といえば、CloudFormation!
→ しかし、以下の課題が・・・
- YAML/JSONなので、ループや条件分岐などを用いたリソース定義が書けない
- 大規模なインフラだと、一から十まで書くの大変
これらを解決するのが、AWS CDK!
→ CloudFormation用のテンプレートをプログラム言語から生成しちゃおう
- TypeScriptやPythonなどでリソースを定義するのでループなど自由自在
- ライブラリでリソースが抽象化されているので記述量が大幅減
(抽象化 = REST APIを作るといったパターンが用意されている)
AWS CDK を触ってみよう
何はともあれ、触ってみよう
やったこと
- AWS CDKの実行環境構築
- CDKが用意しているsample-app(SNS + SQS)の構築
- API Gateway + Lambdaの環境構築
筆者の環境
- macOS
- AWSアカウント、IAMユーザー作成済み
- Node.jsインストール済み
1. sample-app を読み解きそのままデプロイ
CDKが用意しているsample-appをそのままデプロイする。配備される資源は以下の通り。
1.1 事前準備
cdkコマンドが実行できるように、アクセスキーIDとシークレットアクセスキーを取得する。
- IAMユーザでAWSマネジメントコンソールにログイン
- IAMサービスに移動し、「自分の認証情報」を開く
- 「アクセスキーを作成」から、アクセスキーIDとシークレットアクセスキーを作成
- 以下内容のファイルを作成し、
~/.aws/credentials
として配置
[default]
aws_access_key_id = 【アクセスキーID】
aws_secret_access_key = 【シークレットアクセスキー】
[default]の部分は必要に応じて、profileを設定して使い分ける必要がある。
1.2 aws-cdkのインストール
$ npm install -g aws-cdk
1.3 プロジェクト作成 (cdk init
)
以下を実行して、プロジェクトを作成する。
$ mkdir cdk-workshop && cd cdk-workshop
$ cdk init sample-app --language typescript
上記を実行すると、いくつかのディレクトリとファイルが作成される。後述するcdk synthコマンドでテンプレートを作成する際に実行されるコマンドは、cdk.json
の以下の行である。
"app": "npx ts-node --prefer-ts-exts bin/cdk-workshop.ts",
したがって、まずはbin/cdk-workshop.ts
を確認してみる。
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdkWorkshopStack } from '../lib/cdk-workshop-stack';
const app = new cdk.App();
new CdkWorkshopStack(app, 'CdkWorkshopStack');
これを見ると、デプロイするスタックの内容はlib/cdk-workshop-stack.ts
に定義されていることがわかる。そこで、次にlib/cdk-workshop-stack.ts
を見てみる。
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';
export class CdkWorkshopStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const queue = new sqs.Queue(this, 'CdkWorkshopQueue', {
visibilityTimeout: Duration.seconds(300)
});
const topic = new sns.Topic(this, 'CdkWorkshopTopic');
topic.addSubscription(new subs.SqsSubscription(queue));
}
}
これを見ると、デプロイ時に以下が実施されるようだ。
- 可視性タイムアウト300秒の「CdkWorkshopQueue」というキューの作成
- 「CdkWorkshopTopic」というSNSトピックの作成
- 「CdkWorkshopTopic」のサブスクリプションとして「CdkWorkshopQueue」の追加
1.4 CloudFormationテンプレート作成 (cdk synth
)
次に以下コマンドを実行して、CloudFormaitonテンプレートを作成する。
$ cdk synth
すると、以下のファイルが作成される。
cdk.out/
├── CdkWorkshopStack.assets.json
├── CdkWorkshopStack.template.json <- CloudFormationテンプレート
├── cdk.out
├── manifest.json
└── tree.json
1.5 ブートストラップの実行 (cdk bootstrap
)
cdkのデプロイに必要な、S3バケット、IAMロール、SSMパラメータ等を作成する。
※cdkによるデプロイの初回のみ行う。
$ cdk bootstrap
1.6 デプロイ (cdk deploy
)
最後に以下コマンドを実行し、資源をデプロイする。
$ cdk deploy
以下のようにIAMポリシーの変更内容について確認されるので、「y」と入力する。
IAM Statement Changes
┌───┬─────────────────────────────┬────────┬─────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼─────────────────────────────┼────────┼─────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ + │ ${CdkWorkshopQueue.Arn} │ Allow │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": { │
│ │ │ │ │ │ "aws:SourceArn": "${CdkWorks │
│ │ │ │ │ │ hopTopic}" │
│ │ │ │ │ │ } │
└───┴─────────────────────────────┴────────┴─────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)?
2. API Gateway+Lambda環境を構築
2.1 スタック変更
以下のような環境を構築する。
ただし、そのままだとAPIを誰でも叩けてしまうので、APIキーと使用量プランによる利用制限をかける。
スタックの内容を以下のように変更する。
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Runtime, Code } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { LambdaRestApi } from 'aws-cdk-lib/aws-apigateway';
export class CdkWorkshopStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const helloLambda = new NodejsFunction(this, 'HelloHandler', {
runtime: Runtime.NODEJS_18_X,
entry: 'src/hello.ts',
handler: 'handler'
});
const helloApi = new LambdaRestApi(this, 'hello-endpoint', {
handler: helloLambda,
defaultMethodOptions: { apiKeyRequired: true },
proxy: false
});
const helloEndPoint = helloApi.root.addResource('hello');
helloEndPoint.addMethod('GET');
const apiKey = helloApi.addApiKey('ApiKey', {
apiKeyName: 'hello-api-key'
});
const plan = helloApi.addUsagePlan('UsagePlan', {
name: 'hello-api-usageplan'
});
plan.addApiKey(apiKey);
plan.addApiStage({ stage: helloApi.deploymentStage });
}
}
Lambda関数にデプロイするソースコードは以下の通り。
import {
APIGatewayEventRequestContextV2,
APIGatewayProxyEventV2,
APIGatewayProxyResultV2
} from 'aws-lambda';
export const handler = async (
event: APIGatewayProxyEventV2,
context: APIGatewayEventRequestContextV2
): Promise<APIGatewayProxyResultV2> => {
console.log({ event, context });
const responseBody = {
message: 'Hello World!'
};
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseBody)
};
};
2.2 cdk synth & cdk deploy
→ デプロイすると、先ほど構築したSNS+SQS環境が削除され、新しくAPI Gateway+Lambda環境が構築される。
cdk synthコマンドを実行すると Error: spawnSync docker ENOENT が出る場合
$ cdk synth
Error: spawnSync docker ENOENT
at Object.spawnSync (node:internal/child_process:1117:20)
at spawnSync (node:child_process:876:24)
at dockerExec (/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:1:3585)
at Function.fromBuild (/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4718)
at new Bundling (/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.js:1:3776)
at Function.bundle (/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.js:1:960)
at new NodejsFunction (/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/aws-lambda-nodejs/lib/function.js:1:1729)
at new CdkWorkshopStack (/Path/to/directory/cdk-workshop/lib/cdk-workshop-stack.ts:11:25)
at Object.<anonymous> (/Path/to/directory/cdk-workshop/bin/cdk-workshop.ts:6:1)
at Module._compile (node:internal/modules/cjs/loader:1356:14) {
errno: -2,
code: 'ENOENT',
syscall: 'spawnSync docker',
path: 'docker',
spawnargs: [
'build',
'-t',
'cdk-805ab1d42744d346e7ee81b097a37bd764cf7a67af41bcccaad6ccee384363e2',
'--platform',
'linux/amd64',
'--build-arg',
'IMAGE=public.ecr.aws/sam/build-nodejs18.x',
'--build-arg',
'ESBUILD_VERSION=0',
'/Path/to/directory/cdk-workshop/node_modules/aws-cdk-lib/aws-lambda-nodejs/lib'
]
}
NodejsFunctionでは、esbuildを用いてTypeScriptをビルドする。
macOSでは手動でインストールする方が良いらしく、以下を実施すると解消した。
参考:[備忘録] Error: spawnSync docker ENOENTの解消
$ npm i -D esbuild
2.3 APIのテスト
APIキーをヘッダーに渡して以下を実行
$ curl -X GET -H "x-api-key: APIキー" https://xxxx.execute-api.us-east-1.amazonaws.com/prod/hello
{"message":"Hello World!"}
ちなみに誤ったAPIキーを指定すると以下のようになる
$ curl -X GET -H "x-api-key: 誤ったAPIキー" https://xxxx.execute-api.us-east-1.amazonaws.com/prod/hello
{"message":"Forbidden"}
まとめ・所感
- AWS CDKはすごい!!ちょっとした検証を行うとき、ささっと環境構築できそう!!
- リソースの定義・連携の記述方法は、慣れないとちょっと難しい。
→ 結局公式のドキュメントが一番わかりやすいかも?
参考文献
以下の記事を参考にさせていただきました。ありがとうございます。