スケーラブルで復元性の高いアプリのためのパターン

Last reviewed 2024-03-19 UTC

このドキュメントでは、復元性が高くスケーラブルなアプリを作成するためのパターンと手法、多くの最新アーキテクチャに関する実践における 2 つの基本的目標について説明します。適切に設計されたアプリは、需要の増減に応じてスケールアップまたはスケールダウンでき、サービス障害が生じた場合に対応できる十分な復元力を備えています。こうした要件に適合するアプリを作成し運用するには、計画と設計を慎重に行う必要があります。

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

スケーラビリティとは、処理量が増減したときにリソースを追加または削除することでシステムがその変化にどのように対応できるかを表す尺度です。たとえば、スケーラブルなウェブアプリとは、ユーザーが 1 人の場合でも多数の場合でも正常に機能し、トラフィックのピークと低下を適切に処理するアプリを指します。

アプリが使用するリソースを調整するための柔軟性は、クラウドに移行する際のビジネス上の主要な促進要因です。適切な設計を行い、使用率が低下しているリソースを削除することによって、パフォーマンスやユーザーの操作性を損なうことなくコストを削減できます。同様に、リソースを追加することによって、トラフィックの増大時でも良好なユーザー エクスペリエンスを維持できます。このようにして、アプリが需要を満たすうえで必要なリソースのみを使用するようにできます。

Google Cloud には、スケーラブルで効率性に優れたアプリを構築する際に有用なプロダクトと機能が用意されています。

  • Compute Engine 仮想マシンと Google Kubernetes Engine(GKE)クラスタは、定義した指標に基づいてリソース消費量を増減できるオートスケーラーと統合されます。
  • Google Cloud のサーバーレス プラットフォームには、ゼロから大量の場合まで、リクエスト量に応じて迅速にスケールするマネージド コンピューティング、データベースなどのサービスが用意されており、お支払いは従量制です。
  • BigQuerySpannerBigtable などのデータベース プロダクトは、非常に広範囲なデータサイズに対応して一貫したパフォーマンスを実現できます。
  • Cloud Monitoring には、さまざまなアプリとインフラストラクチャで使用できる指標が用意されており、スケーリングに関するデータドリブンの決定を行うことができます。

復元性: 障害に強い設計

復元性の高いアプリとは、システム コンポーネントに障害が発生した場合でも機能し続けるアプリを指します。復元性を実現するには、アーキテクチャのあらゆるレベルで計画を行う必要があります。復元性は、インフラストラクチャとネットワークの配置方法、アプリとデータ ストレージの設計方法にまで影響します。また、復元性の影響範囲にはユーザーと文化も含まれます。

復元性の高いアプリを作成し運用することは容易ではありません。このことは特に、インフラストラクチャ、ネットワーク、サービスの複数のレイヤを有する可能性がある分散アプリの場合に該当します。ミスやサービスの停止はつきものです。また、アプリの復元性は継続的に改善し続ける必要があります。計画を慎重に行うことによって、障害に対応するアプリの能力を改善できます。適切なプロセスと組織文化によって、障害から得られた知見をもとにアプリの復元性をさらに高めることもできます。

Google Cloud は、可用性が高く復元性に優れたアプリを構築する際に有用なツールとサービスを備えています。

  • Google Cloud サービスは、世界各地のリージョンとゾーンで利用でき、可用性の目標を最も良く満たすようにアプリをデプロイできます。
  • Compute Engine インスタンス グループと GKE クラスタは、1 つのリージョン内の利用可能な任意のゾーンで分散管理できます。
  • Compute Engine のリージョン永続ディスクは、リージョン内のゾーン間で同期的に複製されます。
  • Google Cloud には、ユーザーに最も近い正常なリージョンにトラフィックを転送できるグローバルなロード バランシングなど、アプリのトラフィックを管理するためのさまざまなロード バランシング オプションが用意されています。
  • Google Cloud のサーバーレス プラットフォームには、冗長性とロード バランシングの組み込み機能を備えたマネージド コンピューティングとデータベース プロダクトが用意されています。
  • Google Cloud は、ネイティブ ツールや一般的なオープンソース テクノロジーとの統合を通じて CI / CD をサポートし、アプリのビルドとデプロイの自動化を支援します。
  • Cloud Monitoring には、アプリとインフラストラクチャ全体で使用できる指標が用意されており、アプリのパフォーマンスと健全性に関するデータドリブンの意思決定を支援します。

推進要因と制約

アプリのスケーラビリティと復元性を改善する際の要件や動機にはさまざまなものがあります。また、スケーラビリティと復元性の目標を達成するにあたって、対応力を制限する制約が存在する場合もあります。これらの要件と制約の相対的な重要性は、アプリのタイプ、ユーザーのプロファイル、組織の規模と成熟度によって異なります。

推進要因

要件に優先順位を付けることができるよう、組織のさまざまな部門の推進要因を検討してください。

ビジネス推進要因

ビジネス的観点から見た場合の一般的な推進要因には次のものがあります。

  • コストとリソース消費を最適化する。
  • アプリのダウンタイムを最小限に抑える。
  • 使用率が増加してもユーザーの需要が満たされるようにする。
  • サービスの質と可用性を改善する。
  • 障害が発生した場合にも、ユーザーの操作性と信頼が維持されるようにする。
  • 変化する市場の需要に対応するための柔軟性とアジリティを高める。

開発の推進要因

開発サイドから見た場合の一般的な推進要因には次のものがあります。

  • 障害の調査に費やす時間を最小限に抑える。
  • 新機能の開発に費やす時間を増やす。
  • 自動化によって繰り返し作業を最小限に抑える。
  • 最新の業界のパターンと手法を活用してアプリを構築する。

