復元力を備えたスケーラブルなアプリケーションの作成

復元力とスケーラビリティの両方を兼ね備えたウェブ アプリケーションを作成することは、どのアプリケーション アーキテクチャにおいても不可欠な部分です。適切に設計されたアプリケーションであれば、需要の増減に応じてシームレスにスケールできるだけでなく、1 つ以上のコンピューティング リソースを失っても耐えられるだけの復元力を備えています。

このドキュメントは、Compute Engine に精通したシステム運用の専門家を対象としています。ここでは、あらゆるアプリケーションに広く適用されるパターンと手法を適用し、Google Cloud Platform(GCP)を使用してスケーラブルで復元力を備えたアプリケーション アーキテクチャを構築する方法を説明します。また、よく使われているオープンソースのプロジェクト管理ツール Redmine(Ruby on Rails ベースのアプリケーション)をデプロイする例を通して、このような原則が現実のシナリオにどのように適用されるかについても学びます。後半のサンプル ソリューションのデプロイのセクションでは、アプリケーションを自分でデプロイして、参照用のソースをすべてダウンロードできます。

GCP では、スケーラビリティと復元力の両方を兼ね備えたウェブ アプリケーションを作成して実行できます。需要に応じてアプリケーションのリソースを調整するには、Compute Engine やオートスケーラーなどのサービスを利用できます。Google Compute Engine の料金モデルでは、秒単位で課金されるだけでなく、継続利用割引により、複雑な容量計画や予約計画を立てなくても自動的に最適な料金が適用されます。

GCP でのウェブ ホスティングのオプションの概要については、ウェブサイトの処理をご覧ください。

スケーラビリティと復元力の定義

サンプル アプリケーション アーキテクチャについて説明する前に、用語「スケーラビリティ」と「復元力」を定義します。

スケーラビリティ: 需要を満たすための容量調整

スケーラブルなアプリケーションとは、ユーザーが 1 人または 100 万人でも正常に機能し、トラフィックのピークと低下を適切に自動処理するアプリケーションを指します。スケーラブルなアプリケーションは、必要なときにのみ仮想マシンを追加または削除し、需要を満たすのに必要なリソースだけを消費します。

次の図は、スケーラブルなアプリケーションがどのように需要の増減に対応するのかを示しています。

需要を満たすためにリソースがどのようにスケーリングできるのかを示す図

需要の変化に合わせて容量が動的に調整されている点に注目してください。設計の弾力性と呼ばれることもあるこの構成は、特定の時点でアプリケーションが必要とするコンピューティング リソースに対してのみ料金を支払うようにするのに役立ちます。

復元力: 予想外の事態に耐える設計

可用性の高いアプリケーション、つまり復元力を備えたアプリケーションとは、システムのコンポーネントに想定内または想定外の障害が発生しても機能し続けるアプリケーションを指します。単一のインスタンスで障害が発生しても、ゾーン全体で問題が発生しても、復元力を備えたアプリケーションはフォールト トレラントな状態を維持します。つまり、引き続き機能し、必要に応じて自動的に自己修復するということです。ステートフルな情報はどのインスタンスにも保存されないため、単一のインスタンスが失われても、さらにはゾーン全体が失われても、アプリケーションのパフォーマンスは影響を受けません。

真の復元力を備えたアプリケーションを設計するには、ソフトウェア開発レベルとアプリケーション アーキテクチャ レベルの両面から計画する必要があります。このドキュメントでは主に、アプリケーション アーキテクチャ レベルに焦点を当てます。

復元力を備えたアプリケーションのアプリケーション アーキテクチャの設計には、一般に次の処理が含まれます。

  • ロードバランサでサーバーをモニタリングし、リクエストを最適に処理できるサーバーにトラフィックを分散する
  • 複数のリージョンで仮想マシンをホストする
  • 堅牢なストレージ ソリューションを構成する

GCP: 柔軟で費用対効果の高いプラットフォーム

スケーラビリティと復元力をサポートする従来型のアーキテクチャでは、多くの場合、リソースに多大な投資が必要になります。オンプレミス ソリューションでのスケーラビリティとは多くの場合、ピーク時の使用量に対処するためにサーバー容量に過剰に支出するか、あるいはトラフィック急増時のアプリケーション パフォーマンスやユーザー エクスペリエンスの劣化というリスクを前提に、平均的なニーズだけに基づいてリソースを購入するかの二者択一を意味します。復元力については、サーバー容量だけでなく、場所も重要です。暴風雨や地震などの物理的な事象の影響を低減するために、複数の物理的な場所でサーバーを運用することを検討する必要がありますが、これには多大な費用がかかります。

GCP では代替手段として一連のクラウド サービスを提供し、柔軟にスケーラビリティと復元力をアーキテクチャに追加できるようにしています。しかも、GCP ではユーザーが管理できる料金体系でこれらのサービスを提供しています。

