アプリにおけるレイテンシの増加をトラブルシューティングする

多くの場合、アプリケーションのレイテンシが増加すると、最終的に 5xx サーバーエラーが発生します。エラーとレイテンシの急増の根本原因が同じである場合があるため、レイテンシの問題のトラブルシューティングには次の方法を適用します。

  1. レイテンシの問題の範囲を定める
  2. 原因を特定する
  3. トラブルシューティング

レイテンシの問題の範囲を定める

次の問題を考慮して、問題の範囲を定めます。

  • この問題はどのアプリケーション、サービス、バージョンに影響しているか。
  • この問題はサービスのどのエンドポイントに影響しているか。
  • すべてのクライアントに全体的に影響しているか、それとも特定のクライアント群に影響しているか。
  • インシデントの開始時刻と終了時刻はいつか。タイムゾーンを指定することを検討してください。
  • 具体的にどのようなエラーが発生しているか。
  • レイテンシにどれほどの差分が認められ、特定のパーセンタイルでの増加として通常示されるものはどれか。たとえば、90 パーセンタイルでレイテンシが 2 秒増加したなどです。
  • レイテンシをどのように測定したか。具体的には、レイテンシをクライアントで測定したか、または Cloud Logging や App Engine サービス提供インフラストラクチャによって提供される Cloud Monitoring のレイテンシ データでレイテンシを確認できるか。
  • サービスにどのような依存関係があり、依存関係のいずれかにインシデントが発生しているか。
  • この問題を引き起こすコード、構成、ワークロードの変更を最近行ったか。

サービス独自のカスタム モニタリングとロギングを使用すると、問題の範囲をさらに絞り込むことができます。問題の範囲を定義することで、考えられる根本原因を特定し、次のトラブルシューティング手順を決定できます。

原因を特定する

リクエストパス内のどのコンポーネントがレイテンシまたはエラーの原因となっているかを判断します。リクエストパスの主なコンポーネントは次のとおりです。

クライアント --> インターネット --> Google Front End(GFE)-> App Engine サービス提供インフラストラクチャ --> サービス インスタンス

上記の情報で障害の原因を特定できない場合は、サービス インスタンスの正常性とパフォーマンスを確認しながら、次の方法を適用します。

  1. App Engine リクエスト ログをモニタリングします。これらのログに HTTP ステータス コードエラーまたはレイテンシ増加が見られた場合、サービスを実行しているインスタンスに問題がある可能性があります。

  2. サービス インスタンスの数がトラフィック レベルに合わせてスケールアップされていない場合、インスタンスが過負荷状態になり、エラーとレイテンシが増加する可能性があります。

  3. Cloud Monitoring でエラーまたはレイテンシの増加が発生している場合は、問題が App Engine の指標を記録するロードバランサの上流にある可能性があります。ほとんどの場合、これはサービス インスタンスに問題があることを示しています。

  4. モニタリング指標でレイテンシの増加やエラーが見られてもリクエストログでは見られない場合は、ロード バランシングの障害か、ロードバランサによるリクエストのルーティングを妨げる重大なインスタンス障害が考えられます。これらの事例を区別するには、インシデントが始まる前のリクエストログを確認します。リクエストログで障害発生前にレイテンシが増加している場合は、ロードバランサがリクエストのルーティングを停止する前にアプリケーション インスタンスで障害が発生し始めていたこととなります。

トラブルシューティング

このセクションでは、リクエストパスの次のコンポーネントによるレイテンシの増加に関する問題のトラブルシューティング方法について説明します。

  1. インターネット
  2. Google Front End(GFE)
  3. App Engine サービス提供インフラストラクチャ
  4. アプリケーション インスタンス
  5. アプリケーションの依存関係

インターネット

接続が不安定な場合や帯域幅が低い場合、アプリケーションでレイテンシの問題が発生することがあります。

インターネット接続が不安定

インターネット接続が原因になっているかどうかを確認するには、クライアントで次のコマンドを実行します。

$ curl -s -o /dev/null -w '%{time_connect}\n' <hostname>

time_connect の値は、最も近い Google Front End へのクライアント接続のレイテンシを表します。接続が遅い場合は、traceroute を使用してトラブルシューティングをさらに行い、ネットワーク上のどのホップが遅延の原因となっているかを判断します。

さまざまな地域のクライアントからテストを行ってください。App Engine は、最も近い Google データセンターにリクエストを自動的に転送しますが、Google データセンターはクライアントのロケーションによって異なります。

低い帯域幅

アプリケーションが迅速に応答している可能性がありますが、ネットワークのボトルネックにより、App Engine サービス提供インフラストラクチャでネットワーク全体にパケットが迅速に送信されず、レスポンスが遅くなります。

Google Front End(GFE)

ルーティングが正しくない場合、HTTP/2 クライアントから送信された並列リクエストの場合、または SSL 接続が終了した場合に、アプリケーションでレイテンシの問題が発生することがあります。