運用の推進要因

運用側の観点から考慮すべき要件には次のものがあります。

  • 人間による介入を必要とする障害の発生頻度を低減する。
  • 障害から自動的に復旧する機能を強化する。
  • 自動化によって繰り返し作業を最小限に抑える。
  • 特定のコンポーネントの障害による影響を最小限に抑える。

制約

制約により、アプリのスケーラビリティと復元性の向上が制限される場合があります。設計上の決定により以下の制約が生じる、あるいは制約の発生に寄与することがないようにします。

  • スケーリングが困難なハードウェアまたはソフトウェアに対する依存関係。
  • 高可用性の構成での運用が困難なハードウェアまたはソフトウェアに対する依存関係。
  • アプリ間の依存関係。
  • ライセンス上の制限。
  • 開発チームおよび運用チームのスキル不足や経験不足。
  • 自動化に対する組織の抵抗。

パターンと手法

このドキュメントの残りの部分では、復元性が高くスケーラブルなアプリを構築する際に有用なパターンと手法を定義します。これらのパターンは、インフラストラクチャの設計、アプリのアーキテクチャ、ストレージの選択、デプロイ プロセス、組織文化など、アプリのライフサイクル全体に影響します。

これらのパターンには、次の 3 つの明確なテーマがあります。

  • 自動化。スケーラブルで復元性の高いアプリを構築するには、自動化が必要です。インフラストラクチャのプロビジョニング、テスト、アプリのデプロイを自動化することで、整合性と速度が向上し、人的エラーが最小限に抑えられます。
  • 疎結合。システムを一連の疎に結合された独立したコンポーネントとして扱うことにより、柔軟性と復元性がもたらされます。独立性を原則として、リソースの物理的な分散方法とアプリを構築しストレージを設計する方法を決定します。
  • データドリブンの設計。アプリの動作を理解するために指標を収集することは非常に重要です。アプリをスケーリングするタイミング、または特定のサービスが正常ではない状態にあるかどうかについての決定は、データに基づいて行う必要があります。指標とログは中核機能として位置づける必要があります。

インフラストラクチャのプロビジョニングを自動化する

自動化によって不変のインフラストラクチャを作成して環境の一貫性を改善し、より多くのデプロイを成功へと導きます。

インフラストラクチャをコードとして扱う

Infrastructure as Code(IaC)は、インフラストラクチャのプロビジョニングと構成をアプリケーション コードの処理と同じように扱うことを推奨する手法です。プロビジョニングと構成のロジックはソース管理に保存されるため、検出可能でありバージョン管理と監査を行うことができます。コード リポジトリに存在するため、継続的インテグレーションと継続的デプロイ(CI / CD)パイプラインの利点を活用し、構成に対する変更を自動的にテストしてデプロイできます。

インフラストラクチャのプロビジョニングから手動の手順を排除し、IaC により人的エラーを最小限に抑えることで、アプリと環境の一貫性および再現性が向上します。このように、IAC を採用することでアプリの復元性が高まります。

Cloud Deployment Manager では、柔軟なテンプレートを使用して Google Cloud リソースの作成と管理を自動化できます。また、Config Connector を使用すると、Kubernetes の手法とワークフローを使用してリソースを管理できます。Google Cloud には、TerraformChefPuppet などの一般的なサードパーティ IaC ツールに対するサポートも組み込まれています。

不変のインフラストラクチャを作成する

不変のインフラストラクチャは、Infrastructure as Code のメリットに基づいた考え方です。不変のインフラストラクチャでは、デプロイ後にリソースを変更しないことが義務付けられています。仮想マシン、Kubernetes クラスタ、ファイアウォール ルールを更新する必要がある場合は、ソース リポジトリでリソースの構成を更新できます。変更をテスト、検証した後に新しい構成を使用してリソース全体を再デプロイします。すなわち、リソースを微調整するのではなく、リソースを再作成します。

不変のインフラストラクチャを作成することによって、デプロイとロールバックの予測可能性が向上します。これにより、構成のずれやスノーフレーク サーバーなど、可変インフラストラクチャに一般的に見られる問題も軽減されます。このようにして、不変インフラストラクチャを採用することによって環境の一貫性と信頼性がさらに向上します。

高可用性を重視した設計

可用性とは、サービスが使用できる時間の割合の尺度です。可用性は、サービス全体の正常性の主な指標としてよく使用されます。高可用性アーキテクチャは、一般的にコンポーネントを冗長的にデプロイすることで、サービスの可用性を最大化することを目的としています。端的に表現すれば、高可用性のアーキテクチャでは通常、コンピューティング リソースの分散、ロード バランシングやデータのレプリケーションによって高可用性が実現されています。

リソースを物理的に分散する

Google Cloud サービスは世界中のロケーションで利用可能です。これらのロケーションは、「リージョン」と「ゾーン」に分けられます。アプリの可用性やレイテンシなどの特性に及ぼす影響は、これらのリージョンとゾーンにアプリをどのようにデプロイするかに依存します。詳細については、Compute Engine のリージョン選択に関するおすすめの方法をご覧ください。

冗長性とは対象システムの全般的な可用性の向上を目的として、システムのコンポーネントを重複して配置することを指します。Google Cloud では冗長性は通常、アプリやサービスを複数のゾーン、場合によっては複数のリージョンにデプロイすることによって実現されます。サービスが複数のゾーンやリージョンに存在する場合、特定のゾーンやリージョンで発生するサービス障害への対応力が強化されます。Google Cloud では、そうした障害の発生を回避するよう万全を期していますが、ある種のイベントは予測不可能であり、あらかじめ備えておくのが最善と言えます。