GCP を使用した、復元力を備えたスケーラブルなアーキテクチャの構築

次の表では、アプリケーションをスケーラブルで復元力を備えたものにするうえで必要となる主な要件に、各 GCP サービスがどのように対応しているのかを示します。

アーキテクチャ要件 GCP サービス
負荷分散 HTTP 負荷分散
サーバー ホスティング Compute Engineリージョンとゾーン
サーバー管理 インスタンス テンプレートマネージド インスタンス グループオートスケーラー
データ ストレージ Cloud SQLCloud Storage

次の図は、これらの GCP コンポーネントが連携して、スケーラビリティと復元力を備えたアプリケーションを構築する仕組みを示しています。各コンポーネントが果たす役割については、次のセクションで詳しく説明します。

スケーラブルで復元力を備えたアプリケーションを示した図。

コンポーネントの概要

サンプル アプリケーション アーキテクチャの各コンポーネントは、アプリケーションがスケーラビリティと復元力を備えるようにするうえでそれぞれ役割を果たします。このセクションでは、各サービスについて簡単に説明します。後述のセクションでは、これらのサービスが連携する仕組みについて説明します。

HTTP ロードバランサ

HTTP ロードバランサは、顧客がアプリケーションにアクセスするのに使用する単一の IP アドレスを公開します。このパブリック IP アドレスは、DNS A レコード(例: example.com)または CNAME(例: www.example.com)に関連付けることができます。受信リクエストは、各ゾーン内の複数のインスタンス グループに、それぞれのグループの容量に応じて分散されます。ゾーン内では、リクエストがグループ内の各インスタンスに均等に分散されます。HTTP ロードバランサはトラフィックを複数のリージョンに分散できますが、このサンプル ソリューションでは、複数のゾーンからなる単一のリージョンで HTTP ロードバランサを使用しています。

ゾーン

ゾーンとはリージョン内の分離されたロケーションです。ゾーンは、低レイテンシの高帯域幅ネットワークを通じて同じリージョンの他のゾーンと接続されています。Google では、1 つのリージョン内の複数のゾーンにアプリケーションをデプロイすることをおすすめしています。

インスタンス

インスタンスとは、Google のインフラストラクチャでホストされる仮想マシンのことです。インスタンスは、物理サーバーと同じようにインストールして構成できます。このサンプル デプロイでは、起動スクリプトと Chef を使用して、アプリケーション用のアプリケーション サーバーとコードが含まれたインスタンスを構成できます。

インスタンス テンプレート

インスタンス テンプレートは、マシンタイプ、イメージ、ゾーン、ラベル、その他のインスタンス プロパティを定義します。イスタンス テンプレートを使用することで、マネージド インスタンス グループを作成できます。

マネージド インスタンス グループ

マネージド インスタンス グループとは、インスタンス テンプレートに基づく同種のインスタンスからなるグループのことです。HTTP ロードバランサは、マネージド インスタンス グループをターゲットに、そのグループに含まれるインスタンス全体で処理を分散できます。マネージド インスタンス グループには対応するインスタンス グループ マネージャー リソースがあり、このマネージャーによってグループのインスタンスが追加、削除されます。

オートスケーラー

Compute Engine オートスケーラーは、トラフィック、CPU 使用率、またはその他のシグナルに応じ、マネージド インスタンス グループのマネージャーと連動してグループの Compute Engine インスタンスを追加または削除します。サンプル ソリューションのオートスケーラーは、HTTP ロードバランサのリクエスト数/秒(RPS)指標に応じで動作します。自動的にスケーリングする必要があるマネージド インスタンス グループごとに、オートスケーラーが必要になります。

Cloud SQL

Cloud SQL は、MySQL と PostgreSQL の両方をサポートする、フルマネージド データベース サービスです。つまり、レプリケーション、暗号化、パッチ、バックアップが Google によって管理されます。Cloud SQL インスタンスは単一のゾーンにデプロイされ、データは他のゾーンに自動的に複製されます。この例で使用している Redmine アプリケーションは、MySQL と互換性があり、Cloud SQL とシームレスに連携します。

Cloud Storage

Cloud Storage では、シンプルでスケーラブルなインターフェースを使用してオブジェクト(通常はファイル)を保存、取得できます。このソリューションでは、Cloud Storage バケットを使用して、SSL 秘密鍵をスケーラブルな Compute Engine インスタンスに配布するとともに、Redmine アプリケーションにアップロードされたすべてのファイルを保存します。したがって、ステートフルな情報はどのインスタンスのディスクにも保存されません。

復元力

このサンプル アーキテクチャに復元力を持たせるには、障害が発生したインスタンスまたは利用不可になったインスタンスを自動的に置き換える必要があります。新しいインスタンスは、オンラインになった時点で次のことが必要になります。

  • システムにおける自身の役割を理解する
  • 自動的に自己構成する
  • 依存関係をすべて検出する
  • 自動的にリクエストの処理を開始する

