3 層アーキテクチャは、今でもほとんどの Web アプリケーションの内側にある形です。ユーザーインターフェースを提供するもの、ビジネスロジックを実行するもの、そしてデータを保存するものです。古くて退屈ですが、ほとんどのシステムにとって正しい出発点です。各層を個別にスケールさせ、個別に保護できるからです。
この記事では、3 つの層を説明し、それらがサブネットとセキュリティグループを使ってクラウドのネットワークにどう対応するかを示し、2 つのアベイラビリティゾーンにまたがる典型的な AWS のデプロイを図にします。同じ構成はどのクラウドでも使えます。
3 つの層
| 層 | 役割 | 典型的なコンポーネント | スケールの方法 |
|---|---|---|---|
| プレゼンテーション | UI を届け、リクエストを受け付ける | CDN、静的アセット、ロードバランサー、Web サーバー | エッジキャッシュ、インスタンスの追加 |
| アプリケーション | ビジネスロジックと API | アプリケーションサーバー、コンテナ、関数 | インスタンスの追加、オートスケーリング |
| データ | 状態を保存し、取り出す | リレーショナルデータベース、キャッシュ、オブジェクトストレージ | より大きなインスタンス、レプリカ、キャッシュ |
これを単なる 3 つの箱ではなくアーキテクチャにしているルールは、各層は隣の層とだけ通信する、というものです。ブラウザーがデータベースに届くことはなく、プレゼンテーション層が SQL を実行することもありません。
AWS は 3 層アーキテクチャの概要 でこのパターンを説明しており、アプリケーションサーバーを関数に置き換えるサーバーレスの変形版も紹介しています。
層をネットワークに対応付ける
層は、異なるルートを持つ異なるサブネットに置かれたときに、本当の境界になります。
- パブリックサブネット には、インターネットから到達できる必要があるものだけを置きます。ロードバランサーと NAT ゲートウェイです。
- プライベートのアプリケーションサブネット にはアプリサーバーを置きます。アップデートや API のために NAT 経由でインターネットへ出ていくことはできますが、ロードバランサーを経由する以外に外部からアプリサーバーに到達する手段はありません。
- プライベートのデータサブネット にはデータベースとキャッシュを置き、インターネットへのルートは一切持たせません。
そのうえで セキュリティグループ で隣の層からのトラフィックだけを許可します。データベースはポート 5432 を、IP 範囲からではなく、アプリケーション層のセキュリティグループからだけ受け付けます。アドレスではなくグループを参照すれば、インスタンスが増減してもルールは正しいままです。
ゾーンをまたぐ高可用性
すべての層を、少なくとも 2 つのアベイラビリティゾーンで動かします。
- ロードバランサーは、設計上複数のゾーンにまたがります。
- アプリサーバーは、各ゾーンにインスタンスを持つオートスケーリンググループで動かします。あるゾーンが障害を起こしても、ほかのゾーンがトラフィックを引き受け、グループが容量を補充します。
- データベースは、別のゾーンに同期スタンバイを置いて動かします。Amazon RDS Multi-AZ なら、スタンバイへのフェイルオーバーは自動で、エンドポイント名も変わりません。
どのインスタンスでもどのリクエストでも処理できるよう、アプリケーション層はステートレスに保ちます。セッションは共有キャッシュかデータベースへ、アップロードはオブジェクトストレージへ置き、重要なものはインスタンスのディスクに置きません。営業時間中にランダムなアプリサーバーを停止してテストしましょう。ユーザーが気づくなら、まだどこかの状態がインスタンスに隠れています。
各層のスケーリング
層によってスケールの仕方が違うことこそ、層を分けておく理由です。
- プレゼンテーション: 静的なトラフィックの大部分は CDN が吸収します。どれだけ吸収するかはキャッシュヘッダーで決まります。
- アプリケーション: インスタンスを追加します。リクエスト数か CPU でスケールし、インスタンスの起動を速く保ちます。
- データ: 最も難しい層です。まずスケールアップし、読み取り中心の負荷にはリードレプリカを、ホットなキーにはキャッシュを追加し、シャーディングは最後の手段と考えます。
層を通る 1 つのリクエスト
注文履歴を読み込むユーザーを追ってみます。
- DNS が CDN のアドレスを返します。ブラウザーがページを要求し、HTML、スクリプト、画像は CDN のキャッシュか、その裏にある静的ファイル用のバケットから返されます。
- ページが
GET /api/ordersを呼び出します。CDN は動的なパスをロードバランサーに転送し、ロードバランサーは TLS を終端して、どちらかのゾーンにある健全なアプリサーバーを選びます。 - アプリサーバーはキャッシュからセッションを読み、ユーザーがサインインしていることを確認して、プライマリデータベースに注文を問い合わせます。
- レスポンスはロードバランサーと CDN を通って戻ります。レスポンスはプライベートなので、CDN はキャッシュしません。
どのホップも層の境界を 1 つだけ越え、各境界に許可された入口はちょうど 1 つです。これが、この設計を理解しやすく、保護しやすくしています。
コストと適正なサイズ
請求額の大半を占めるのはたいていデータ層で、後から縮小するのも最も難しいので、推測ではなく計測に基づいてサイズを決めましょう。アプリケーション層は最も調整しやすい層です。小さく始め、実際の負荷に合わせてスケールし、ピークはオートスケーリングに任せます。意外な出費になりやすいのは NAT ゲートウェイとゾーン間のトラフィックです。やり取りの多いトラフィックはできるだけ 1 つのゾーン内に収め、オブジェクトストレージには VPC エンドポイントを使って、アプリサーバーがアップロードのたびに NAT の料金を払わないようにしましょう。
アーキテクチャ図
ダイアグラムの内容
- Users
- DNS
- CDN
- Static assets bucket
- VPC across two availability zones
- Presentation tier: public subnets
- Application load balancer
- Application tier: private subnets
- App servers, zone A
- App servers, zone B
- Data tier: private subnets, no internet route
- Database primary, zone A
- Standby, zone B
- Session cache
- HTTPS
- resolve
- static files
- dynamic requests
- SQL
- sync
- sessions
図の読み方は次のとおりです。
- 上: ユーザーは DNS を解決して CDN に接続します。CDN はバケットから静的ファイルを配信し、動的なリクエストを転送します。
- プレゼンテーション層: パブリックサブネットにあるのは、アプリケーションロードバランサーだけです。
- アプリケーション層: ゾーン A と B のアプリサーバーはプライベートサブネットにあり、ロードバランサーからのトラフィックだけを受け取ります。
- データ層: ゾーン A のプライマリデータベースは、ゾーン B のスタンバイに同期レプリケーションします。アプリサーバーはセッションキャッシュも使います。この層にはインターネットへのルートがありません。
- Markdown ブロック には、層の分離を実際に強制しているセキュリティグループのルールを並べています。
よくある間違い
- パブリックサブネットに置かれたデータベース: ノート PC から手早くクエリを打つために「一時的に」置いたものです。代わりに踏み台サーバーかマネージドなセッションツールを使いましょう。
- CIDR 範囲全体を許可するセキュリティグループ: 層の間で、隣の層のグループを参照する代わりに使われているものです。
- アプリサーバー上の状態: メモリー上のセッションやローカルディスク上のアップロードです。オートスケーリングやフェイルオーバーでデータが失われます。
- データベースだけ 1 つのアベイラビリティゾーン: コスト削減のためにそうしているのに、アプリケーション層は 3 つのゾーンに分散しているケースです。
- CDN を省略する: すべての画像をアプリサーバーから配信してしまいます。
- ヘルスチェックのパスがない: アプリサーバーにヘルスチェックのパスがないと、ロードバランサーはデータベースに到達できないインスタンスにトラフィックを送り続けます。
3 層では足りなくなったら
3 層が表すのは 1 つのアプリケーションです。システムが複数のサービスに成長すると、各サービスがそれぞれ 3 層の形を保つことが多く、図は 1 段上がって、サービス同士がどう通信するかを示すようになります。このステップは マイクロサービスのアーキテクチャ図 で扱っています。ネットワークの詳細は AWS VPC の図 を、複数のリージョンについては AWS マルチリージョンのアーキテクチャ図 を参照してください。
Archboard で作る
- Google または GitHub でサインインし、図を作成します。
- C4 モデルパックから 人物 を、ネットワークパックから DNS を、クラウドパックから CDN と オブジェクトストレージ ノードを追加します。
- ネットワークパックから VPC を追加し、その中に基本パックの フレーム ノードを層ごとに 1 つ、合計 3 つ置きます。
- プレゼンテーション層に クラウドロードバランサー を、アプリケーション層に コンピューティング ノードを 2 つ、データ層に マネージドデータベース ノードを 2 つとデータパックの Redis を追加します。
- 各層を隣の層とだけつなぎ、セキュリティグループのルールを書いた基本パックの Markdown ブロックを追加します。構成がすでに Terraform で書かれているなら、Terraform から図を作るツール で最初のたたき台を作れます。
この例で使うダイアグラムノードは 15 個で、Free プランの範囲に収まります。
よくある質問
3 層アーキテクチャの 3 つの層とは何ですか? プレゼンテーション層(ユーザーインターフェースと入口)、アプリケーション層(ビジネスロジックと API)、データ層(データベース、キャッシュ、ストレージ)です。
3 層アーキテクチャは時代遅れですか? いいえ。Web アプリケーションの標準的な形です。マイクロサービスやサーバーレスのシステムも、多くの場合サービスごとに、同じ 3 つの役割を持っています。
3 層(ティア)と 3 レイヤーのアーキテクチャの違いは何ですか? レイヤーはコード内の論理的な分割です。ティアは、ネットワークの境界で隔てられた別々のマシンやサービスへの物理的な分割です。
データベースをパブリックサブネットに置くべきですか? いいえ。インターネットへのルートがないプライベートサブネットに置き、アプリケーション層のセキュリティグループからだけ到達できるようにします。
3 層アプリケーションを高可用性にするにはどうすればよいですか? 各層を少なくとも 2 つのアベイラビリティゾーンで動かし、アプリサーバーをステートレスに保ち、別のゾーンに自動スタンバイを持つデータベースを使います。