こんにちは!今年度は業務でPMを担当しているTatsuroです(決して偉くなったわけではない)。
今回は、AWS CDKのローカルテストについて調べてみたので紹介します。
戦略といえるほどのものではないかもしれませんが・・・
https://github.com/tatsurou9003/lt_aws-cdk
AWSのサービスは基本的にコンソール上で開発できます。ただ、ポチポチして開発するのは何かダサいし怠い・・・ということでAWS CDKに手を出してIaC化するわけです。そうなると勿論デプロイする前にローカルでデバッグ/テストしたくなってくるわけですが、本記事ではLocalStackおよびSAMを通じたスタックテストについて御紹介しようと思います。
LocalStackとは
LocalStackは、コンテナまたはCI環境で実行されるクラウドサービスエミュレーターです。LocalStack を使用すると、リモートクラウドプロバイダーに接続せずに、AWSアプリケーションやLambdaを完全にローカルマシン上で実行できます。複雑なCDKアプリケーションやTerraform構成をテストしている場合でも、AWSサービスについて学び始めたばかりの場合でも、LocalStackはテストと開発のワークフローを高速化し、簡素化するのに役立ちます。LocalStackは、AWS Lambda、S3、DynamoDB、Kinesis、SQS、SNSなど、多くのAWSサービスをサポートします・・・とのこと。要するにローカルテストにうってつけということです。
では実際に動かしてみましょう。
//docker-compose.yml
version: "3.8"
services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
image: localstack/localstack
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
- "127.0.0.1:4510-4559:4510-4559" # external services port range
environment:
- DEBUG=${DEBUG:-0}
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
まずはdocにしたがってLocalStackのコンテナを立ち上げます。
docker-compose up
コンテナ起動
npm install -g aws-cdk-local aws-cdk
pip install awscli-local
コンテナの起動を確認できたらAWS CLI、CDKとLocalStackを併せて使うために、awslocalとcdklocalをインストールしておきます。
cdklocal init -language python
ではcdklocalプロジェクトを作成します。
lib/lambda_handler.pyを追加するとこのようなディレクトリ構成になるはず
// cdk_local/cdk_local_stack.py
from aws_cdk import (
Stack,
Duration,
aws_lambda as lambda_,
aws_lambda_event_sources as lambda_event_sources,
)
from constructs import Construct
function_timeout = 3
function_memory_size = 128
class CdkLocalStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
lambda_function = lambda_.Function(self, "HelloWorldFunction",
code=lambda_.Code.from_asset("lib"),
handler="lambda_handler.lambda_handler",
runtime=lambda_.Runtime.PYTHON_3_8,
timeout=Duration.minutes(function_timeout),
memory_size=function_memory_size,
)
lambda_function.add_event_source(lambda_event_sources.ApiEventSource(
method="get",
path="/hello",
)
)
// lib/lambda_handler.py
import json
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({
"message": "Welcome to PlayGround",
}),
}
簡単なAPI GatewayとLambdaのスタックを定義
cdklocal bootstrap
お馴染みのコマンドでIAMロール・バケット等のスタックを作成
cdklocal deploy
コンテナにデプロイします。
awslocal lambda list-functions
{
"Functions": [
{
"FunctionName": "CdkLocalStack-HelloWorldFunction000000000",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:CdkLocalStack-HelloWorldFunction0000000000",
"Runtime": "python3.8",
"Role": "arn:aws:iam::000000000000:role/CdkLocalStack-HelloWorldFunctionServic-000000",
"Handler": "lambda_handler.lambda_handler",
"CodeSize": 282,
"Description": "",
"Timeout": 180,
"MemorySize": 128,
"LastModified": "2024-05-13T16:23:48.267109+0000",
"CodeSha256": "00000000000000000000000000",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "000000000000",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/CdkLocalStack-HelloWorldFunction000000000000"
}
}
]
}
awslocalコマンドを使ってdeployされているlambda関数をリスト表示すると、先ほどのLambda関数を確認できました。では早速API Gatewayのエンドポイントにアクセスしてみましょう。と思ったけど・・・
curl -X GET http://localhost:4566/restapis/{apiID}/test/_user_request_/hello
ドキュメントを見ると、apiIdなるものが必要らしいです。
awslocal apigateway get-rest-apis
{
"items": [
{
"id": "apiID", // こいつだ!
"name": "CdkLocalStackHelloWorldFunction:ApiEventSource",
"createdDate": "2024-05-14T01:23:53+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"EDGE"
]
},
"tags": {
"aws:cloudformation:logical-id": "CdkLocalStackHelloWorldFunctionApiEventSource",
"aws:cloudformation:stack-name": "CdkLocalStack",
"aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/CdkLocalStack/000000"
},
"disableExecuteApiEndpoint": false,
"rootResourceId": "00000000"
}
]
{"message": "Welcome to PlayGround"}%
先ほどのエンドポイントを叩くと・・・出てきました。
お次はSAM CLIでやってみましょう。
SAMとは
AWS SAMテンプレートは、サーバーレスアプリケーション用のIaCの定義に最適化された簡潔な構文を提供します。AWS CloudFormationの拡張機能として、SAMテンプレートをCloudFormationに直接デプロイできます。これにより、AWSでの広範なIaCサポートの恩恵を受けることができます。SAM CLIは、SAMの機能をすぐに使えるようにするデベロッパーツールです。これを使用すると、サーバーレスアプリケーションをすばやく作成、開発、デプロイできます。とのこと。早い話がLambda・API Gateway・dynamoDBのような構成に特化したCloudFormationということですね。
こいつの良いところは、CDKで作成したテンプレートを与えてやると、ローカルでLambda・API Gatewayを動かせるところです。
では実際に動かしてみましょう。
まずはSAM CLIをインストールしましょう。GUIで出来ます。
sam local invoke HelloWorldFunction --no-event -t ./cdk.out/CdkLocalStack.template.json
sam local invokeでDockerコンテナが立ち上がり、直接Lambda関数を呼び出せます。
Invoking lambda_handler.lambda_handler (python3.8)
Local image is up-to-date
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.
Mounting
/Users/tatsurom/Documents/aws-cdk-lt/cdk_local/cdk.out/asset.000000000000000 as /var/task:ro,delegated, inside runtime container
START RequestId: 78a999e2-47c3-4eac-b494-f9fdc01b148a Version: $LATEST
END RequestId: ###############
REPORT RequestId: ############# Init Duration: 0.69 ms Duration: 500.30 ms Billed Duration: 501 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"Welcome to PlayGround\"}"}
上手く呼び出せました。お次はAPI Gatewayを通して呼び出してみましょう。
sam local start-api -t ./cdk.out/CdkLocalStack.template.json
sam local start-apiでローカルにAPIが立ち上がります。
curl -X GET http://127.0.0.1:3000/hello
{"message": "Welcome to PlayGround"}%
返ってきました。
まとめ
いかがだったでしょうか。今回は、LocalStack, SAMを使ってAWSのサービスをローカルでテストしてみました。所感としては、サーバレス構成の場合は圧倒的にSAM CLIが楽チンだと感じました。LocalStackは汎用的なツールで、課金すると扱えるサービスが増えたり、より容易に扱えるようになるみたいです。
少し話は逸れますが、先日コミュニティ内でLT会を開催して登壇しました(この記事の内容も入ってた)。
30人近くの前で1時間半ぐらい喋って大変疲れました(どこがLightning Talkやねんという声は置いておいて)。
もっとコミュニティ内でAWSに興味を持ってくれる学生が増えたらいいなと思います。
それではまた次回の記事でお会いしましょう、さようなら👋