Compute Engine のマネージド インスタンス グループを使用すると、仮想マシンのインスタンスをリージョン内の複数のゾーンに分散し、論理ユニットとして管理できます。Google Cloud には、リージョン内の 2 つのゾーンにデータを自動的に複製するリージョン永続ディスクも用意されています。

同様にリージョン クラスタを作成することによっても、GKE にデプロイされたアプリの可用性と復元性を改善できます。リージョン クラスタは、リージョン内の複数のゾーンに GKE コントロール プレーン コンポーネント、ノード、Pod を分散します。コントロール プレーン コンポーネントは分散されているため、1 つ以上の(ただし全部ではない)ゾーンが関係するサービスの停止中であっても、クラスタのコントロール プレーンには引き続きアクセスできます。

マネージド サービスを使用する

マネージド サービスを使用すると、アプリケーション スタックのすべての部分を個別にインストールしてサポートや運用を行うのではなく、アプリケーション スタックの一部をサービスとして使用できます。たとえば、MySQL データベースを仮想マシン(VM)にインストールして管理する代わりに、Cloud SQL に用意された MySQL データベースを使用できます。その場合は可用性に関するサービスレベル契約(SLA)を取得し、Google Cloud を利用してデータの複製、バックアップ、基盤となるインフラストラクチャを管理できます。マネージド サービスを利用することにより、インフラストラクチャの管理に費やす時間を削減し、アプリの信頼性の向上により多くの時間を割り当てることができます。

Google Cloud のマネージド型のコンピューティング、データベース、ストレージ サービスの多くは組み込みの冗長性を備えており、それによって可用性の目標の達成を支援できます。これらのサービスの多くには、リージョン モデルが用意されています。つまり、アプリを実行するインフラストラクチャは特定のリージョンに配置されており、対象リージョン内のすべてのゾーンで冗長的に利用できるように Google が管理しています。あるゾーンが使用不可能になった場合、アプリやデータは自動的にリージョン内の別のゾーンからサービスを配信します。

特定のデータベースとストレージ サービスもマルチリージョンにおける可用性を備えています。つまり、アプリを実行するインフラストラクチャは複数のリージョンに配置されています。マルチリージョンのサービスは、リージョン全体が消失してもサービスを継続できますが、通常はレイテンシの増加を伴います。

各階層での負荷分散

ロード バランシングではリソースのグループ間でトラフィックを分散できます。トラフィックを分散することで、他のリソースがアイドル状態である間に個々のリソースが過負荷状態になることを回避できます。ほとんどのロードバランサにはヘルスチェック機能が備わっており、正常ではない、あるいは利用できないリソースにトラフィックが転送されることを回避できます。

Google Cloud には、いくつかのロード バランシング オプションが用意されています。アプリが Compute Engine または GKE で実行されている場合は、トラフィックのタイプやソースなどの要素に応じて、最適なタイプのロードバランサを選択できます。詳細については、ロード バランシングの概要と GKE のネットワーキングの概要をご覧ください。

また、App Engine や Cloud Run などの一部の Google Cloud マネージド サービスは、トラフィックを自動的にロードバランスします。

ウェブ クライアントやモバイル クライアントなどの外部ソースから受信したリクエストをロードバランスするのが一般的な方法です。ただし、アプリ内の異なるサービスまたは階層間でロードバランサを使用することによっても、復元性と柔軟性を高めることができます。Google Cloud では、この目的のために内部のレイヤ 4レイヤ 7 を対象にロード バランシングを行います。

次の図は、us-central1asia-east1 の 2 つのリージョンでグローバル トラフィックを分散する外部ロードバランサを示しています。この図は、ウェブ階層から受信したトラフィックを各リージョン内の内部階層に分散する内部のロード バランシングについても示しています。

リージョン間でのグローバル トラフィックの分散。

インフラストラクチャとアプリをモニタリングする

アプリの復元性とスケーラビリティを改善する方法を決定する前に、アプリの動作を理解する必要があります。アプリのパフォーマンスと状態に関連する一連の指標と時系列の全体に対するアクセス権を割り当てられていることによって、サービス停止を引き起こす前に潜在的な問題を発見できる場合があります。サービス停止が発生した場合には、その診断と解決に対しても有用な可能性があります。Google の SRE ブックでは、分散システムのモニタリングの章において、モニタリングのいくつかの手法について概要を示しています。

アプリの状態についての分析情報を示すのみでなく、指標を使用して自動スケーリング時のサービスの動作を制御することもできます。

Cloud Monitoring は Google Cloud の統合モニタリング ツールです。Cloud Monitoring は、イベント、指標、メタデータを取り込み、ダッシュボードとアラートで分析情報を提供します。ほとんどの Google Cloud サービスは指標を自動的に Cloud Monitoring に送信します。また、Google Cloud は多くのサードパーティのソースにも対応しています。Cloud Monitoring は、一般的なオープンソースのモニタリング ツールのバックエンドとしても使用でき、1 つの画面からアプリをモニタリングできます。

すべてのレベルでモニタリングする

アーキテクチャ内のさまざまなレベルや階層で指標を収集することで、アプリの正常性と動作に関する全体像を把握できます。

インフラストラクチャのモニタリング

インフラストラクチャ レベルのモニタリングにより、アプリのベースラインの正常性とパフォーマンスに関する情報を取得できます。このモニタリング手法によって CPU 負荷、メモリ使用量、ディスクへの書き込みバイト数などの情報を取得できます。これらの指標は、マシンが過負荷であるか、想定どおりに機能していないことを示すことがあります。

