ha6_6ruブログ

主にフロントエンドの技術ネタを中心にしていく予定です。

障害解析の進め方

概要

技術部門に異動してから10年以上にわたって実践してきた、他部門から依頼された障害解析における思考およびプロセスを残すものです。
初見のアプリケーションの障害解析が中心だったり、構成に利用したことのないインフラや製品などが含まれることも普通にあるので冗長な部分も多いですが、汎用的な内容になっていると思います。

障害解析の基本的な流れ

これまで実践してきた障害解析の基本的な作業の流れを以下にまとめました。
最初に障害の事象に関して、情報を集め状況を整理するところから始めます。集めた情報を元に関連箇所の絞り込みと障害原因のおおまかな分類を行い、原因分類に応じた解析を行います。
ログメッセージで原因がすぐに特定されるようなわかりやすい事象や経験により即座に原因を類推出来るケースでは、この流れで実施する作業の多くはスキップします。

事象の整理

  1. 資料の採取
  2. システム構成の確認
  3. 環境依存性の確認
  4. 発生前後の変更の確認
  5. 事実の整理
  6. 時系列の整理
  7. 再現性の確認

解析

  1. 関連箇所の絞り込み
  2. 原因の分類
  3. 解析

各作業におけるポイント

事象の整理

必要な情報を収集して、発生している事象を正確に整理することは非常に重要です。
過去に解析した実績では、情報不足や誤った情報によって解析が長期化したケースは多く、逆に情報が十分であれば、過去の知見により即時に発生原因を特定出来たケースもありました。

資料の採取

ログ(アプリケーションログ、サーバーログ、イベントログ、パフォーマンスログなど)、ダンプファイル、ネットワークキャプチャなど、解析に必要な資料を採取します。
必要な情報が残っていないと解析不能に陥る可能性が非常に高くなるので、削除される可能性のある資料には気を付ける必要があります。特に最大サイズやローテーションなどが設定され、バックアップを行っていない資料に気を付けましょう。また、解析不能になってしまった場合、これらの設定の見直しを行いましょう。

システム構成の確認

明らかなアプリケーションのバグなど障害の原因が特定できているケースを除き、システム構成から関連する要素を整理しておきます。
システム構成を確認しながら、障害に関係ありそうな箇所と関係無さそうな箇所の色分けを行うことで、調査の範囲を絞り込みます。
過去の経験から、ネットワークが原因となっていたケースでは、システム構成を確認した段階で疑わしい箇所は絞り込むことが出来ていました。疑わしい箇所の代表として、TLSSSLアクセラレータやロードバランサーが挙げられます。最近ではマネージドサービス固有の制限やサービスダウンなどのケースも増えてきました。

環境依存性の確認

特定の環境でしか発生しない場合は環境差異に着目します。着目するポイントは、稼働環境、ハードウェアやネットワーク、ソフトウェア環境の差異、リリースしているアプリケーションの差異です。
過去の解析で多かった差異は、ロードバランサーの有無、インストールソフトウェアバージョンの違い、端末の機種や OS、設定の違いなどです。

発生前後の変更の確認

直近のインフラまたはアプリケーションの変更に着目することで、原因に当たりを付けることが出来ます。
アプリケーションの変更があったケースでは、ソースコードの差分に着目し、事象との関連性を確認します。
インフラに変更があったケースでは、変更が及ぼす影響と事象との関連性を確認し、原因を類推します。

事実の整理

情報を整理する際に、事実と不確かな情報を分けて整理しておくことが重要です。
疑わしい箇所を探していく段階では不確かな情報でも有用ですが、事実とそうでないかが整理されていないと、先入観により原因箇所に目を向けるまでに時間がかかってしまうケースや再確認により更に時間がかかってしまうケースがあります。

時系列の整理

ログや環境変更、リリース、オペレーションなどログとログに残らないようなイベントも含めて、関連する情報を時系列に整理することで、規則性など解決の糸口が見えてくることがあります。
また、特定のタイミングによって発生する問題や複数の処理が関連して発生する問題を解決するためには時間軸を正確に把握しておく必要があります。
時系列が正しく整理できておらずに認識を誤ると、やはり先入観により原因箇所に目を向けるまでに時間がかかってしまうケースがあります。

再現性の確認

原因にもよりますが、一度きりの障害発生では資料が採取出来ないこともよくあります。その場合は、再現テストが重要になります。繰り返し事象を再現させることが出来る場合、ほぼ間違いなく原因特定に至ります。
事象整理の流れについてまとめてきましたが、事象整理した段階で過去の知見により即時に発生原因を特定できるケースを除き、再現テストが解決への一番の近道になります。

解析

事象が整理できたら、それらの情報を使って関連箇所を絞り込み、障害を大まかに分類し、解析を行っていきます。

関連箇所の絞り込み

