E2Eな開発をローカルで完結! AWS CDKを用いた開発環境の構築
このような前提条件を踏まえたうえで、齋藤氏たちが理想のローカル開発環境としたのが、フロントやAPIで分業しやすい環境ではなく、フロントからAPI、永続層までを含むE2Eな開発が行えることだった。
API GatewayとLambda部分のローカル開発環境について、齋藤氏はCDKのソースコードを公開し説明を行った。そのソースコードが次の図である。認証・認可のためのAuthorizer、Rest APIのためのAPI Gateway、APIのパスのまとまりとしてAPI Constructを定義。Constructの中ではさらにLambdaを定義し、API Gatewayのリソースとの紐付けを行う。
このAPI Gateway、Lambda部分のローカル環境は、AWS SAM CLIを利用し、次の3ステップで構築している。
第1ステップはcdk synthコマンドによる、CDKのコードからCloudFormationのテンプレートの出力である。ここではスタックごとの実行で時間を短縮するため、--exclusivelyオプションを指定し、スタック間の依存を無視し、対象のスタックのみsynthするようにしたり、各コマンドの末尾に&を記載し、バックグラウンドプロセスとして並列実行している。またスタックごとにテンプレートファイルを出力。APIスタックのテンプレートファイルであれば、API GatewayとLambdaの構成の起動のために利用されるなど、後続のステップで利用される。
第2ステップはsam buildコマンドによる、各Lambda関数のDockerイメージのビルド。LambdaのDockerイメージのビルドでは、--parallelオプションを指定し、並列実行により時間短縮できるようにしている。またDockerイメージはソースコードを含むため、ソースコードの変更のためにビルドする必要がある。「これを毎回手動で行うのは手間なので、VS Codeの『Run on Save』という拡張機能を利用し、ソースコード保存時にsam buildが実行されるようにすることで、擬似的にホットリロードを実現している」(齋藤氏)
第3ステップはsam local start-apiコマンドによる、API Gateway、Lambdaの構成を起動。標準出力されるログは読みづらく、永続化されないため、--log-fileオプションを利用しファイルへのログ出力を設定。また23年4月にリリースされたバージョンよりsam local start-apiコマンドがLambda Authorizerに対応。これにより、ローカルでもクラウドの環境に近い構成が再現可能できたという。
永続層であるDynamoDBのローカル環境構築については恋塚氏が説明してくれる。
DynamoDBのCDKソースコードは次のようになる。「DynamoDBStackというスタックを作り、その中でusersテーブルを定義する際は、TableとGSIに関する記述を行っています」(恋塚氏)
DynamoDBのローカル開発環境も自作スクリプトを利用し、次の3ステップで構築している。
- 公式のDockerイメージからDynamoDB Localを起動
- cdk synthコマンドを利用して、CDKのコードからCloudFormationのテンプレートを出力
- テンプレートを元に、自作スクリプトによるDynamoDB Localへのテーブル作成を実施
自作スクリプトはPythonで記述した約80行のシンプルなもの。「サードパーティのライブラリもあるが、スクリプト自体が小さく、メンテナンスされていない可能性もあるので、自分たちで自作して管理する方が柔軟性も得られるので、良いと判断しました」(恋塚氏)
実際にサービスを運用する中で、さまざまな課題にも直面したという。最初に直面したのが、CloudFormation Sackのリソース上限問題である。「半年の開発で1つのスタックに宣言できるリソースの上限である500に迫る勢いでAPIリソースが増えていった」と恋塚氏は明かす。この課題を解決するため恋塚氏が取った方法は「NestedStackを利用し、APIの親パスごとにスタックを分割すること」だった。スタック数がAPIの親パスレベルで増えてしまうというデメリットについては、ソースコードに少し変更を加えるだけで対応できたという。
次に直面した課題は、$sam local start-apiがNested Stackに対応していなかったことである。そのため、URIにおけるARNの解決ができず、ローカル環境が構築できなかった。
この課題に対しては、「CDKのソースコード上で、ローカル用とクラウド用でAPI Gatewayに紐付けるLambdaの指定方法に変更を加えることで解決しました」と恋塚氏。そうすることで出力されたテンプレートで、Fn::GetAttが使われなくなり、$sam local start-apiが意図通り動くようになったという。
その他にも恋塚氏はCDKとサーバレスアーキテクチャを使った開発におけるティップスを紹介。例えばコールドスタート対策として、よく利用するAPIについてプロビジョンドコンカレンシーを使用してウォームアップすることや、Lambdaアーキテクチャについては、AWS CDKスクリプト内で動的にアーキテクチャを決定し、開発者間の実行環境に依存しないようにすること、各開発者の専用クラウド環境については、stageという概念を用いて1つのAWSアカウント内に各開発者専用の開発環境を構築することなどを伝授した。
恋塚氏は最後に「AWS CDKとサーバレスアーキテクチャはかなり相性の良い組み合わせだと思っている。ぜひ、皆さんも機会があればトライしてほしいと思います」と呼びかけ、セッションを締めた。