また、Cloud Monitoring では、自動的に収集される指標に加えて、Compute Engine VM(これらのマシンで実行されているサードパーティ アプリなど)からより詳細な情報を収集するためにインストールできるエージェントが提供されています。

アプリのモニタリング

アプリレベルの指標をキャプチャすることをおすすめします。たとえば、特定のクエリを実行するのに要する時間や、関連付けられた一連のサービス呼び出しを実行するのに要する時間を測定できます。このようなアプリレベルの指標は、お客様自身により定義されます。これらの指標は、組み込みの Cloud Monitoring 指標ではキャプチャできない情報です。アプリレベルの指標では、主要なワークフローをより詳細に反映した集約された状態をキャプチャでき、下位レベルのインフラストラクチャの指標で明らかにすることができない問題を特定できます。

また、OpenTelemetry を使用してアプリレベルの指標を取得することもおすすめします。OpenTelemetry は、テレメトリー データ用の単一のオープン スタンダードを提供します。OpenTelemetry を使用して、クラウド ファーストのアプリケーションとインフラストラクチャからデータを収集し、エクスポートします。その後、エクスポートされたテレメトリー データをモニタリングして分析できます。

サービスのモニタリング

分散アプリやマイクロサービス主導のアプリの場合は、アプリ内のさまざまなサービスやコンポーネント間のやり取りをモニタリングすることが重要です。これらの指標は、エラー数の増加やサービス間のレイテンシなどの問題を診断する際に有用です。

Istio は、マイクロサービスのネットワークに分析情報と運用制御を提供するオープンソース ツールです。Istio は、すべてのサービス通信の詳細なテレメトリーを生成し、Cloud Monitoring に指標を送信するように構成できます。

エンドツーエンドのモニタリング

エンドツーエンドのモニタリング(ブラックボックス モニタリング)は、外部に表示可能な動作をユーザーに対して表示される場合と同じ方法でテストします。この種類のモニタリングでは、定義したしきい値の範囲内でユーザーが重要な操作を完了できるかどうかを確認します。この大まかなモニタリングによって、よりきめ細かいモニタリングでは発見できない場合があるエラーやレイテンシを発見でき、ユーザーの視点で可用性を明らかにできます。

アプリの状態を公開する

高可用性を備えたシステムには、システムの中で正常な状態であり正しく機能している部分を特定するための、なんらかの手法が必要です。特定のリソースが正常ではないと判断される場合に、他の場所にリクエストを送信できます。通常、ヘルスチェックではエンドポイントからデータが取得され、サービスのステータスや正常性が特定されます。

ヘルスチェックは、ロードバランサに課された重要な処理です。仮想マシン インスタンスのグループに関連付けられたロードバランサを作成した場合は、ヘルスチェックの定義も行う必要があります。ヘルスチェックでは、ロードバランサが仮想マシンとの通信を行い、特定のインスタンスがトラフィックの受信を継続するべきかどうかについて評価する方法を定義します。ロードバランサのヘルスチェックは、インスタンスのグループを自動修復し、正常ではないマシンを再作成する目的でも使用できます。GKE で実行し Ingress リソースを使用して外部トラフィックのロード バランシングを実施している場合は、GKE が自動的にロードバランサに適したヘルスチェックを作成します。

Kubernetes には Liveness Probe と Readiness Probe に対する組み込みのサポートが用意されています。これらのプローブは、Kubernetes のオーケストレーターがクラスタ内での Pod とリクエストの管理方法を決定するのを支援します。アプリが Kubernetes にデプロイされている場合は、適切なエンドポイントを経由してこれらのプローブにアプリの状態を公開することをおすすめします。

主要な指標を設定する

モニタリングとヘルスチェックにより、アプリの動作とステータスに関する指標を取得できます。次のステップでは、これらの指標を分析して、最も適切に状況を説明する、あるいは影響が大きい指標を特定します。主要な指標は、アプリがデプロイされているプラットフォームや、アプリが実行している作業に応じて異なります。

アプリをスケールするかどうか、あるいは特定のサービスが正常かどうかを示す指標を 1 つのみに絞って特定できない場合があります。多くの場合、一連の特定の状況は要因の組み合わせによって表現されます。Cloud Monitoring では、カスタム指標を作成してそうした状況をキャプチャできます。Google SRE ブックでは、ユーザー向けシステムのモニタリングにおける 4 つのゴールデン シグナル(レイテンシ、トラフィック、エラー、飽和度)を推奨しています。

また、外れ値に対する許容度についても検討してください。平均値や中央値を使用して正常性やパフォーマンスを測定することが最適な選択ではない場合があります。これは、測定で広範囲にわたる不均衡が見落とされる可能性があるためです。そのため、指標の分布を考慮することが重要です。平均値よりも 99 パーセンタイルのほうが有益な情報である場合があります。

サービスレベル目標(SLO)を定義する

モニタリング システムにより収集された指標を使用して、サービスレベル目標(SLO)を定義できます。SLO は、サービスのパフォーマンスや信頼性の目標レベルを指定します。SLO は SRE プラクティスの重要な要素であり、SRE ブックのサービスレベル目標の章と SRE ワークブックの SLO の実装の章で詳細に説明されています。

サービスのモニタリングを使用すると、Cloud Monitoring の指標に基づいて SLO を定義できます。SLO のアラート ポリシーを作成すると、SLO に違反する可能性があるかどうかについて通知を受け取れます。

指標を保存する