システム構成上の関連要素、環境依存性、発生前後の変更などから関連個所を絞り込んでいきます。
再現性がある場合は、例えばユースケースから関連する処理が導き出せていることでしょう。他にログから類似事象の情報が見つかったり、リソース状況から特異点が見つかったりなど整理した内容を手掛かりに、関連個所を絞り込んでいきます。
ここで先入観を持ちすぎず、別の可能性も想定しておくことが重要です。

原因の分類

関連箇所の絞り込みを行っていくと、関連する処理や類似事象の情報、リソース状況の特異点などから、おおよその原因に当たりをつけることができます。
過去の経験に基づく分類になりますが、代表的な原因の分類をアプリケーションがクラッシュするケースとハングするケースに分けて以下に示します。

原因分類            クラッシュ ハング                        
モリー使用量の増大 OutOfMemoryException の発生やネイティブメモリの割り当て時に実行時エラーが発生。
主な原因はメモリーリークや大量メモリーを確保するロジック、負荷集中などによるメモリーの逼迫。
応答停止やスローダウンが発生。メモリー使用量が OS やプロセスの上限まで上昇する。
主な原因はメモリーリークや大量メモリーを確保するロジック、負荷集中などによるメモリーの逼迫。
I/O IOException やタイムアウト系のエラーが発生する。
主な原因は I/O の競合。
応答停止やスローダウンが発生。事象発生時には、Disk Queue Length 値の増加が見られる。
主な原因は特定ファイルへのアクセス集中、アンチウィルスソフトウェアなどによる想定外の負荷発生など。
ネットワーク 想定外の実行時エラーが発生する可能性が高い。
例:二重送信、順序不正
主な原因はネットワーク機器や Web サーバーの不具合や設定誤り、クライアントの強制切断など。
スローダウンが発生。
主な原因はバーストトラフィック
バグ 想定外の実行時エラーが発生。
アプリケーションのバグから OS やミドルウェアフレームワークのバグなど、原因は多岐にわたる。
例:access violation の発生、OS リソース枯渇(ハンドルの割り当てエラー)など
応答停止やスローダウンが発生。
原因は多岐にわたる。
例:スレッド間デッドロック

解析

ここから個別の解析になっていきますが、原因分類ごとにどのような思考とプロセスで解析を行ったのかをまとめておきます。

モリー使用量の増大

調査方法概要

以下の流れで調査します。

  • 原因の切り分け
  • モリー使用量を増加させる箇所の特定
原因の切り分け

モリー使用量が増大する原因は、大きく分けて以下の 2 つに分類されます。

モリー使用量を増加させる箇所の特定

モリー使用量が増加した状態でメモリーの状態を確認して、オブジェクト数が多いもの、サイズが大きいオブジェクトを探す形が基本です。

調査方法詳細
必要な資料

事象の発生に備えておく資料としては以下があります。

  • パフォーマンスモニタのログ(メモリー関連)
  • ダンプ

パフォーマンスモニターは、メモリー増加の傾向を見るために使用するので、日常的に取得しておきましょう。
ダンプはメモリー増加の時系列に沿って複数取得しておくと、解析時に差分比較により原因となる箇所を特定できる可能性があります。

原因の切り分け

パフォーマンスモニタのログから、次のようにメモリーリークか一時的なメモリー使用量の増加なのかを切り分けます。

  • モリー使用量の傾向を確認する。
    • 使用量は急激に増加するか、漸増するか
    • 使用量が減少するタイミングがあるか

急激に増加してもその後減少するのであれば、一時的な負荷の可能性が高いです。一方、急激あるいは少しずつ増えたメモリーの使用量が、時間がたっても減らない場合は、メモリーリークの可能性があります。

モリー使用量を増加させる箇所の特定
  1. モリーの状態の確認
    デバッグ、デバッガーのアタッチ、プロファイラーの使用、ダンプなどからメモリの状態を調べます。オブジェクトの数とサイズでソートし、怪しいオブジェクトをピックアップします。
    複数のタイミングで確認できる場合は、差分を見ることで増加しているオブジェクトが容易に特定できます。

  2. 参照関係の確認
    増加しているオブジェクトの参照関係をたどり、それを保持しているオブジェクトを特定していきます。

  3. 処理の特定
    参照関係の確認を行っている時に参照関係にアプリケーションで実装しているオブジェクト(ユーザーオブジェクト)を見つけられれば、処理の特定は簡単です。ソースコード上でユーザーオブジェクトを扱っている箇所を確認していきます。
    ユーザーオブジェクトが出てこない場合、フレームワークやライブラリなどに調査が及ぶことになります。

多く観測された原因
  • イベントハンドラーの解除漏れ
  • ロジック誤りによるオブジェクトの蓄積
  • static領域のオブジェクト残存

I/O

調査方法概要