クライアント IP を地理的リージョンにマッピングする

Google は、DNS ルックアップで使用されているクライアント IP アドレスに基づいて、App Engine アプリケーションのホスト名を解決して、クライアントに最も近い GFE に変換します。クライアントの DNS リゾルバが EDNS0 プロトコルを使用していない場合、Google はクライアント リクエストを最も近い GFE にルーティングしないことがあります。

HTTP/2 ヘッドオブライン ブロッキング

複数のリクエストを並列で送信する HTTP/2 クライアントでは、GFE でのヘッドオブライン ブロッキングが原因でレイテンシが増加する場合があります。この問題を解決するには、クライアントが QUIC プロトコルを使用する必要があります。

カスタム ドメインの SSL 終端

GFE が SSL 接続を終端することがあります。appspot.com ドメインではなくカスタム ドメインを使用している場合、SSL 終端には追加のホップが必要です。これにより、一部のリージョンで動作しているアプリケーションのレイテンシが増加する可能性があります。詳細については、カスタム ドメインのマッピングをご覧ください。

App Engine サービス提供インフラストラクチャ

サービス全体のインシデントや自動スケーリングが原因で、アプリケーションのレイテンシが増加することがあります。

サービス全体のインシデント

Google は、サービス全体に重大な影響を与えるインシデントの詳細を Service Health ダッシュボードに投稿します。ただし Google は、段階的なロールアウトを行うため、サービス全体のインシデントがすべてのインスタンスに同時に影響を与えることはほとんどありません。

自動スケーリング

次の自動スケーリング シナリオでは、レイテンシの増加やエラーが発生する可能性があります。

  • トラフィックの増加が速すぎる: App Engine の自動スケーリングでは、トラフィックが増加してもインスタンスは同じ速さでスケーリングされないため、一時的に過負荷が発生することがあります。通常、過負荷は、エンドユーザーではなくコンピュータ プログラムによってトラフィックが生成された場合に発生します。この問題を解決するには、トラフィックを生成するシステムをスロットリングします。

  • トラフィックの急増: トラフィックが急増すると、レイテンシに影響を与えずに、自動スケーリングされるサービスが可能な速度よりも高速にスケールアップする必要がある場合に、レイテンシが増加することがあります。通常、エンドユーザーのトラフィックによるトラフィックの急増は頻繁には発生しません。トラフィックの急増が確認された場合は、原因を調査する必要があります。バッチシステムが一定の間隔で動作している場合は、トラフィックを平滑化するか、別のスケーリング設定を使用できることがあります。

  • オートスケーラーの設定: オートスケーラーは、サービスのスケーリング特性に基づいて構成できます。スケーリング パラメータは、次のシナリオでは最適でなくなることがあります。

    • App Engine スタンダード環境のスケーリング設定が厳しすぎると、レイテンシが発生する可能性があります。ログにステータス コード 500 と「Request was aborted after waiting too long to attempt to service your request」というメッセージを含むサーバーのレスポンスが確認された場合は、アイドル状態のインスタンスを待っている間に保留中のキューでリクエストがタイムアウトしたことを意味します。

    • 十分なインスタンスをプロビジョニングしても、手動スケーリングを使用すると保留時間が長くなることがあります。アプリケーションがエンドユーザー トラフィックを処理する場合は、手動スケーリングを使用しないことをおすすめします。手動スケーリングは、タスクキューなどのワークロードに適しています。

    • 基本スケーリングでは、レイテンシを犠牲にして費用を最小限に抑えます。レイテンシの影響を受けやすいサービスには、基本スケーリングを使用しないことをおすすめします。

    • App Engine のデフォルト スケーリング設定では、ほとんどのサービスで最小のレイテンシが保証されます。それでも保留時間の長いリクエストが見られる場合は、最小数のインスタンスを指定してください。アイドル状態のインスタンスを最小限に抑えることで費用を抑えるようにスケーリング設定を調整すると、負荷が急増した場合にレイテンシが急増するおそれがあります。

デフォルト スケーリング設定でパフォーマンスのベンチマーク評価を行ってから、これらの設定を変更するたびに新たにベンチマーク評価を実施することをおすすめします。

デプロイ

デプロイの直後にレイテンシが増加する場合は、トラフィックを移行する前に十分にスケールアップが行われていません。新しいインスタンスでは、ローカル キャッシュのウォームアップが行われていないために、古いインスタンスよりも処理が遅くなることがあります。

レイテンシの急増を回避するため、サービスの既存バージョンと同じバージョン名を使用して App Engine サービスをデプロイしないでください。既存バージョン名を再利用すると、トラフィックを新しいバージョンに徐々に移行できなくなります。App Engine は短時間に各インスタンスを再起動するため、リクエストが遅くなることがあります。以前のバージョンに戻す場合は、再デプロイも必要です。