短期的な観点で見た場合、モニタリング システムから取得した指標は、リアルタイムのヘルスチェックを行う際や直近の問題を調査する際に有用です。Cloud Monitoring は、このようなユースケースに最適な対応を行えるよう、数週間の間指標を保持します。

一方で、長期間の分析を目的としてモニタリング指標を保存することにも価値があります。履歴レコードにアクセスできるようにすることで、データドリブンの手法を採用してアプリのアーキテクチャを絞り込むことができます。サービス停止の最中またはその後に収集したデータを使用して、アプリにおけるボトルネックと相互依存関係を特定できます。このデータを使用して有意義なテストを作成、検証することもできます。

履歴データは、重要となる期間内においてアプリがビジネス目標をサポートしているかどうかを検証する際にも有用です。たとえば、過去数四半期、あるいは過去数年間にわたってトラフィック量が増大するプロモーション イベント中にアプリがどのようにスケーリングされたかを分析する際に、履歴データを活用できます。

指標をエクスポートして保存する方法の詳細については、Cloud Monitoring 指標のエクスポートに関するソリューションをご覧ください。

スケーリング プロファイルを特定する

リソースの過剰なプロビジョニングを行わずに、ユーザー エクスペリエンスとパフォーマンスの目標を達成することが理想的です。

次の図は、アプリのスケーリング プロファイルを簡略化して示しています。アプリはベースライン レベルのリソースを維持し、自動スケーリングを使用して需要の変化に対応しています。

アプリのスケーリング プロファイル。

コストとユーザー エクスペリエンスのバランスを取る

アプリをスケーリングするかどうかの決定とは、基本的にはコストとユーザー エクスペリエンスのバランスを取ることです。パフォーマンスの最小許容レベルを決定し、場合によっては上限をどこに設定するかも決定します。これらのしきい値はアプリによって異なります。また、単一のアプリ内のさまざまなコンポーネントやサービスによっても異なる場合があります。

たとえば、消費者向けウェブアプリやモバイルアプリの場合、レイテンシに関する厳格な目標を設定するはずです。わずかな遅延でもアプリに対するユーザー評価に否定的な影響が生じ、その結果コンバージョン率が低下し登録数が減少することが調査によって示されています。そのため、ユーザーのリクエストに迅速に対応できるよう、アプリに十分な処理能力を持たせることが重要です。この場合は、稼働するウェブサーバー数の増加に伴うコスト増加は正当化されるはずです。

わずかな遅延であれば許容される可能性が高い、ビジネス上の重要性が低い内部アプリであれば、コスト パフォーマンスが異なる場合があります。そのため、スケーリング プロファイルはそれほどアグレッシブな動きにならないはずです。この場合は、ユーザー エクスペリエンスの最適化よりも、コストの抑制が重要となるでしょう。

ベースライン リソースを設定する

スケーリング プロファイルのもう 1 つの重要な要素は、適切な最小リソースセットを決定することです。

Compute Engine 仮想マシンや GKE クラスタでは通常、スケールアップに時間を要します。これは、新しいノードを作成し初期化する必要があるためです。そのため、トラフィック量がゼロの場合でも最小限のリソースのセットを維持することが必要な場合があります。ここでも、ベースライン リソースの範囲は、アプリのタイプやトラフィックのプロファイルに依存します。

逆に App Engine、Cloud Run 関数、Cloud Run などのサーバーレス技術では、ゼロ状態へのスケーリングや、コールド スタートしたインスタンスの迅速な起動・スケーリングができるように設計されています。アプリのタイプとトラフィックのプロファイルによっては、これらの技術によりアプリの部分的な効率を改善できます。

自動スケーリングを構成する

自動スケーリングでは、アプリが使用するコンピューティング リソースを自動的にスケールできます。通常、自動スケーリングは特定の指標がしきい値を超えた、または条件が満たされた場合に行われます。たとえば、ウェブ階層へのリクエストのレイテンシが一定値を超過し始めた場合は、マシンを自動的に追加して処理能力を増強できます。

多くの Google Cloud コンピューティング プロダクトは、自動スケーリング機能を備えています。Cloud Run、Cloud Run 関数、App Engine などのサーバーレスのマネージド サービスは、迅速にスケーリングできるように設計されています。これらのサービスには通常、自動スケーリングの動作に対して制限を加える、または影響する構成オプションが用意されていますが、一般に、オートスケーラーの動作のほとんどはオペレーターにより確認することができません。

Compute Engine と GKE には、スケーリングの動作を制御するためのその他のオプションが用意されています。Compute Engine では、Cloud Monitoring のカスタム指標とロードバランサの処理能力などのさまざまな入力に基づいてスケーリングできます。スケーリングの動作に下限と上限を設定して、複数のシグナルを使用する自動スケーリング ポリシーを定義し、さまざまなシナリオを処理できます。GKE ではワークロード、Pod の指標、またはクラスタ外部の指標に基づいてノードを追加あるいは削除するようにクラスタのオートスケーラーを構成できます。

自動スケーリングの動作は、主要なアプリ指標、コスト プロファイル、定義したリソースの最小要件レベルに基づいて構成することをおすすめします。

起動時間を最小限に抑える

スケーリングを効果的に行うには、負荷の増加に十分対応できるよう迅速にスケーリングを行う必要があります。これは特に、コンピューティング容量や処理能力を追加する場合に当てはまります。

事前に作成済みのイメージを使用する