以下の流れで調査します。

  • 原因の切り分け
  • I/O の集中や競合を発生させているプロセス、処理の特定
原因の切り分け

クラッシュの場合はログにファイル名が出るなど、I/O の問題とすぐに断定出来るケースが多いので、ここでは解説を省略します。
ハングの場合は少しややこしく、ビジー状態の場合はパフォーマンスモニタのログで当たりを付けられるため、I/O を原因として想定し易いですが、フリーズ状態の場合は最初から I/O を原因として想定することは難しいことが多く、後述の「バグ」の解析手順の結果、I/O を原因と想定した解析を行うパターンが多いです。
ハングの場合で I/O を疑って調査を行う場合、ネットワークが原因になっていることもあるため、ディスクの問題なのかネットワークの問題なのかを切り分ける必要があります。
I/O が問題となる場合の主な原因としては、以下が挙げられます。

  • I/O の集中
  • 同じリソースに対してアクセスが競合
I/O の集中や競合を発生させているプロセス、処理の特定

I/O の集中による問題は、要求に対してスペックが不足している場合以外に、そもそも想定していなかった I/O 要求が発生したケースが過去の解析では多くありました。
想定外の I/O要求を発生させた原因の大半はアンチウィルスソフトウェアによるもので、これについては知識を持っておけば、原因の特定に至るまでスムーズに解析することが出来ます。
また、事前に除外などの対策も忘れずに検討しておきましょう。

調査方法詳細
必要な資料

主に採取・使用する資料を示します。

  • パフォーマンスモニターのログ(ディスク関連)
    ネットワークストレージ、ファイルサーバーの場合、ネットワークが起因しているケースがあります。その場合、ネットワーク関連のパフォーマンスモニターのログも合わせて確認する必要がありますが、詳細は割愛します。
  • ダンプ
  • 監査ログ
  • プロファイラーのログ
よく使用するツール
  • パフォーマンスモニター
    ディスク関連のパフォーマンスモニターのログの解析に使います。
  • ディスクアクセス監査ツール
    ファイルアクセスの統計情報の確認やプロセスの特定、ファイルアクセスの流れの確認に使用します。処理を特定する場合は、デバッガーやプロファイラーを使用します。
  • デバッガー
    主にダンプの解析に使用し、ファイルハンドラを保持している処理の特定などの解析を行います。フリーズの場合、待機中のスレッドとその処理を確認します。
  • プロファイラー
    ファイルハンドラを保持しているプロセスの特定や、ファイルアクセスの処理の流れの確認に使用します。
原因の切り分け
  1. I/O 集中が疑われる場合、事象発生時のアクセス数から想定外のアクセスが発生していないかどうかを確認します。
  2. アクセス数が許容範囲内である場合、ディスクアクセスの待ち状態を確認します。
    特定のファイルにアクセスが集中しておらず、待ちが発生している場合はスペック不足の可能性があります。
I/O の集中や競合を発生させているプロセス、処理の特定
  1. アクセス元のプロセスの確認
    アクセス元のプロセスを特定します。この時点で、アンチウィルスソフトウェアが原因であった場合は特定に至ります。
    アプリケーションのプロセスがリストアップされた場合、処理の特定を行っていきます。
    ファイルロックにより競合が発生している場合は、監査ログから特定するのは難しいケースもあり、その場合は再現テストが必要になります。

  2. 処理の特定
    アプリケーションのプロセスが原因であった場合、競合が発生していたファイルにアクセスしている処理をソースコード上で特定します。この場合、マルチプロセスやマルチスレッドの考慮が出来ていないことが想定されます。
    ソースコード上の特定に時間がかかる場合や他プロセスによるファイルロックが疑われる場合は、再現手順が確立しているのであれば、以下のような手法での解析も可能です。

    • プロファイラーを使用してファイルアクセスの流れを追う
    • デバッガーをアタッチし、ファイルハンドラを保有しているプロセスを特定する
    • ダンプ解析により、ファイルハンドラを取得した処理を特定する
多く観測された原因
  • アンチウィルスソフト
  • プロセス/スレッド間の排他制御ロジックの不備
  • エディタなどの外部プロセスによるファイルロック

ネットワーク

調査方法概要

ネットワークを原因として想定し、調査に至るまでのパターンについては、大きくクラッシュとビジー状態のハングのパターンがあります。
ジー状態のハングについては、パフォーマンスモニターのログで当たりを付けられるため、わかりやすいパターンと言えます。
クラッシュの場合は発生する事象が特殊なケースが多く、ワンタイムトークンのチェックエラーや処理順序不正エラーなどで表面化することがあります。これらの事象を引き起こす原因は、二重送信、遅延による前後関係の逆転などであり、そういった原因を想定して調査を進める必要があります。
バーストトラフィックにより、ビジー状態のハングが発生しているケースについてはわかり易いので、ここでは以下のクラッシュのパターンについて解説します。

  • 想定外の通信の発生