障害が発生したインスタンスを自動的に置き換えるときには、複数の Compute Engine コンポーネントをまとめて使用できます。

起動スクリプトは、インスタンスの起動時または再起動時に実行されます。起動スクリプトを使用して、ソフトウェアや更新をインストールすること、仮想マシン内でサービスが実行されていることを確認できます。さらに、起動スクリプトによって Chef、Puppet、Ansible、Salt などの構成管理ツールをインストールすることも可能です。

このシナリオでは、起動スクリプトを使用して Chef Solo をインストールし、Chef Solo によってアプリケーションと連携するようにインスタンスを構成します。起動スクリプトと Chef Solo を使用してインスタンスを自動的に構成する方法については、このトピックの末尾にある付録: 新しいインスタンスの追加をご覧ください。

起動スクリプトとは別に、Compute Engine インスタンスを起動する前に定義しなければならない項目がいくつかあります。たとえば、マシンタイプ、使用するオペレーティング システム イメージ、接続するすべてのディスクを指定する必要があります。このようなオプションは、インスタンス テンプレートを使用して定義します。

インスタンス テンプレートと起動スクリプトの組み合わせにより、Compute Engine インスタンスを起動する方法と、アプリケーション アーキテクチャでの特定の役割を満たすようにそのインスタンスでソフトウェアを構成する方法が定義されます。

起動スクリプト、インスタンス テンプレート、インスタンスがどのように連携するのかを示した図

もちろん、インスタンス テンプレートは単なるテンプレートです。このテンプレートを機能させるには、新しい Compute Engine インスタンスがオンラインになった時点で、そのインスタンスにテンプレートを適用する手段が必要になります。その手段として作成するのが、マネージド インスタンス グループです。常時実行するインスタンスの数、そしてそれらのインスタンスに適用するインスタンス テンプレートは、ユーザーが決定します。インスタンス グループ マネージャーは、必要に応じてそれらのインスタンスを起動し、構成します。

次の図では、以下の各コンポーネントがどのように連携するかを示しています。

  • 起動スクリプト
  • インスタンス テンプレート
  • インスタンス グループ マネージャー
  • マネージド インスタンス グループ
起動スクリプト、インスタンス テンプレート、インスタンス グループ マネージャー、マネージド インスタンス グループがどのように連携するのかを示した図

マネージド インスタンス グループと、それに対応するインスタンス グループ マネージャーは、ゾーン固有のリソースにすることも、リージョン リソースにすることもできます。インスタンス テンプレートはプロジェクト レベルのリソースであるため、任意のゾーンの任意のリージョン内にある複数のマネージド インスタンス グループで再利用できます。ただし、インスタンス テンプレートで一部のゾーンリソースを指定した場合、そのテンプレートを使用できるのは、それらのリソースがあるゾーンに限定されます。

これで、起動スクリプト、インスタンス テンプレート、マネージド インスタンス グループが揃ったので、正常でないインスタンスを新しいインスタンスで置き換えることができるシステムが構築されました。次のセクションでは、正常でないインスタンスがどのようなものかを定義し、またそうしたインスタンスを検出する方法を定義する 1 つの方法を示します。

ヘルスチェック

この時点で、サンプル アプリケーションには、復元力を備えるのに必要なツールがほぼすべて揃いました。ただ 1 つ欠けているものは、正常でないインスタンスを特定し、そのインスタンスを置き換える必要があることを認識する方法です。

このアプリケーションは、HTTP ロードバランサを使用して、正常な状態の適切なインスタンスにユーザーを接続するように設計されています。このアーキテクチャでは、次の 2 つのサービスを使用して、リクエストに対応できるインスタンスを特定できます。

  • ヘルスチェック。HTTP ヘルスチェックは、各インスタンスに対してヘルスチェックを実行するためのポートとパスを指定します。ヘルスチェックでは、正常なインスタンスからは 200 OK レスポンスが返されることが想定されます。
  • バックエンド サービス。バックエンド サービスは、ロードバランサからトラフィックを受信する 1 つ以上のインスタンス グループを定義します。バックエンド サービスにより、インスタンスが公開するポートとプロトコル(例: HTTP ポート 80)と、インスタンス グループ内のインスタンスに対して使用する HTTP ヘルスチェックが指定されます。

次のアプリケーション アーキテクチャの図では、バックエンド サービスと HTTP ヘルスチェックがロードバランサやインスタンス グループとどのように関係しているのかを示します。

ヘルスチェックとバックエンド サービスを示した図

Cloud SQL でのデータの復元力