アプリが Compute Engine VM で実行されている場合は、ソフトウェアをインストールしてアプリを実行するインスタンスを構成することが必要な可能性があります。起動スクリプトを使用して新規のインスタンスを構成することはできますが、より効率的な方法はカスタム イメージを作成することです。カスタム イメージは、アプリ固有のソフトウェアと構成によって設定するブートディスクです。

イメージの管理について詳しくは、イメージ管理のベスト プラクティスの記事をご覧ください。

イメージを作成したら、インスタンス テンプレートを定義できます。インスタンス テンプレートは、ブートディスク イメージやマシンタイプなどのインスタンス プロパティを組み合わせたものです。インスタンス テンプレートを使用して、個別の VM インスタンスやマネージド インスタンス グループを作成できます。インスタンス テンプレートは、VM インスタンスの構成を保存する便利な方法であり、これを後から使用して同一の新しい VM インスタンスを作成できます。

カスタム イメージとインスタンス テンプレートを作成することによってデプロイをより迅速に行えるようになりますが、メンテナンス コストが増加する場合があります。これは、イメージをより高頻度で更新する必要性が生じる場合があるためです。詳細については、イメージ構成とデプロイ速度のバランスを取るのドキュメントをご覧ください。

アプリをコンテナ化する

カスタマイズされた VM インスタンスをビルドする代わりに、アプリをコンテナ化することもできます。コンテナは、アプリの実行に必要なすべての要素(コード、ランタイム、システムツール、システム ライブラリ、設定)を含む、軽量かつスタンドアロンの実行可能なソフトウェアのパッケージです。このような特性によって、コンテナ化されたアプリがさらにポータブルになり、仮想マシンと比べてデプロイや大規模な保守をより簡単に行うことができます。コンテナは通常、起動も速く、スケーラブルで復元性の高いアプリに適しています。

Google Cloud には、アプリコンテナを実行するためのサービスがいくつか用意されています。Cloud Run は、ステートレス コンテナをホストするサーバーレスのマネージド コンピューティング プラットフォームを備えています。App Engine フレキシブル環境では、マネージドの PaaS(サービスとしてのプラットフォーム)でコンテナをホストします。GKE は、コンテナ化されたアプリをホストおよびオーケストレートするためのマネージド Kubernetes 環境を備えています。コンテナ環境を完全に制御する必要がある場合は、Compute Engine でアプリコンテナを実行することもできます。

アプリを最適化して起動時間を短縮する

インフラストラクチャとアプリを可能な限り効率的にデプロイできるようにするだけでなく、アプリを迅速にオンラインにすることも重要です。

アプリに適した最適化は、アプリの特性と実行プラットフォームによって異なります。次のことを行うことが重要です。

  • 起動時に呼び出されるアプリの重要なセクションをプロファイリングすることで、ボトルネックを見つけて排除する。
  • 特に高コストなリソースに対しては遅延初期化などの手法を実装して、初回起動時間を短縮する。
  • 起動時に読み込む必要性が生じる可能性があるアプリの依存関係を最小限に抑える。

モジュール式アーキテクチャを採用する

コンポーネントを個別にデプロイ、管理、スケーリングできるアーキテクチャを選択することで、アプリの柔軟性を高めることができます。このパターンでは、単一障害点を排除することで復元性を改善することもできます。

アプリを独立したサービスに分割する

アプリを疎結合の一連の独立したサービスとして設計すると、アプリの柔軟性を高めることができます。疎結合の設計を採用することで、サービスを個別にリリースしてデプロイできるようになります。他の多くの利点に加えて、この手法ではこれらのサービスがさまざまな技術スタックを使用し、異なるチームによって管理されるようにできます。この疎結合を用いた手法は、マイクロサービスや SOA などのアーキテクチャ パターンの重要なテーマです。

サービスを区分する方法を検討するうえで、可用性とスケーラビリティの要件は重要な要素です。たとえば、特定のコンポーネントの可用性要件またはスケーリング プロファイルが他のコンポーネントと異なる場合、スタンドアロン サービスとすることが最適な可能性があります。

ステートレスを指向する

ステートレスなアプリやサービスは、永続的なデータや状態をローカルで保持しません。ステートレス モデルでは、前のリクエストと独立させて、各リクエストやサービスとのやり取りを処理できます。このモデルでは、スケーラビリティと復元性を向上できます。これは、進行中のプロセスやリクエストに対応するために必要なデータを失うことなく、サービスを拡大、縮小、再起動できるためです。オートスケーラーを使用している場合、サービスをホストするインスタンス、ノード、Pod が予期せず作成および破棄される可能性があるため、ステートレスは特に重要となります。

すべてのサービスをステートレスなサービスとして運用することが不可能な場合があります。そのような場合は、ステートフルである必要のあるサービスを明示してください。ステートレスなサービスとステートフルなサービスを明確に区分することで、ステートレスなサービスでのスケーリングを容易にしつつ、ステートフルなサービスではより慎重な手法を採用できます。

サービス間の通信を管理する

分散マイクロサービス アーキテクチャの課題の 1 つは、サービス間の通信を管理することです。サービスのネットワークが拡大するにつれて、サービスの相互依存性も増大する可能性があります。1 つのサービスの障害が他のサービスの障害(カスケード障害とも呼ばれます)につながることは望ましくありません。

回路ブレーカー パターン、指数バックオフグレースフル デグラデーションなどの手法を採用することで、過負荷状態にあるサービスや障害が発生したサービスへのトラフィックを削減できます。これらのパターンでは、過負荷状態にあるサービスに回復の機会を与えることで、あるいはエラー状態を適切に処理することで、アプリの復元性を向上させます。詳細については、Google SRE ブックのカスケード障害への対処の章をご覧ください。