想定外の通信の発生

表面化する事象が特殊なケースが多いため、初期の段階ではネットワークが原因と想定することが出来ないケースがあります。
そのため、ここではこれまでに遭遇した代表的な事例を列挙します。

調査方法詳細
必要な資料

主に解析に使う資料としては以下があります。

  • パフォーマンスモニタのログ(ネットワーク関連)
  • 通信ログ(HTTPリクエスト/レスポンスのログ)
  • パケットキャプチャ

情報を採取する場合、通信の片側のみからでは解析が十分に出来ない可能性があることに注意し、可能な限り関連する機器間の通信ログを採取するようにします。
例えば、クライアントから中継器までの間に通信異常があった場合、サーバー側のログではそれが判断出来ません。

よく使用するツール
  • パフォーマンスモニター
    ネットワーク関連のパフォーマンスモニターのログの解析に使います。
  • パケットキャプチャー
    通信内容の確認に使います。
想定外の通信の発生
  1. 通信結果の確認
    ログを参照し、通信時間や終了ステータスを把握します。
    通信ログのタイムスタンプは通信の終了時に記録されることを念頭において、ログの順序と時系列の間に誤った認識を持たないように注意します。
    ブラウザーを閉じるなどクライアントとの通信に異常があった場合には、ログの出力内容が通常とは異なり、これが手掛かりになる可能性があります。

  2. 通信ログ、キャプチャーから通信内容の確認
    時系列に沿って通信内容を追いかけ、プロトコルやアプリケーションのフローと照らし合わせて特異点、不整合を見つけていきます。
    リセットパケットや再送などが発生しているケースがあるので、イレギュラーな通信が発生していないかという観点で見ることで手掛かりを見つけられる可能性があります。

  3. 通信ログとキャプチャーから想定外の通信を発生させている箇所を特定
    基本的に送信元 IP を見て送信元を判断しますが、ロードバランサーTLSSSLアクセラレータ、プロキシサーバーなどの中継機器の存在を認識しておく必要があります。
    また、X-Forwarded-Forなどに送信元IPアドレスが設定されるケースも抑えておく必要があります。

多く観測された原因

バグ

障害の原因となるバグには多様なものがあり、調査方法もバグに合わせて千差万別です。 そのため、ここでは解析が困難なユーザーコード外の OS やミドルウェアフレームワークに存在するバグに起因した障害の一般的な調査方法をまとめます。 また、ここまでに解説した、メモリー使用量の増大、I/O、ネットワークの 3 つに関しても、OSやフレームワーク等のバグが根本的な原因となって引き起こされるケースが存在します。

調査方法

原因として OS やミドルウェアフレームワーク等のバグを疑う場合は、以下の 2 つの方面から原因の特定を目指します。

  • 原因となる箇所の特定
  • 類似事例の調査
原因となる箇所の特定

バグの関与を疑う場合は、事象が発生する最小セットのプログラムで再現させることを目指し、再現性を取った上でプログラムの問題とは関係ない部分を取り外していき、発生個所や条件を絞りこんでいくとよいでしょう。
最小セットのプログラムで再現させた後、原因となる箇所の特定を行います。

類似事例の調査

多様なバグの中から障害の原因となるものを見つけ出すのに有効な手段としては、机上にて類似事例を調査するという方法があります。
OSSリポジトリやベンダーが公開している Knowledge Base(KB)、Stack Overflowといった開発者が集まるコミュニティサイトに有力な情報が存在する可能性があります。発生状況やログなどに出力される例外のメッセージ等を頼りに、情報を探します。Googleなどの検索エンジンで広く情報を探すのも有効です。

必要な資料

主に解析に使う資料としては以下があります。

  • 通信ログ(HTTPリクエスト/レスポンスのログ)
  • アプリケーションログ(操作ログ、エラーログ等)
  • OS やミドルウェアのログ
  • ダンプ

これらの資料から、最小セット作成を目指すための発生条件の特定や、机上調査対象とする例外メッセージの取得を行います。

調査が長期化/追及不能になりがちなケース

これまで見てきたバグによる障害の中で、もっとも厄介な種類のものが access violation や buffer overrun といったメモリアクセス違反よるクラッシュに関するものです。
buffer overrun 等によりメモリーの状態が破壊された場合、access violation が発生した段階ではメモリーを破壊したのがいずれの処理か特定するのが難しく、メモリアクセス違反に関する障害は再現性が取れずに長期化/追及不能となるケースがありました。 このようなケースでは、再現方法の確立が解決への一番の近道となる可能性が高いです。

おわりに

こうしてまとめてみると、オンプレ時代からの名残が多く残っているなと感じますが、障害解析の思考およびプロセスとしては今も無意識で実践していることが多いなと思いました。