AWS CDK + LocalStackでAWSサーバーレス環境をローカルに構築してみた

# はじめに


昨年度書いた[こちらの記事](https://qiita.com/yasomaru/items/fa708a1f21a79e637868)のLocalStackが2022年7月に正式リリースされたということで、今年のアドベントカレンダーもLocalStackについて書きます。
    今回はAWS CDK (AWS Cloud Development Kit)を使用し、LocalStack上にサーバレスアプリケーションを構築します。

https://localstack.cloud/

# 環境構築

    
:::note warn
LocalStackの環境構築については、[こちら](https://docs.localstack.cloud/getting-started/installation/#installation)を参照してください。
:::

## LocalStackの起動

LocalStackの起動方法はいくつかありますが、今回は以下のコマンドでLocalStackを起動します。


```sh
localstack start
```


以下コマンドでLocalStackの起動確認を行います。

```sh
curl http://localhost:4566/health
# 結果
{"features": {"initScripts": "initialized"}, "services": {"acm": "available", "apigateway": "available", "cloudformation": "running", "cloudwatch": "available", "config": "available", "dynamodb": "available", "dynamodbstreams": "available", "ec2": "available", "es": "available", "events": "running", "firehose": "available", "iam": "running", "kinesis": "available", "kms": "available", "lambda": "available", "logs": "available", "opensearch": "available", "redshift": "available", "resource-groups": "available", "resourcegroupstaggingapi": "available", "route53": "available", "route53resolver": "available", "s3": "running", "s3control": "available", "secretsmanager": "available", "ses": "available", "sns": "available", "sqs": "available", "ssm": "running", "stepfunctions": "available", "sts": "running", "support": "available", "swf": "available", "transcribe": "available"}, "version": "1.3.0.dev"}
```

以下のコマンドでも起動確認可能です。
```sh
localstack status services

#結果
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ Service                  ┃ Status      ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ acm                      │ ✔ available │
│ apigateway               │ ✔ available │
│ cloudformation           │ ✔ running   │
│ cloudwatch               │ ✔ available │
│ config                   │ ✔ available │
│ dynamodb                 │ ✔ available │
│ dynamodbstreams          │ ✔ available │
│ ec2                      │ ✔ available │
│ es                       │ ✔ available │
│ events                   │ ✔ running   │
│ firehose                 │ ✔ available │
│ iam                      │ ✔ running   │
│ kinesis                  │ ✔ available │
│ kms                      │ ✔ available │
│ lambda                   │ ✔ available │
│ logs                     │ ✔ available │
│ opensearch               │ ✔ available │
│ redshift                 │ ✔ available │
│ resource-groups          │ ✔ available │
│ resourcegroupstaggingapi │ ✔ available │
│ route53                  │ ✔ available │
│ route53resolver          │ ✔ available │
│ s3                       │ ✔ running   │
│ s3control                │ ✔ available │
│ secretsmanager           │ ✔ available │
│ ses                      │ ✔ available │
│ sns                      │ ✔ available │
│ sqs                      │ ✔ available │
│ ssm                      │ ✔ running   │
│ stepfunctions            │ ✔ available │
│ sts                      │ ✔ running   │
│ support                  │ ✔ available │
│ swf                      │ ✔ available │
│ transcribe               │ ✔ available │
└──────────────────────────┴─────────────┘
```


## CDKプロジェクトの作成

:::note warn
AWS CDKのバージョンはv2を使っています。
:::


```sh
mkdir （任意のディレクトリ名）
cd （作成したディレクトリ名)
npm install -g aws-cdk-local aws-cdk
cdklocal init --language typescript
```

CDKプロジェクトの作成は、`cdk`コマンドでも作成できますが、LocalStack用の`profile`を
準備必要があるため、今回は`cdklocal`コマンドを使用します。


https://docs.localstack.cloud/user-guide/integrations/aws-cdk/#installation


:::note alert
`cdklocal init`コマンドは空のディレクトリで実行する必要があります。
:::



# API GatewayとLambdaの構築

API GatewayとLambdaを利用したサーバレス環境を構築します。
API GatewayとLambdaの詳細については、以下を参照してください。

https://aws.amazon.com/jp/api-gateway/

https://aws.amazon.com/jp/lambda/

構成のイメージは以下のようになります。
![名称未設定ファイル.drawio (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/c6c14a0b-29e8-f0b9-dbfa-2f033400b4ee.png)




## ディレクトリ構成

```
.
├── README.md
├── bin              
├── cdk.context.json 
├── cdk.json        
├── cdk.out          
├── jest.config.js  
├── lib
|    ├──cdk-demo-stack.ts // API Gatewayやlambdaを作成
|    ├── lambda
|          ├── hello.js  //lambda関数
├── node_modules
├── package-lock.json
├── package.json
├── test
└── tsconfig.json
```


## API GatewayとLambdaの作成


API GatewayとLambdaを作成するCDKのソースコードは以下になります。

```typescript:cdk-demo-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as apigateway from "aws-cdk-lib/aws-apigateway";
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class CdkDemoStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // lambda作成
    const hello = new lambda.Function(this, 'HelloFunction', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'hello.handler',
      code: lambda.Code.fromAsset('lib/lambda'),
    });
    // API Gateway作成
    new apigateway.LambdaRestApi(this, 'APIEndpoint', {
      handler: hello,
    });
      }
}
```
次にLambda関数を実装します。
今回は簡単な「Hello World」を返すLambda関数を実装します。

```js:hello.js
exports.handler =  async function(event, context) {
    return {
        statusCode:200,
        headers: {'Content-Type': 'texy/plain'},
        body: 'Hello World'
    }
}
```



#  LocalStackへデプロイ

## デプロイ前の初期設定

デプロイする最初の一回のみ以下コマンドを実行します。このコマンドが正常に終了すれば、デプロイすることができます。

```sh
cdklocal bootstrap
```

以下コマンドでデプロイします。


```sh
cdklocal deploy
```

デプロイ完了すると、以下のようにURLが払い出されます。

```
 ✅  CdkDemoStack

✨  Deployment time: 6.3s

Outputs:
CdkDemoStack.MyEndpoint20B13C5B = https://232d1rbga1.execute-api.localhost.localstack.cloud:4566/prod/
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:000000000000:stack/CdkDemoStack/9822884c

✨  Total time: 19.76s
```



# 疎通確認

**上で払い出されたURLにアクセスしたくなりますが**、[公式ドキュメント](https://docs.localstack.cloud/aws/apigatewayv2/)に書かれているように、ローカル環境でアクセスする場合は以下のようになります。

```diff_shell
- 払い出されたURL：https://232d1rbga1.execute-api.localhost.localstack.cloud:4566/prod/
+ アクセスする場合のURL：http://localhost:4566/restapis/<apiId>/<stageId>/_user_request_/
```

- `apiId`
    - API Gatewayに割り当てられた識別子です。今回の場合、`232d1rbga1`となります。
- `stageId` 
    - API Gatewayのステージ名です。今回の場合、`prod`となります。


`_user_request_`は固定でそのまま含める必要があります。

::: note info
作成したAPI Gatewayを確認したい場合は以下コマンドを使用します。

```sh
aws apigateway get-rest-apis --endpoint-url=http://localhost:4566 --profile <LocalStack用のprofile>
```
:::


`curl`コマンドを使用すると、「Hello World」と返ってきます。
```sh
 curl http://localhost:4566/restapis/232d1rbga1/prod/_user_request_/
# 結果
Hello World
```


# おわりに

今回はAWS CDKとLocalStackを使用し、サーバレス環境を構築しました。
正式リリースされたということもあり`localStack`コマンドで起動や`cdklocal`コマンドなどLocalStackでの開発がしやすくなったと感じました。
AWSへの認証情報を環境変数等に設定しておくことで、ローカルとAWS上で同じソースコードで動作するので、ローカルでのテストに活用できそうです。
[公式ガイド](https://docs.localstack.cloud/user-guide/integrations/)にもあるようにLocalStackはクラウド関係の様々なツールをサポートしているので、興味のある方はぜひ試してみてください！





# 参考


https://docs.localstack.cloud/overview/


https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html


https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway-readme.html