はじめに
家計簿アプリのバックエンド資材を、
ECS(Fargate)からAWS Lambda + API Gatewayへ移行しました。
結果として、
- ALB
- ECS
- Public IPv4
などの常時課金を削減できました。
この記事では実際の移行手順だけでなく、
実際にハマったポイントについても紹介します。
システム構成
移行前
React
│
CloudFront
│
S3
│
ALB
│
ECS(Fargate)
│
MongoDB Atlas
移行後
React
│
CloudFront
│
S3
│
API Gateway
│
Lambda
│
MongoDB Atlas
なぜ移行したのか
ECSは便利ですが、
個人開発では
- ECS
- ALB
- Public IPv4
だけで毎月数十ドル程度かかります。
アクセス数が少ないサービスであれば、
Lambdaの従量課金の方が圧倒的に安くなります。
実施したこと
ExpressをLambda対応
Expressはそのまま使い、
serverless-http
を導入しました。
const serverless = require("serverless-http");
handler = serverless(app);
app.listen()を分離
ローカル起動
server.js
Lambda
lambda.js
に分離しました。
MongoDB接続を共通化
config/mongodb.js
を作成し、
LambdaやECSで共通利用できるようにしました。
Parameter Store
秘密情報は、
AWS Systems Manager Parameter Storeから取得するようにしました。
await loadParameters();
一番ハマったポイント①
Parameter Storeの読み込み順
最初は
const app = require("./app");
await loadParameters();
としていました。
しかし
const JWT_SECRET = process.env.JWT_SECRET;
がrequire時に評価されるため
JWT_SECRET = undefined
になり、Errorが発生しました。
最終的には
await loadParameters();
const app = require("./app");
に変更して解決しました。
一番ハマったポイント②
express-rate-limit
CloudWatchを見ると
ERR_ERL_UNEXPECTED_X_FORWARDED_FOR
というエラー。
原因は
app.set("trust proxy", 1);
を書いていなかったことでした。
API Gateway経由では必須でした。
一番ハマったポイント③
JWT期限切れ
CloudWatchを見ると
TokenExpiredError: jwt expired
実際はCookieではなくJWTの期限切れでした。
CloudWatch Logsを見る重要性を痛感しました。
CloudWatchLog
CloudWatchを見ることで
- JWT_SECRET未設定
- TokenExpiredError
- express-rate-limit
など原因を切り分けることができました。
動作確認
確認した項目
- ログイン
- CRUD
- 管理画面
- CSV
- MongoDB Atlas
- Cookie認証
- API Gateway
- Lambda
- CloudWatch
すべて正常動作を確認しました。
ECS停止
最後に
- ECS Service停止
- ALB削除
- Target Group削除
を行いました。
Cost Explorerを見ると
LoadBalancerUsage
と
PublicIPv4
の課金を止めることができました。
まとめ
Lambdaへ移行したことで
- コスト削減
- CloudWatchによるデバッグ
- CI/CD改善
が実現できました。
逆に学びとしては
- requireのタイミング
- Parameter Store
- JWT
- CloudWatch
の理解がかなり深まりました。
おわりに
最初は「Lambdaへ移行するだけ」と思っていましたが、
実際には
- AWS
- Express
- JWT
- CloudWatch
- GitHub Actions
まで含めて理解が深まりました。
個人開発でアクセス数が少ないサービスならECSよりLambdaの方が非常に相性が良いと感じています。