すべてのアプリケーション アーキテクチャに共通する主要 3 大領域は、ネットワーキング、コンピューティング、ストレージです。これまでに説明したアプリケーション アーキテクチャは、ネットワーキング コンポーネントとコンピューティング コンポーネントをカバーしていますが、完全なアーキテクチャを実現するには、ストレージ コンポーネントにも対処する必要があります。

このサンプル ソリューションでは、Cloud SQL 第 1 世代インスタンスを使用して、フルマネージド MySQL データベースを提供します。Cloud SQL を使用すれば、Google がレプリケーション、暗号化、パッチ管理、バックアップを自動的に管理します。

Cloud SQL データベースは、リージョン規模のデータベースです。つまり、データはリージョン内の各ゾーンに複製されます。これは、データが更新されるたびにバックアップが作成されるのと同じことです。1 つのゾーンの完全な障害というめったに起こらない事態が発生した場合でも、データは保持されます。

Cloud SQL では、レプリケーションを次の 2 つのタイプから選択できます。

  • 同期レプリケーション。同期レプリケーションでは、更新が複数のゾーンにコピーされてからクライアントに返されます。これにより、重大な問題が起きても信頼性と可用性が確保されますが、書き込みの処理速度は遅くなります。
  • 非同期レプリケーション。非同期レプリケーションでは、書き込みのスループットが上がります。書き込みがローカルのキャッシュに保存されると、他の場所にデータがコピーされる前に書き込みの確認応答が行われるためです。非同期レプリケーションでは、レプリケーションが完了するまで待つ必要がないためデータベースへの書き込み処理が速くなります。ただし、万が一、データベースの更新が行われる数秒の間にデータセンターでシステム障害が発生した場合、最新の更新が失われる可能性があります。

このソリューションで使用されている Redmine アプリケーションでは、ワークロードでの書き込み頻度がさほど高くないため、同期レプリケーションを使用しています。同期レプリケーションと非同期レプリケーションのどちらを使用するかは、アプリケーションの書き込みパフォーマンスとデータ耐久性の要件に応じて選択してください。

スケーラビリティ

前のセクションで、サンプル アプリケーションで GCP を使用して、復元力を備えたアプリケーションを作成する方法を説明しました。ただし、復元力だけでは十分でありません。スケーラビリティも重要です。アプリケーションは、ユーザーが 1 人または 100 万人でも適切に機能する必要があります。また、ユーザー数に合わせてそのリソースを増減し、費用対効果を高めなければなりません。

アプリケーションのリソースを増減できるようにするには、以下が必要になります。

  • サービスのインスタンスを追加または削除する手段。インスタンスを追加する必要があるタイミングと、インスタンスを削除する必要があるタイミングを決定する手段も必要です。この問題は、GCP のオートスケーラーが解決します。
  • ステートフルなデータを保存する手段。インスタンスは稼働したり停止したりするため、ステートフルなデータをインスタンスに保存することはおすすめできません。リレーショナル データの場合、アプリケーション アーキテクチャでこの問題を解決するには、別個の Cloud SQL インスタンスにデータを保存するという方法を取れます。ただしその場合は、ユーザーがアップロードしたファイルについても考慮する必要があります。Cloud Storage は、この要件を満たします。

以降のセクションで、オートスケーラーを使用して、Redmine アプリケーションを実行するインフラストラクチャをスケールする方法と、アップロードされたファイルに対して Cloud Storage を利用する方法を説明します。

オートスケーラーを使用したスケーリング

アプリケーションの使用量は増減するため、必要とされるリソースを動的に調整する必要があります。この課題を解決するために使用できるのが、Compute Engine オートスケーラーです。

トラフィックまたは負荷が増加すると、オートスケーラーは追加アクティビティを処理するためにリソースを追加します。一方、トラフィックまたは負荷が低下すると、費用を削減できるよう、リソースを削除します。オートスケーラーは、こうしたアクションをスケーリング ルールに基づいて自動的に実行します。スケーリング ルールはユーザーが定義しますが、その後の人的な介入は不要です。

オートスケーラーは、以下の 2 つの影響を与えます。

  1. 需要を満たすために十分なリソースが常に確保されるため、アプリケーションを使用する際の優れたユーザー エクスペリエンスが実現する。
  2. 需要が指定しきい値を下回ったときにオートスケーラーによってインスタンスが削除されるため、費用をより適切に抑制できる。

オートスケーラーでは、CPU 使用率、処理容量、または Stackdriver Monitoring 指標に基づいて仮想マシンの数をスケールできます。このソリューションでは、処理容量指標を使用して、インスタンスがロードバランサから受信しているリクエスト数/秒(RPS)に基づいて Compute Engine インスタンスを追加または削除します。詳しくは、Compute Engine オートスケーラーを使用したバッチ処理をご覧ください。

リクエスト数/秒(RPS)