サービス メッシュを使用すると、分散サービス全体のトラフィックを管理できます。サービス メッシュは、サービスをリンクし、ビジネス ロジックをネットワーキングから分離できるようにするソフトウェアです。サービス メッシュは通常、リクエストの再試行、フェイルオーバー、回路ブレーカーなどの復元機能を備えています。

適切なデータベースとストレージ技術を使用する

一部のデータベースとストレージ タイプでは、スケーリングや復元性の付与が困難です。データベースの選択によってアプリの可用性とスケーラビリティに制約が生じないようにします。

データベースのニーズを評価する

一連の独立したサービスとしてアプリを設計するパターンは、データベースとストレージにも適用できます。アプリのさまざまな部分に異なるタイプのストレージを選択することが適切な場合がありますが、そうすると多様なストレージを構成することになります。

従来のアプリは多くの場合、リレーショナル データベースでのみ動作します。リレーショナル データベースは、トランザクション、強整合性、参照整合性、テーブル間の高度なクエリなどの有用な機能を備えています。これらの機能があるため、リレーショナル データベースは多くの一般的なアプリ機能に適した選択肢になります。ただし、リレーショナル データベースにはいくつかの制約もあります。通常、スケーリングは困難であり、また高可用性構成では慎重に管理する必要があります。リレーショナル データベースが、すべてのデータベースのニーズにおける最適な選択肢になるとは限りません。

NoSQL データベースとも呼ばれる非リレーショナル データベースでは、別のアプローチを採用しています。詳細はプロダクトによって異なりますが、NoSQL データベースは通常、可用性の向上とスケーリングを容易にすることと引き換えにリレーショナル データベースの一部の機能を犠牲にします。CAP 定理の観点から、NoSQL データベースでは多くの場合に整合性よりも可用性が優先されます。

NoSQL データベースが適切かどうかは、多くの場合、必要な整合性の程度によって決まります。特定のサービスのデータモデルが RDBMS のすべての機能を必要とすることはなく、設計によって結果整合性を実現できる場合は、NoSQL データベースを選択することで可用性とスケーラビリティを改善できる可能性があります。

データ マネジメントの分野では、リレーショナル データベースと非リレーショナル データベースは、競合するテクノロジーではなく、補完的なテクノロジーとして見なされることがよくあります。両方のタイプのデータベースを戦略的に使用することで、組織はそれぞれの強みを活かし、データのストレージ、取得、分析において最適な結果を得ることができます。

さまざまなリレーショナル データベースと NoSQL データベースに加えて、Google Cloud には、強整合性と高可用性を備え、SQL をサポートするグローバル分散データベースである Spanner も用意されています。Google Cloud で適切なデータベースを選択する方法については、Google Cloud データベースをご覧ください。

キャッシュを実装する

キャッシュの主な目的は、基盤となる低速のストレージ レイヤにアクセスする必要性を減らし、データ取得のパフォーマンスを高めることです。

キャッシュにより、ディスクベースのストレージへの依存度が低減されることで、スケーラビリティが向上します。リクエストをメモリで処理できるようになるため、ストレージ レイヤへのリクエストのレイテンシが短縮されます。通常はこれにより、より多くのリクエストを処理できるようになります。またキャッシュにより、アプリのダウンストリームであるサービス、特にデータベースの負荷が軽減され、ダウンストリーム サービスとやり取りする他のコンポーネントもさらに、あるいは完全にスケールできるようになります。

グレースフル デグラデーションなどの手法がキャッシュによりサポートされることでも復元性が向上します。基盤となるストレージ レイヤが過負荷になっている場合や利用できない場合でも、キャッシュは引き続きリクエストを処理できます。また、キャッシュから返されたデータが不完全である、あるいは最新ではないとしても、それが許容される場合もあります。

Memorystore for Redis は、Redis インメモリ データストアを利用したフルマネージド サービスを備えています。Memorystore for Redis は、アクセス頻度の高いデータにおける低レイテンシ アクセスと高スループットを実現します。クロスゾーン レプリケーションと自動フェイルオーバーを可能にする高可用性構成でデプロイできます。

開発プロセスと文化をモダナイズする

DevOps は、開発、運用、関連チーム間のサイロを解体することで、アプリと機能のアジリティを高め、市場投入までの時間を短縮する幅広い一連のプロセス、文化、ツールと見なすことができます。DevOps の手法は、ソフトウェアの品質と信頼性の向上を目的とします。

DevOps の詳細についてはこのドキュメントでは扱いませんが、アプリの信頼性と復元性の向上に関連する重要な点については、以降のセクションで説明します。詳細については、Google Cloud の DevOps ページをご覧ください。

テスト性を重視した設計

自動テストは、最新のソフトウェア デリバリー手法の重要な要素です。単体テスト、統合テスト、システムテストを一連の包括的なセットとして実行できることは、アプリが想定どおりに動作し、デプロイ サイクルの次の段階に進めることを確認するうえで非常に重要です。テスト性は、アプリの重要な設計基準です。

単体テストは迅速に実行でき、一般的に管理が容易なため、このテストを大部分のテストに使用することをおすすめします。また、より高度な統合とシステムテストを自動化することもおすすめします。Infrastructure as code の手法を採用すると、専用のテスト環境とリソースをオンデマンドで作成してテスト終了後に破棄できるため、テストを大幅に簡略化できます。

テスト範囲にあるコードベースの割合が増加すると、不確実性が減り、コードを変更するたびに信頼性が低下する可能性があります。テスト範囲が適切であれば、信頼性が許容レベルを下回る前により多くの変更を加えることができます。