アプリケーション インスタンス

このセクションでは、アプリケーション インスタンスのほか、パフォーマンスの最適化とレイテンシの削減のためのソースコードに適用できる一般的な方針について説明します。

アプリケーション コード

アプリケーション コードに問題があると、特に問題が断続的に発生する場合や再現できない場合に、デバッグが困難になることがあります。

問題を解決するには、以下を行います。

  • 問題を診断するには、ロギングモニタリングトレースを使用してアプリケーションを計測することをおすすめします。Cloud Profiler を使用することもできます。

  • ローカル開発環境で問題を再現してみてください。それにより、App Engine では実行できない言語固有のデバッグツールを実行できる場合があります。

  • アプリケーションの不具合やどのようなボトルネックが発生するかをより適切に理解するには、障害が発生するまでアプリケーションの負荷テストを行います。最大インスタンス数を設定し、アプリケーションに問題が発生するまで負荷を徐々に増やします。

  • レイテンシの問題が新しいバージョンのアプリケーション コードのデプロイと相関している場合は、ロールバックして、新しいバージョンがインシデントの原因かどうかを判断します。ただし、デプロイを継続的に行うと、デプロイが頻繁に発生することで、開始の時点に基づきデプロイによってインシデントが発生したかどうかを判断することが困難になります。

  • アプリケーションは、Datastore 内などに構成設定を保存する場合があります。構成変更のタイムラインを作成することで、レイテンシの増加の開始と一致する構成変更があるかどうかを判断できます。

ワークロードの変化

ワークロードの変化により、レイテンシが増加する可能性があります。ワークロードの変化を示すモニタリング指標には、qps、API の使用状況、レイテンシなどがあります。リクエストとレスポンスのサイズの変化も確認してください。

メモリ負荷

モニタリングでメモリ使用量にのこぎり歯のようなパターンが見られる場合や、デプロイに相関するメモリ使用量低下が見られる場合は、メモリリークがパフォーマンスの問題の原因となっている可能性があります。メモリリークによりガベージ コレクションが頻繁に発生し、レイテンシが増加することがあります。この問題をコードの問題に帰することができない場合は、より多くのメモリを使用してより大きなインスタンスをプロビジョニングしてみてください。

リソースリーク

アプリケーションのインスタンスで、インスタンス起動からの経過時間に伴うレイテンシの上昇が見られる場合は、リソースリークの発生によってパフォーマンスの問題が引き起こされていることがあります。デプロイが完了すると、レイテンシが低下します。たとえば、CPU 使用率が高いために時間の経過とともに遅くなるデータ構造があると、CPU 制約ワークロードが遅くなることがあります。

コードの最適化

App Engine のレイテンシを短縮するには、次の方法に従いコードを最適化します。

  • オフライン作業: Cloud Tasks を使用して、ユーザー リクエストによってメールの送信などの作業の完了をアプリケーションが待つことが妨げられないようにします。

  • 非同期 API 呼び出し: API 呼び出しの完了待ちがコードでブロックされないようにします。

  • バッチ API 呼び出し: 通常、API 呼び出しのバッチ バージョンは、呼び出しを個々に送信するよりも高速です。

  • データモデルの非正規化: データモデルを非正規化することで、データ永続化レイヤに対する呼び出しのレイテンシを短縮します。

アプリケーションの依存関係

アプリケーションの依存関係をモニタリングすることで、レイテンシの急増が依存関係の不具合と相関しているかを検出できます。

ワークロードの変化とトラフィックの増加により、依存関係のレイテンシが増加することがあります。

スケーリングされない依存関係

App Engine インスタンスの数が増加に伴ってアプリケーションの依存関係がスケーリングされない場合、トラフィックが増加すると、依存関係が過負荷状態になることがあります。スケーリングできない依存関係の例として、SQL データベースがあります。アプリケーション インスタンスの数が多くなるとデータベース接続数が増え、データベースの起動が妨げられてカスケード障害が発生することがあります。この問題を解決するには、次の操作を行います。

  1. データベースに接続されない新しいデフォルト バージョンをデプロイします。
  2. 以前のデフォルト バージョンをシャットダウンします。
  3. データベースに接続される、デフォルト以外の新しいバージョンをデプロイします。
  4. トラフィックを新しいバージョンにゆっくり移行します。

予防策として、アダプティブ スロットリングを使用して依存関係へのリクエストをドロップするようにアプリケーションを設計してください。

キャッシュ レイヤの障害

リクエストを高速化するには、エッジ キャッシュ、Memcache、インスタンス内メモリなど、複数のキャッシュ レイヤを使用します。これらのキャッシュ レイヤのいずれかの障害が原因で、レイテンシが急増する場合があります。たとえば、Memcache のフラッシュによって、より低速の Datastore に送信されるリクエストが増える場合があります。