前のセクションでは、ロードバランサからのトラフィックを受信するインスタンス グループを特定する、単一のバックエンド サービスについて説明しました。このサンプル ソリューションでは、バックエンド サービスに関連付けられたインスタンス グループごとに balancingMode=RATE も設定しています。このプロパティはロードバランサに対し、maxRatePerInstance プロパティに定義されている RPS に応じてバランスを取るよう指示します。この例では RPS が 100 に設定されています。この構成は、ロードバランサが各インスタンスの RPS を 100 未満に維持しようとすることを意味します。バックエンド サービスの構成プロパティの詳細については、
バックエンド サービスに関するドキュメントをご覧ください。

RPS に基づくスケーリングを行うには、自動的にスケールする必要があるインスタンス グループごとにオートスケーラーを作成する必要があります。この例のインスタンス グループはゾーン別のリソースであるため、ゾーンにオートスケーラーを作成する必要があります。

オートスケーラーがアプリケーションのアーキテクチャにどのように収まるのかを示した図。

各オートスケーラーには utilizationTarget プロパティが含まれています。オートスケーラーは、このプロパティで定義された、ロードバランサの最大処理容量の割合を維持します。この例では、各オートスケーラーの utilizationTarget を、各インスタンスの最大レートである 100 RPS の 80% に設定しています。つまり、オートスケーラーは、RPS がインスタンスごとの最大レートの 80%(80 RPS)を超過するとインスタンス グループをスケールアップします。RPS がこのしきい値を下回ると、インスタンス グループをスケールダウンします。

インスタンスを追加または削除する必要があるかどうかをオートスケーラーが決定する方法を示したフローチャート。

各オートスケーラーでは、そのオートスケーラーが違反できないインスタンスの最大数と最小数も定義します。

自動スケーリング機能は、マネージド インスタンス グループでのみ使用できます。詳しくは、インスタンス グループインスタンスのグループの自動スケーリングをご覧ください。

ファイル アップロードの処理

Redmine アプリケーションの機能の中には、ユーザーがログインするときにファイルをアップロードして保存できる機能があります。Redmine や他の多くの同様のアプリケーションのデフォルト動作では、アップロードされたファイルはローカル ディスクに直接保存されます。このアプローチは、サーバーが 1 つのみで、適切に定義されたバックアップ メカニズムを備えている場合には問題はありません。ただし、ロードバランサの背後に自動的にスケールされる複数の Compute Engine インスタンスがある場合、これは最適なアプローチとは言えません。ユーザーがファイルをアップロードした場合、そのファイルが保存されているマシンで次のリクエストが受信されるとは限らないためです。また、ファイルが保存されているインスタンスが、オートスケーラーにより不要なインスタンスとして終了されないという保証もありません。

Cloud Storage を使用すれば、この問題をよりよく解決できます。Google Cloud Storage は、自動的にスケーリングされる一連のウェブサーバーからのファイル アップロードを一元的に保存して、アクセスするのに最適な場所となります。また、Cloud Storage は、Amazon S3 クライアントと相互運用可能な API も公開しています。したがって、Redmine S3 プラグインなどの Amazon S3 用の既存のアプリケーション プラグインと互換で、変更を加える必要はありません。多くのサードパーティのアプリケーションやオープンソース アプリケーションには、Cloud Storage などのオブジェクト ストアをサポートするプラグインが用意されています。独自のアプリケーションを構築している場合は、Cloud Storage API を直接使用してファイルを保管できます。

次の図は、Redmine と Cloud Storage を使用してファイルをアップロード(青の矢印)し、そのファイルを取得(緑の矢印)するフローを示しています。

Redmine アプリケーションを介したリクエストのフローを示した図

図で示されているプロセスは、次のとおりです。

  1. ユーザーが、ウェブブラウザからファイルを POST する。
  2. ロードバランサが、リクエストを処理するインスタンスを選択する。
  3. インスタンスが、Cloud Storage にファイルを保存する。
  4. インスタンスが、Cloud SQL データベース内にファイルのメタデータ(名前、所有者、Cloud Storage 内での場所など)を保存する。
  5. ユーザーがファイルを要求すると、ファイルが Cloud Storage からインスタンスにストリーミングされる。
  6. インスタンスが、ロードバランサを介してストリームを送信する。
  7. ファイルがユーザーに送信される。

ストレージ容量

Cloud Storage は、Compute Engine インスタンスからステートフルなファイル アップロードを取り除いてインスタンスを動的にスケーリングできるようにするだけでなく、事実上無限数のファイル アップロードに対応できる、冗長性と耐久性を兼ね備えたストレージを提供します。このストレージ ソリューションは、復元力を備えており、スケーラブルで費用対効果の高いソリューションです。容量計画を気にすることなく、使用したストレージに対してのみ料金を支払うことができます。しかも、データは自動的に、複数のゾーンに重複して保存されます。

