AWSのFaaS(Function as a Service)であるLambdaは以前からよく使っているのですが、基本的にAWSコンソールの画面でコーディングするか、ローカルで普通のPythonプログラムとして作って(今回はTypeScriptで説明しますが、もともとはPythonで書くことが多いのです)、テストして、それからPythonパッケージとまとめてZipで圧縮してアップロードという方法でした。
CDKとSAMとは
今回は、AWSのCDK(Cloud Development Kit)と、SAM(Serverless Application Model)を使って、Lambda関数の開発環境をグレードアップしようと思います。
CDK
CDKはLambdaなどのAWSの各種サービスのデプロイを自動化するソリューションで、具体的にはTypeScriptなどの言語でデプロイ内容の定義と、Lambda関数自体をコーディングして、それをCloudFormationのテンプレートに変換してAWS上にデプロイするという働きをします。
AWSコンソールなどでいちいち操作してデプロイしていたのをやめて、自動化できるので、多くのデプロイ作業を人間が行う煩雑さを解消しますし、起きがちなミスをなくすことができます。いわゆるCD(Continuous Delivery)の文脈にあるツールといえるでしょう。
SAM
SAMは、その名のとおりサーバレスアプリケーションのためのフレームワークで、それ単体でLambda関数の開発からデプロイを行うことができます。開発したLambda関数をローカルで実行してテストすることも可能です。
ただ、CDKと違ってSAMはAWSの多くのサービスが使えるというわけではありません。
CDKとSAMを組み合わせる
CDKとSAMを組み合わせれば、CDKでLambda関数の開発やAWSの様々なサービスとの統合をまとめて管理しつつ、ローカルでのテストはSAMで行うということができます。
この記事は、それを試してみようというわけです。
CDKとSAMのインストール
まず、CDKをインストールします。Node.jsの環境が必要です。
npm i -g aws-cdk
次に、SAM(正確にはSAM-CLI)をインストールします。SAMでのローカルテストはDockerの環境が必要になるので、あらかじめDocker Desktopをインストールしておきます。
macOSではSAMはbrewでインストールできるのですが、私はASDFを使っているので、ASDFでインストールしました。
asdf plugin add aws-sam-cli
CDKを使い始める
プロジェクトディレクトリを作成します。
mkdir hello-cdk
cd hello-cdk
CDKプロジェクトを初期化します。今回はTypeScriptを使用します。
cdk init app --language typescript
こんな感じのファイルツリーができます。
.
├── README.md
├── bin
│ └── hello-cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── hello-cdk-stack.ts
├── node_modules
│ └── (省略)
├── package-lock.json
├── package.json
├── test
│ └── hello-cdk.test.ts
└── tsconfig.json
Lambda関数の開発
lib/hello.ts
を作成し、下記のようにします。POSTされたname
の値をHelloの後にくっつけるだけの簡単なプログラムです。API Gatewayにつなぐことが前提のコードになっています。
export const handler = async (event: any, context: any) => {
try {
let body = JSON.parse(event.body)
let name = 'world'
if ('name' in body) {
name = body.name
}
const responseBody = {
message: `Hello, ${name}!`
}
console.log('response:', responseBody)
return {
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(responseBody)
}
} catch (error) {
console.log('error:', error)
let body
if (error instanceof Error) {
body = error.stack
} else {
body = JSON.stringify(error, null, 2)
}
return {
statusCode: 500,
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
}
}
}
デプロイ内容の定義
lib/hello-cdk-stack.ts
を下記のように編集します。
import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
import { Runtime } from 'aws-cdk-lib/aws-lambda'
import { Duration } from 'aws-cdk-lib'
import { RestApi, LambdaIntegration } from 'aws-cdk-lib/aws-apigateway'
export class HelloCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const helloFunction = new NodejsFunction(this, 'helloFunction', {
entry: 'lib/hello.ts',
handler: 'handler',
runtime: Runtime.NODEJS_18_X,
timeout: Duration.seconds(30),
})
const helloApi = new RestApi(this, 'helloApi', {
restApiName: 'Hello API',
})
const helloApiResource = helloApi.root.addResource('api').addResource('v1').addResource('hello')
helloApiResource.addMethod('POST', new LambdaIntegration(helloFunction))
}
}
CloudFormationのテンプレートに変換します。(コード変更の度に実行する必要があります。)
cdk synth
テンプレート等のファイルは、./cdk.out
に作成されます。
ローカルでの実行
ここからがSAMの出番です。Lambda関数を直接実行するほか、API Gatewayのをローカルで動作させることも可能です。
Lambda関数
Lambda関数を直接実行する場合は、sam local invoke
を使用します。-t
オプションにCDKで作成されたCloudFormationのテンプレートを指定します。
--event -
とすると、echo
でパイプされた文字列(標準入力)がpayloadとして使用されます。--event ファイル名
のようにすると、JSONファイルを使用することができます。
echo '{"httpMethod":"POST", "path":"/", "body":"{\"name\":\"inoue\"}"}' | sam local invoke -t ./cdk.out/HelloCdkStack.template.json --container-host 127.0.0.1 --event - helloFunction
API Gateway
sam local start-api
のようにすると、API Gatewayをローカルでテストできます。
sam local start-api -t ./cdk.out/HelloCdkStack.template.json --container-host 127.0.0.1
curl -X POST -d '{"name":"inoue"}' http://127.0.0.1:3000/api/v1/hello
AWSへのデプロイ
最後に、AWSにデプロイしてみましょう。
cdk bootstrapの実行
AWSのアカウント、リージョン毎に一度だけcdk bootstrap
を実行する必要があります。(プロジェクト毎に必要なわけではありません。)
あらかじめAWS CLIをインストールして、aws config
で認証情報をセットしておきましょう。
cdk bootstrap aws://<ACCOUNT>/ap-northeast-1
ACCOUNTの情報は、下記で取得することができます。
aws sts get-caller-identity
デプロイ
cdk synth
した後に、cdk deploy
を実行します。
途中で確認メッセージが表示されるので、y
と入力します。
cdk deploy
API GatewayのエンドポイントURLが表示されるので、Curlで実行してみましょう。payloadはbodyの内容だけでOKです。
curl -X POST -d '{"name":"inoue"}' https://kzr7qo04cf.execute-api.ap-northeast-1.amazonaws.com/prod/api/v1/hello
クリーンアップ
cdk destroy
でクリーンアップできます。但し、cdk bootstrap
で実行された内容はクリーンアップされません。(同じアカウント、リージョンでデプロイする際に利用されます。)
cdk destroy