はじめに
AWSの学習の一環として、既存のWebアプリケーションをAWS環境へ移行する取り組みを行っています。
これまでAWS CDKを使用して各種リソースの構築を進め、VPCの概要や構築についても記事にしてきました。
ただ、構築手順を振り返る中で、そもそも「なぜ今回のAWS構成を採用したのか」という部分を十分に整理できていなかったため、今回は少し前の段階に戻り、構成を検討した際の考え方をまとめてみます。
まずは複数のAWS構成案を比較し、それぞれの特徴やメリット・デメリットを整理しました。
その結果、フロントエンドをS3とCloudFrontで配信し、バックエンドをLambdaで実行するサーバーレス構成を採用しました。
今回は、その検討の流れと各AWSサービスを選択した理由を整理します。
なお、この記事は実際の構築手順ではなく、構成検討の内容を中心とした備忘録になります。
1. 移行対象のWebアプリケーション
今回AWSへ移行するWebアプリケーションは、以下の技術を使用しています。
| 項目 | 使用技術 |
|---|---|
| フロントエンド | Nuxt 4 |
| バックエンド | Fastify(Node.js) |
| ORM | Prisma |
| データベース | PostgreSQL |
フロントエンドはNuxt 4を使用しており、ssr: false を設定したSPAとして動作します。
そのため、ビルド後の静的ファイルをS3から配信する構成を採用できます。
バックエンドにはFastifyを使用し、フロントエンドからのAPIリクエストを処理します。
また、Prismaを使用してPostgreSQLへのアクセスを行います。
2. AWS構成を検討するうえで考えたこと
AWS構成を検討するにあたり、今回は以下の点を重視しました。
- 既存アプリケーションを大きく変更せずに移行できること
- AWSの主要なサービスについて学習できること
- できるだけ不要なランニングコストを抑えること
- AWS CDKを使用して構成をコードで管理できること
- 将来的にCI/CDを導入できること
特に今回はAWSの学習も目的としているため、単純にアプリケーションを動かすだけでなく、各サービスの役割を理解しながら構築できる構成を目指しました。
また、小規模なWebアプリケーションを対象としているため、最初から大規模な構成を用意するのではなく、必要なリソースを段階的に追加する方針としました。
3. 最初に検討したAWS構成
まず、既存アプリケーションをAWSへ移行する場合にどのような選択肢があるのか、構成案を比較しました。
このとき、以下の4つの案を検討しました。
| 構成案 | 主な特徴 | 検討ポイント |
|---|---|---|
| App Runner + S3/CloudFront | マネージドなWebアプリ実行環境 | サーバー管理の負担を抑えやすい |
| ECS Fargate + ALB + S3/CloudFront | コンテナを利用した構成 | 柔軟な設定が可能だが、構成要素が増える |
| Amazon Lightsail | 比較的シンプルな料金体系 | 小規模環境や検証用途で利用しやすい |
| Lambda + Web Adapter | サーバーレス構成 | アクセスが少ない場合に実行コストを抑えやすい |
それぞれの特徴について簡単に整理します。
案① App Runner + S3/CloudFront
App Runnerは、WebアプリケーションやAPIを比較的簡単にデプロイできるマネージドサービスです。
ソースコードやコンテナイメージからアプリケーションを実行でき、インフラの管理負荷を抑えられる点が特徴です。
今回のアプリケーションであれば、フロントエンドをS3とCloudFrontで配信し、バックエンドをApp Runnerで実行する構成が考えられます。
メリットとしては、EC2のようにOSを直接管理する必要がなく、アプリケーションの実行環境をAWS側で管理できることが挙げられます。
一方で、VPC内のRDSへプライベート接続するにはVPCコネクタなどの設定が必要となるため、ネットワーク構成をまったく考えなくてよいわけではありません。
また、構成によってはリクエストがない時間帯にも費用が発生するため、利用状況に応じたコスト比較が必要です。
案② ECS Fargate + ALB + S3/CloudFront
ECSはコンテナ化したアプリケーションを管理・実行するサービスです。
Fargateを使用すると、EC2インスタンスを自分で管理せずにコンテナを実行できます。
今回の構成では、フロントエンドをS3とCloudFrontで配信し、バックエンドをECS Fargateで実行する案です。
コンテナ単位でアプリケーションを管理できるため、実行環境を統一しやすく、柔軟な構成を取りやすい点がメリットです。
一方で、コンテナイメージの管理やECSのタスク・サービス、ALBなど、追加で理解する必要がある要素もあります。
また、常時稼働するECSサービスとして構築した場合は、アクセスが少ない時間帯でもタスクの稼働コストが発生します。
案③ Amazon Lightsail
Lightsailは、仮想サーバーやコンテナ、データベースなどを比較的シンプルな料金体系で利用できるサービスです。
AWSの各サービスを細かく組み合わせる構成と比較して、少ない設定項目で環境を用意しやすい点が特徴です。
今回のアプリケーションであれば、Lightsailインスタンス上でNginxとFastifyを動作させ、データベースにはLightsailのマネージドデータベースを利用する構成などが考えられます。
案④ Lambda + Web Adapter
Lambdaは、サーバーを自分で管理せずにプログラムを実行できるサービスです。
AWS Lambda Web Adapterを利用すると、既存のHTTPサーバー型アプリケーションをLambdaで実行する方法もあります。
今回のFastifyのようなNode.jsアプリケーションでも、Lambdaへの移行方法の選択肢になります。
メリットとしては、リクエストに応じて処理を実行できるため、アクセスが少ない時間帯のコンピュート費用を抑えやすい点があります。
一方で、Lambdaには実行時間の制限やコールドスタート、データベース接続数など、特有の注意点があります。
4. 最終的に採用したAWS構成
これまでの構成案を比較した結果、今回のアプリケーションでは、フロントエンドとバックエンドを分離したサーバーレス構成を採用することにしました。
フロントエンドがSPAとして動作すること、バックエンドがAPIを中心とした構成であること、サーバーの運用負担やランニングコストを抑えたいことを重視しました。
最終的に採用したAWSサービスは以下のとおりです。
| サービス | 役割 |
|---|---|
| S3 | フロントエンドの静的ファイルを保存 |
| CloudFront | フロントエンドの配信とAPIリクエストの振り分け |
| API Gateway | バックエンドAPIの入口 |
| Lambda | Fastifyによるバックエンド処理 |
| RDS | PostgreSQLのデータを管理 |
| VPC | LambdaやRDSが利用するネットワーク |
※それぞれの詳しい解説は今後作成する各回の記事で実施します。
全体構成図
※ 上記は採用した構成の概要図です。実際のリソース配置やネットワーク設定の詳細は省略しています。
フロントエンドをS3 + CloudFrontにした理由
今回のNuxtアプリケーションはSPAとして動作するため、ビルド後の静的ファイルを配信するだけであれば、Node.jsのサーバーを常時起動しておく必要はありません。
そのため、S3に静的ファイルを配置し、CloudFrontを経由して配信する構成を採用しました。
これにより、フロントエンドの配信だけを目的としたEC2インスタンスを用意する必要がなくなります。
また、S3への直接アクセスを制限し、CloudFront経由で配信する構成としています。
バックエンドをAPI Gateway + Lambdaにした理由
バックエンドはAPIリクエストに応じて処理を実行する構成であるため、Lambdaを採用しました。
Lambdaを利用すれば、EC2のようにサーバーを常時管理する必要がなく、実行回数や実行時間などに応じた課金となります。
また、API Gatewayと組み合わせることで、HTTPリクエストを受け付けるバックエンドAPIを構成できます。
ただし、Lambdaには実行時間の制限やコールドスタート、データベース接続数などの注意点があります。
そのため、今回の構成ではLambdaを採用しましたが、アプリケーションの要件によってはECSなどの利用も選択肢になると考えています。
フロントエンドの通信経路
フロントエンドは以下の経路で配信します。
ユーザー → CloudFront → S3
ユーザーがWebアプリケーションへアクセスすると、CloudFrontを経由してS3に配置したHTML、JavaScript、CSSなどの静的ファイルが配信されます。
バックエンドの通信経路
バックエンドは以下の経路で処理する構成です。
ブラウザ → CloudFront → API Gateway → Lambda → RDS PostgreSQL
CloudFrontでは、API向けのパスをAPI Gatewayへ振り分ける設定を行っています。
API Gatewayがリクエストを受け取ると、Lambdaでバックエンド処理を実行します。
データベースへのアクセスが必要な場合は、Prismaを使用してRDS PostgreSQLへ接続します。
VPCとサブネット
今回の構成では、デフォルトVPCを変更せず、新規VPCを作成する方針としました。
VPC内には、2つのAvailability ZoneにまたがるPrivate Isolated Subnetを作成しています。
LambdaとRDSは、このプライベートなネットワークを利用する構成としています。
また、LambdaからRDSへの接続については、Security Groupを利用して通信を制御しています。
なお、S3、CloudFront、API Gatewayなど、すべてのAWSサービスがVPC内に配置されるわけではありません。
5. Lambdaを採用するうえで注意したこと
Lambdaはサーバー管理の負担を減らせる一方で、従来の常駐型アプリケーションとは異なる点があります。
今回のアプリケーションでは、特にデータベース接続について考慮する必要がありました。
Lambdaとデータベース接続
Lambdaでは、リクエストの増加に応じて複数の実行環境が起動する場合があります。
それぞれの実行環境からRDSへ接続すると、Lambdaの同時実行数に応じてデータベース接続数が増加する可能性があります。
今回のバックエンドではPrismaを使用しているため、DB接続プールの設定も考慮しました。
具体的には、接続プールの上限を環境変数で指定できるようにしています。
const dbPoolMax = Number(process.env.DB_POOL_MAX);
if (!Number.isSafeInteger(dbPoolMax) || dbPoolMax < 1) {
throw new Error('DB_POOL_MAX must be a positive integer');
}
上記は環境変数の値を検証する処理であり、実際の接続プール上限の設定は、DB接続処理側で行います。
また、Lambda側では以下のように設定しています。
DB_POOL_MAX: '1',
これにより、DB接続処理側でこの値を使用することで、各実行環境の接続プール上限を設定できます。
ただし、この設定だけでLambda全体からの接続数を1に制限できるわけではありません。
実行環境が増えると接続数も増加する可能性があるため、必要に応じて同時実行数の制御やRDS Proxyなども検討する必要があります。
今回は、Lambdaを利用することで新たに考慮すべき点があることも学びました。
6. AWS CDKを採用した理由
今回のAWS環境は、AWS CDKを使用して構築する方針としました。
AWS CDKは、TypeScriptなどのプログラミング言語を使用してAWSのインフラ構成を定義できる仕組みです。
AWSマネジメントコンソールから手動でリソースを作成する方法もありますが、CDKを使用することで以下のメリットがあります。
- インフラ構成をコードとして管理できる
- GitHubで変更履歴を確認できる
- 同じ構成を再現しやすい
- デプロイ前に変更差分を確認できる
- CI/CDとの連携がしやすい
CDKで記述した内容はCloudFormationのテンプレートに変換され、CloudFormationを通じてAWSリソースが作成・更新されます。
今回はTypeScriptを使用して、VPC、S3、CloudFront、Lambda、API Gateway、RDSなどの構成を管理しています。
7. 構築する順番について
今回の構成では、最初からすべてのAWSリソースを作成するのではなく、段階的に構築を進める方針としました。
当初検討した大まかな流れは以下のとおりです。
- VPC・サブネットの作成
- S3の作成
- CloudFrontの構築
- Lambdaの構築
- API Gatewayとの連携
- CI/CDの整備
- RDS PostgreSQLの構築
- PrismaとDBの接続設定
- アプリケーション全体の疎通確認
特にRDSについては、DBインスタンスを稼働させている間の費用が発生するため、初期段階では構築を後回しにする方針としました。
まずは課金を抑えながらフロントエンドやAPIの構成を学習し、必要な段階でRDSを構築する考え方です。
なお、上記は当初検討した構築順序であり、実際の作業では必要に応じて順序の変更や設定の見直しを行っています。
まとめ
今回は、既存のWebアプリケーションをAWSへ移行するにあたり、そもそもなぜ今回の構成を採用したのかを整理しました。
今回の検討を通して、AWSでは単純にサーバーを用意するだけでなく、アプリケーションの役割に応じてサービスを組み合わせられることを学びました。
一方で、各サービスにはメリットだけでなく、コストや運用面で考慮すべき点もあるため、アプリケーションの特性や目的に応じた選択が重要だと感じました。
すでにVPCの構築については記事にしていますが、今回はその前段階となるAWS構成の検討について振り返る内容となりました。
今後も、各AWSサービスの構築手順や実際に発生した課題などを、備忘録としてまとめていこうと思います。
関連記事
今回採用したAWS構成のうち、VPCやPrivate Isolated Subnetの概要については、こちらの記事で紹介しています。