料金

ここまでの内容では、このドキュメントで紹介しているアプリケーション アーキテクチャで、GCP を使用して復元力とスケーラビリティを備えたアプリケーションを作成する方法を説明しました。ただし、可能な限り費用対効果の高いアプリケーションを作成するには、それだけでは足りません。

このセクションでは、このドキュメントで紹介しているアプリケーション アーキテクチャが復元力を備えていてスケーラブルなだけでなく、費用対効果にも優れたものであることを説明します。まず、アプリケーション使用の負荷と頻度について一般的な想定を行い、その想定を基本的な費用の見積もりに変換します。このような想定は単なる想定にすぎないということに留意してください。各数値を必要に応じて自由に調整して、独自のアプリケーションで予想される使用に合うように費用の見積もりを作成してください。

コンピューティング

すべてのアプリケーション アーキテクチャの主要な関心事は、サーバーを稼働しつづけるのにかかる費用です。この費用分析では、次の想定を使用しています。

指標
月あたりの平均ページビュー 20,000,000
月あたりの平均 HTTP リクエスト数 120,000,000
使用ピーク時間(90% 以上) 月曜日から金曜日の午前 7 時から午後 6 時まで
ページビューあたりのデータ転送 100 KB
月あたりのピーク時間 220
ピーク時のリクエスト レート 127 リクエスト/秒(RPS)
オフピーク時のリクエスト レート 6 リクエスト/秒(RPS)

上記の想定に基づいて、毎月の月曜日から金曜日の午前 7 時から午後 6 時までのピーク時にアプリケーションが受け取るページビュー数は、次のように求めることができます。

20,000,000(ビュー/月) x 6(リクエスト/ビュー) x 90%(ピーク時に発生) = 108,000,000

毎月平均して、営業日は 22 日あります。各営業日にピーク時間が 11 時間ある場合、毎月 242 時間のピーク時間を処理するのに十分なコンピューティング リソースを用意する必要があります。

次に、このようなタイプのトラフィックを処理できる Compute Engine インスタンスのタイプを明らかにする必要があります。このアプリケーション アーキテクチャは、基本的な負荷テストとして gatling.io を使用してテストされました。その結果、タイプが n1-standard-1 の 4 つの Compute Engine インスタンスで十分であることがわかりました。

このソリューションでは、非ピーク時に、少なくとも 2 つの n1-standard-1 インスタンスが実行されます。

インスタンスの実行にかかる費用については、GCP の料金計算ツールで最新の料金の見積もりを確認してください。どちらのインスタンスにも、自動的に継続利用割引が適用されることがわかります。

負荷分散とデータ転送

このアプリケーションでプロビジョニングしているロードバランサには、ユーザーの接続先となるパブリック IP アドレスが単一の転送ルールとして使用されています。この転送ルールは、時間単位で課金されます。

データ転送の見積もりでは、まず、最悪のシナリオを検討します。ロードバランサでは、処理した上りデータに対して課金されます。ロードバランサから送信されるトラフィックには、通常の下り料金が課金されます。120,000,000 HTTP リクエストの 99.5% がユーザーによる Redmine プロジェクト ページの読み込みであると想定します。ページの読み込みは、1 件の HTTP GET リクエストとしてカウントされ、さらに他のアセット(CSS、イメージ、jQuery)を読み込むために 5 件の HTTP GET リクエストが発生します。ページ全体を読み込むと、6 件の HTTP リクエストが発生します。その結果、次のようになります。

  • 月あたり約 20,000,000 回の完全なページ読み込み
  • ページあたり約 10 KB の上りデータ処理および約 450 KB のデータ転送
  • ロードバランサにより月あたり合計約 214 GB のデータが処理され、9,091 GB の下りトラフィックが発生

20,000,000 件の HTTP リクエストの残りの 0.5% は、平均サイズ(約 0.5 MB)のファイルをアップロードする HTTP POST リクエストであり、月あたり 500 GB の追加データが処理されます。

GCP の料金計算ツールによるこの見積もりから、このシナリオでロードバランサが処理する 714 GB のデータ転送と 9,091 GB を超える下りトラフィックに対する料金がわかります。

このデータ転送の見積もりは最悪の場合のシナリオです。これは、Compute Engine インスタンスからの各リクエストで静的アセットも含めすべてのコンテンツを提供しており、かつキャッシュやコンテンツ配信ネットワーク(CDN)のメリットを享受せず、ロードバランサによりこれを行っているためです。各ページ読み込みの約 450 KB のペイロードのうち、jQuery の読み込みに 333 KB が必要とされていますが、このソリューションが月あたり 2 千万回のページ読み込みに基づいていることを思い出してください。Google がホストしているライブラリから jQuery を読み込むようにアプリケーションの 1 行を更新するだけで、データ転送が 73% 削減されます。