自動テストは、継続的インテグレーションで欠かせないコンポーネントです。コードが commit されるたびに安定性の高い一連の自動テストを実行することによって、変更に対する迅速なフィードバックが得られ、ソフトウェアの品質と信頼性が向上します。Cloud Build などの Google Cloud のネイティブ ツール、Jenkins などのサードパーティ製ツールによって継続的インテグレーションを実装できます。

デプロイを自動化する

継続的インテグレーションと包括的なテストの自動化により、ソフトウェアの安定性を確保できます。安定性が確保できたら、次のステップとしてアプリのデプロイを自動化します。デプロイの自動化のレベルは、組織の成熟度によって異なります。

適切なデプロイ計画を選択することは、新しいソフトウェアのデプロイに伴うリスクを最小限に抑えるうえで非常に重要です。適切な計画で、大規模なオーディエンスに徐々に新バージョンを公開でき、その過程で動作を検証できます。問題発生時のロールバックのための明確なプロビジョニングを設定することもできます。

障害に対処するための SRE プラクティスを採用する

大規模に動作する分散アプリの場合、1 つ以上のコンポーネントである程度の障害が発生することはよくあります。このドキュメントに記載されているパターンを採用すると、ソフトウェア リリースの不具合、仮想マシンの予期しない終了、ゾーン全体に影響するインフラストラクチャの停止による中断に対するアプリの対応力が向上します。

ただし、アプリを慎重に設計した場合であっても、人間による介入を必要とする予期しないイベントが発生する可能性があります。このようなイベントに対応するための構造化されたプロセスがあれば、イベントによる影響を大幅に軽減し、より迅速に解決できるようになります。さらに、イベントの原因と対応について調査することで、以後の類似するイベントからアプリを保護できるようになります。

インシデントの管理過失を責めない事後検証を実行するための強力なプロセスは、SRE の重要な原則です。Google SRE のすべての手法を実装することは組織にとって現実的ではありませんが、最小限のガイドラインを採用することによっても、アプリの復元性を高めることができます。SRE ブックの付録には、プロセスの形成に際して有用ないくつかのテンプレートが含まれています。

アーキテクチャの検証とレビュー

アプリの進化に伴い、ユーザーの行動、トラフィック プロファイル、さらにはビジネス上の優先事項も変化する可能性があります。同様に、アプリが依存する他のサービスやインフラストラクチャも進化することが考えられます。そのため、アプリの復元性とスケーラビリティを定期的にテストして検証することが重要です。

復元性をテストする

アプリが想定どおりに障害に応答することをテストすることが重要です。包括的テーマは、「障害を回避する最良の方法は、障害を経験し、そこから学ぶこと」です。

障害のシミュレーションと導入は複雑です。アプリやサービスの動作を確認することに加え、想定されるアラートが生成され、適切な指標が生成されることを確認する必要もあります。単純な障害を導入してからエスカレーションする体系的アプローチをおすすめします。

たとえば、次の手順を踏むことで、各段階での動作を検証して文書化できます。

  • 一時的な障害を導入する。
  • サービスの依存関係へのアクセスをブロックする。
  • すべてのネットワーク通信をブロックする。
  • ホストを終了する。

詳細については、Google Cloud Next 2019 の分割によりシステムを分割できないようにするの動画をご覧ください。

Istio などのサービス メッシュを使用してアプリサービスを管理している場合は、Pod やマシンを強制終了する代わりにアプリケーション レイヤに対してフォールト インジェクションを行うか、TCP レイヤでパケットを破損させることができます。遅延を導入して、ネットワーク レイテンシやアップストリーム システムの過負荷をシミュレートできます。また、アップストリーム システムでの障害を模した中断を導入することもできます。

スケーリングの動作をテストする

機能面以外の自動化テストを使用して、アプリが想定どおりにスケーリングするかどうかを検証することをおすすめします。多くの場合、この検証はパフォーマンスや負荷のテストと組み合わせて実行されます。hey などのシンプルなツールを使用して、負荷をウェブアプリに送信できます。REST エンドポイントに対して負荷テストを実施する方法を示す詳細な例については、Google Kubernetes Engine を使用した負荷分散テストをご覧ください。

一般的な方法の 1 つは、負荷が変動する状況で重要な指標が想定レベル内に収まるかどうかを確認することです。たとえば、ウェブ階層のスケーラビリティをテストする場合は、ユーザー リクエストが急増する状況でリクエスト レイテンシの平均値を測定することが考えられます。同様に、バックエンドでの処理機能については、タスクの量が急増する状況でタスク処理の平均時間を計測することが考えられます。

また、テスト負荷を処理するために作成されたリソースの数が想定範囲内であることをテストで測定します。たとえば、一部のバックエンド タスクを処理するために作成された VM の数が特定の値を超えないことをテストで確認できます。

エッジケースをテストすることも重要です。たとえば、スケーリングの上限に達した場合のアプリやサービスの動作や、サービスをスケールダウンした後、負荷が再び急増した場合の動作などがあります。

設計に取り組み続ける姿勢

技術の世界の移り変わりは激しく、このことは特にクラウドにおいて当てはまります。頻繁に新しいプロダクトと機能がリリースされ、新たなパターンが生まれ、ユーザーや内部の関係者からの需要は増大し続けます。

クラウドネイティブ アーキテクチャの原則のブログ投稿で定義されているように、アプリのアーキテクチャを見直して簡素化し、改善する方法を常に模索する必要があります。ソフトウェア システムは生きものであり、変化し続ける優先事項を反映できるよう対応する必要があります。

次のステップ