この更新された料金の見積もりでは、Google でホストされているライブラリに切り替えることで節約できるデータ転送料が示されています。

ストレージ

このソリューションは、Redmine アプリケーションを介してアップロードされたすべてのファイルに対して Cloud Storage を使用します。前のセクションで説明したように、使用量の約 0.5% はファイルをアップロードするためであり、アップロードされるファイルの平均サイズは 0.5 MB です。つまり、月あたり 1,000,000 回の新しいファイル アップロードが発生し、結果として月あたり 500 GB の新しいストレージが必要になると見込むことができます。また、このソリューションでは、新しいファイルを保存するために、月あたり 1,000,000 回の HTTP PUT オペレーションを想定していますが、これは、クラス A オペレーションとして課金されます。

GCP の料金計算ツールによるこの料金見積もりから、Cloud Storage を使用した場合に想定される料金がわかります。

このアーキテクチャは Cloud SQL を使用して、アプリケーションのすべてのリレーショナル データを保存します。前述のサンプル指標に基づくと、1,024 MB の RAM を採用した D2 データベース タイプの容量は、アプリケーションのワークロードに対応するのに十分であり、このデータベースは 1 日 24 時間、週 7 日間稼働することになります。おそらくこのデータベースの使用率は高くなるため、計算ツールの入出力オペレーションには [多い] オプションを選択します。このサンプル アーキテクチャのテストは、100,000 個のドキュメントを挿入することで行われました。その結果、50 GB のディスクで 100,000,000 個を超えるドキュメントがサポートされ、上述のレートで使用した場合、このデータベースで 8 年を超える期間においてサポートできることが判明しました。

GCP の料金計算ツールによるこの見積もりでは、アーキテクチャ費用のこの部分に対して予想される費用が示されています。

サンプル ソリューションのデプロイ

このソリューションで説明しているサンプル アプリケーションをデプロイするには、GitHub リポジトリにアクセスして GCP でのスケーラブルで復元力を備えたアプリケーションをご覧ください。

付録: 新しいインスタンスの追加

復元力を備えたスケーラブルなアプリケーション アーキテクチャを作成する作業の一環として、新しいインスタンスの追加方法を決定する必要があります。具体的には、新しいインスタンスがオンラインになったときにそのインスタンスを自動的に構成する方法を決定する必要があります。

このセクションでは、利用可能なオプションのいくつかを見ていきます。

ソフトウェア インストールのブートストラップ

ユーザーのウェブ リクエストを処理するには、各インスタンスに、基本オペレーティングをベースにインストールされた追加ソフトウェアが必要になります。さらに、構成データも必要です。構成データには、データベース接続情報と、ファイルを保存する Cloud Storage バケットの名前が含まれます。こうしたコンポーネントをレイヤとして考えると、次のように、各インスタンスで実行されるスタック全体を可視化できます。

ソフトウェアがどのようにインスタンス上にスタックされるのかを示した図

このソリューションではインスタンス テンプレートを使用して、インスタンスが起動時に使用する Compute Engine イメージを指定します。具体的には、このソリューションは、Canonical によって開発されてサポートされている Ubuntu 14.10 イメージを使用します。これは基本オペレーティング システム イメージなので、アプリケーションに必要なソフトウェアや構成は含まれていません。

新しいインスタンスの起動時にスタックの残りが自動的にインストールされるようにするには、Compute Engine 起動スクリプトChef Solo を組み合わせて使用し、起動時にブートストラップします。起動スクリプトを指定するには、startup-script メタデータ属性項目をインスタンス テンプレートに追加します。起動スクリプトは、インスタンスの起動時に実行されます。

この起動スクリプトは以下を行います。

  1. Chef クライアントをインストールします。
  2. node.json という特殊な Chef ファイルをダウンロードします。このファイルは Chef に対し、このインスタンスで実行する構成を指示します。
  3. Chef を実行し、詳細な構成を Chef に処理させます。

起動スクリプト全体を以下に示します。

#! /bin/bash

# Install Chef
curl -L https://www.opscode.com/chef/install.sh | bash

# Download node.json (runlist)
curl -L https://github.com/googlecloudplatform/... > /tmp/node.json

# Run Chef
chef-solo -j /tmp/node.json -r https://github.com/googlecloudplatform/...

アプリケーション構成の提供

新しいインスタンスは、起動して、起動スクリプトと Chef を使用して自己構成した後に、リクエストの処理を開始する前にいくつかの情報を把握する必要があります。この例で各インスタンスが把握しなければならない情報は、データベース接続情報(ホスト名、ユーザー名、パスワードなど)、使用する Cloud Storage バケットの名前、接続に使用する認証情報です。

すべての Compute Engine インスタンスには、メタデータ属性が関連付けられています。これらの属性は、ユーザーが定義できます。前に、特殊な startup-script メタデータ属性を追加する方法を説明しましたが、任意の Key-Value ペアを追加することもできます。ここでは、インスタンス テンプレート内で属性を指定して、インスタンスがデータベースと Cloud Storage バケットに接続するときに必要となる構成データを属性に追加できます。

GCP Console でインスタンス テンプレートのメタデータがどのように表示されるのかを以下に示します。

インスタンス テンプレートのカスタム メタデータ情報を示している、Google Cloud Platform Console のスクリーンショット

Chef は Ohai というツールを使用して、インスタンスのメタデータに含まれる各構成情報を解析し、テンプレートにデータを入力して、アプリケーションに必要な構成ファイルを作成します。適切なメタデータ項目に自動的にアクセスして、データベース接続情報が含まれた database.yaml ファイルを作成するテンプレートを以下に示します。

production:
    adapter: mysql2
    database: <%= node['gce']['instance']['attributes']['dbname'] %>
    host:     <%= node['gce']['instance']['attributes']['dbhost'] %>
    username: <%= node['gce']['instance']['attributes']['dbuser'] %>
    password: <%= node['gce']['instance']['attributes']['dbpassword'] %>

また、ローカル メタデータ サービスを使用して、インスタンス内からメタデータ値に手動でアクセスすることもできます。次のように、curl を使用してデータベースのパスワードを取得できます。

curl "http:/metadata.google.internal/computeMetadata/v1/instance/attributes/dbpassword" -H "Metadata-Flavor: Google"

パフォーマンスと依存関係に関する考慮事項

このソリューションで採用しているブートストラップ アプローチでは、デフォルト オペレーティング システム イメージから開始し、Chef を使用して起動時にすべてのソフトウェアをインストールし、インスタンス メタデータを使用してアプリ構成データを提供しています。

ソフトウェアがどのようにインスタンス上にスタックされるのかを示した図一部のソフトウェアがイメージにバンドルされ、一部のソフトウェアが起動時にインストールされ、一部のソフトウェアが起動後に提供される仕組みを示すスタック

このアプローチの利点は、システムの構成が Chef のクックブックに指定されていることです。クックブックをバージョン管理、共有、使用することで、Vagrant または Docker を使用してテストのためにローカルに仮想マシンをプロビジョニングしたり、データセンター内のサーバーや異なるクラウド プロバイダを使用したサーバーを構成したりできます。イメージの管理も簡素化します。このサンプル アプリケーションの場合、アプリケーションが使用している 1 つの基本 OS をトラックするだけで済みます。

考慮する必要がある欠点としては、すべてのソフトウェアをインストールするため(場合によっては、コンパイルも必要)、起動時間が長くなる可能性があります。また、この方法に伴う依存関係を考慮することも重要になります。この例では、Chef によって、apt、Rubygems、GitHub から多くのパッケージがインストールされています。新しいインスタンスの起動時に各リポジトリのいずれかが利用不可であった場合、構成が失敗します。

カスタム イメージとブートストラップ

Compute Engine では独自のカスタム イメージを作成できるため、ブートストラップのアプローチは、起動時にすべてをインストールすることだけではありません。たとえば、以下のことができます。

  1. ベースの Ubuntu 14.10 イメージを起動します。
  2. Redmine アプリを除くすべて(Ruby、nginx など)をインストールします。
  3. 結果からイメージを作成します。
  4. インスタンス テンプレートでそのイメージを使用します。

これで、新しいインスタンスは、起動時に、Redmine をインストールするだけで済みます。起動時間が改善され、外部パッケージ依存関係の数が削減されました。

Redmine を除き、すべてがイメージ上にインストールされたインスタンス スタックを示した図

カスタム イメージ アプローチをさらに進め、すべての依存関係、アプリケーション ソース、構成など、すべてのものをイメージに焼くことができます。これにより、起動時間が最短となり、外部依存関係がゼロになるという利点が得られますが、アプリケーションで何か 1 つでも変更された場合、新しいイメージを作成してインスタンス テンプレートを更新する必要が生じます。

すべてのソフトウェアがイメージにバンドルされているインスタンス スタックを示した図

連続体としてインスタンスをブートストラップするアプローチを検討します。起動時における構成が増加すると、起動時間は長くなりますが、管理するイメージ数は削減されます。カスタム イメージに焼く構成が増加すると、起動時間は短縮され、依存関係が削減されますが、管理するイメージ数が増加する可能性があります。ほとんどのお客様にとっては、その中間あたりで折り合いをつけたアプローチが妥当なアプローチとなります。お客様、そしてご使用のアプリケーションにとって妥当なアプローチを選択してください。

ソフトウェアがインスタンス上にインストールされる一連の方法を示した図範囲は、すべてが起動後にインストールされるようにする方法から、すべてがイメージにバンドルされるようにする方法までです。

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...