【真実はいつもひとつ!】トラブルシュートの3つの心得
はじめに
私は仕事柄、システム開発・運用の中で様々なトラブルシュートを行ってきたのですが(最近はもう…)、そこで得た自分なりの心得を伝えたいなと思ったため、この記事でまとめてみます。
とはいえ、トラブルシュートでは、扱っている技術スタックや事象によって、使用するデバッグツールやテクニックが大きく変わります。そのため、本記事では様々あるツールやテクニックではなく、もう少し抽象的なトラブルシュート全般に通じる「考え方」を書いていこうと思います。
トラブルシュートの3つの心得
トラブルシュートで私が大事にしている3つの心得を紹介します。
もちろん、他にも大事なことは沢山あると思いますが、特に意識しているものです。
- 事象の把握をする前に原因の特定をしない
- 不具合はシステムの外側/内側の両方で把握する
- トライ&エラーは丁寧に
以降では、それぞれの心得について説明していきます。
心得1:事象の把握をする前に原因の特定をしない
トラブルシュートは、「事象の把握」→「原因の特定」の順番で進めるべきだと考えています。
「事象の把握」、「原因の特定」は、それぞれ以下のような作業を指します。
- 事象の把握: 原因は気にせず、どういうことが起きているかを把握する。
- 原因の特定:なんでその事象が起きたかを特定する。
ここで大事になるのが、
事象の把握フェーズで、原因の特定をしない
ということです。
トラブルシュートを始めた直後は、「あれが原因かな?」、「この辺が原因かな?」と思いつくことが多くあります。ここで思いついたことを「あとで原因の特定をするときに最有力仮説にしよう」と頭の片隅に置ければばよいのですが、人間は思いついたことに引きずられてしまいがちです。
その結果、そんなつもりはなかったのに、思いついた原因ありきで事象を捉えてしまったり、それが原因であることを裏付ける調査を初めてしまったり、ということが起きえます。
思いついた原因が読み通りであれば最短で解析完了!となるわけですが、実際にそう上手くは進みません。まずは「なんでか知らんけどこうゆうことが起きてるんだな」と正しく把握することを終わらせましょう。原因を考えるのはその後で大丈夫です。
「事象の把握」の前に「原因の特定」を行ってしまうと、的外れな解析に時間を費やしたり、
そもそも起きてもいないことに対して調査をしてしまったり、時間を無駄に使うことになります。
トラブルシュートにあたる際は
- 不具合事象は正確に把握できているか?
- 事象の把握が出来ていないのに、原因について考えたり調べたりしていないか?
を自問するようにしてください。
心得2:不具合はシステムの外側/内側の両方で把握する
この心得は前述の「事象の把握」をどう進めていくかに関するものです。システムの外側・内側を意識しながら3つのStepで「事象の把握」を進めていくことで、正確に事象を捉えることができ、素早く「原因の特定」ができるようになるでしょう。
Step1:システムの外側の把握
まず、システムの外側にいるユーザーが「これはおかしい」と認識するきっかけとなった不具合事象を把握します。この段階では、システムの中で何が起きたのかや、どんなログが出力されたのかはガン無視して、システムを外から見た状態がどうであるかだけに着目します。
たとえば、以下のような状態が、システムの外側の不具合事象です。
- クライアント上でリクエストがずっと処理されないでグルグルしている。
- 「エラーが発生しました」とだけ書かれたエラー画面が表示された。
- 正常終了しているように見えるが出力が期待と違う。
これらは、あくまでもユーザー目線での不具合になりますので、これだけで「何が起きているのか?」を把握することはできません。しかし、これを知っておかないと、システム内の状態を調査した際に「システム内の調査結果は、システム外で起きている事象と整合しているか?」を評価できず、事象を正確に把握できないリスクが高まります。
開発中であればシステム外の状態把握は容易なので、このStepを意識する必要はあまりないかもしれません。一方、商用稼働中のシステムである場合、システム外の状態を知っているのはエンドユーザまたは対応したオペレータであることが多く、トラブルシュートにあたるエンジニアがシステム外の状態を直接知ることが難しくなってきます。
そのため、商用稼働中のシステムでトラブルシュートを行う際には、たとえば以下のような情報を収集し、システム外で何が起きているかを把握することが大事です。
- エンドユーザの問い合わせ文面
- 外接システム側で起きているエラーの詳細
- エラーの出方
- エラーメッセージが出ているのか?
- エラーが出ずに固まっているのか?
- エラーは出ていないが出力が期待と違うのか?
Step2:システムの内側の把握
次に、不具合が発生したことを示す証拠をシステムの内側で収集します。言い換えると、検知した問題に紐づくシステム内部の情報を特定します。
たとえば、以下のような確認を進めていきます。
- エラーログが出ていないか?
- エラーログが出ていない場合、通常のログの出方がいつもと違う部分がないか?
- CPU使用率など、リソース面で異常の兆候がないか?
この段階で重要なのは、
見つけた情報は、本当に今トラブルシュートしたい事象に紐づく情報なのか?
の裏付けをとることです。
たとえば、大量のログの中にエラーログが出力されているときに、「システム外で不具合が発生した時刻と、ログの時刻がほぼ一致している」という根拠だけで、「そのエラーログはその不具合に関するログである」と言い切ってもよいでしょうか?
その不具合は、エラーログが出力されないタイプの不具合かもしれず、出力されたエラーログは全く別の不具合によるものかもしれません。
もちろん、全てのケースで裏付けがとれるわけではありませんが、裏付けが取れない場合は「たぶん合っているけれど、もしかしたら別の事象のログかもしれない」という可能性を覚えておき、トラブルシュートが難航した際には取り違えのリスクを思い出せるようにしておきましょう。
Step3:不具合事象が起きた経緯の把握
最後に、Step2で収集した情報や設計情報を元に、不具合が発生するに至った経緯を把握しましょう。システムがどのように動作した結果、不具合が発生したのかを解析していきます。(一般に「切り分け」と呼ばれる作業はこの部分だと思います)
ある日のトラブル ~検索結果がおかしい?~
たとえば、「検索画面で存在するはずの情報が検索結果から漏れている」というトラブルシュート例を考えてみましょう。
Step1
Step1では、システムの外側から見た不具合事象を把握した上で、関連情報を収集します。
- エンドユーザからの問い合わせを契機に、エンジニアがトラブルシュートを依頼される。
- エンジニアは問い合わせ文面から「検索画面で存在するはずの情報が検索結果から漏れている」というシステムの外側の不具合事象を把握する。
- 合わせて画面操作時刻やエンドユーザのアカウント名を確認する。
Step2
Step2では、不具合事象に紐づくシステムの内側の情報を収集します。
- エンジニアはアプリケーションログを参照する。
- 時刻・画面名・ユーザ名などから、当該操作に関する一連のアプリケーションログを特定する。
Step3
Step3では、一連のアプリケーションログの内容を確認していきます。
その結果、以下のような事実がわかりました。
- リクエストがサーバに到達し、アプリケーションの検索機能が呼び出されたことを確認した。
- 検索機能の前処理が正常終了したことを確認した。
- 前処理の実行後、主処理内のDB検索が正常終了したことを確認した。
- データベースの検索結果に含まれているべきデータが含まれていないことを確認した。
結果として
処理自体は上手く動いており、DBから正しいレコードを検索できていない。なんでかしらんけど。
という「事象の把握」をすることができました。
このタイミングで初めて「レコードがそもそも存在しないのでは?」や「SQLの検索条件が誤っているのでは?」といった原因仮説を立てていきます。そして、その仮説が正しいかを検証していくことで、一歩ずつ原因に近づいていくことができます。
アーキテクチャを知ろう
実際のStep3は例のようにシンプルではなく、もっと複雑なものになるでしょう。より複雑な不具合事象を正確に把握するには、アーキテクチャを知ることが重要です。
少し話が逸れますが、切り分けとは「問題箇所を特定する」の裏返しとして「問題が起きていない箇所を特定する」ことでもあります。そのため、Step3では不具合が発生したこと自体を示すログだけではなく、そこに至った経緯を追うことで「ここは問題ない」、「ここも問題ない」と問題が起きていない箇所を取り除いていくことが大事です。
前述の例では、アプリケーションの検索画面が表示されていることが自明だったので、経緯を追う起点はアプリケーションログとなりました。しかし、これが「エラーが発生しました」とだけ記載されたエラー画面だった場合はどうでしょうか?
そのエラー画面はアプリケーションが返却した画面かもしれないし、リクエストがアプリケーションに到達する前に、ネットワーク経路上にあるどこかのノードが HTTP 500 Internal Server Error
の代わりに返却した画面かもしれません。
このような場合、不具合の経緯を追うためにはシステムのアーキテクチャに対する知識が必要になります。アーキテクチャを知ることで切り分けの対象となる全体を広く捉えることができ、思わぬところに問題箇所が存在する場合でもトラブルシュートを進めることができるでしょう。
心得3:トライ&エラーは丁寧に
トラブルシュートは、試行錯誤の連続です。
ログや設定を確認する・コマンドを実行する・ソースコードを修正するなど、情報収集や仮説検証に必要な手数はかなりなものです。そのため、トライ&エラーの作業ではスピード重視になりがちです。
スピード重視は大事なことですが、難易度が高く、なかなか原因を特定できないことも出てきます。そんなときは、丁寧にトライ&エラーを積み重ねるようにしてください。急がば回れです。
実際にやることは難しくありません。以下の2つです。
- トライを記録する
- トライしたら元に戻す
トライを記録する
1つ1つのトライは、以下の要領で進めましょう。
- トライの内容を以下の項目で整理しテキスト化する。
- 検証パターン名
- 着目しているパラメータの状態
- 実施する作業(コマンド等)
- 整理した内容どおりに実行する。
- 実行結果を以下の項目で整理し追記する。
- OK または NG
- 実行結果のログ
- 実行結果に対する見解
難易度の高いトラブルシューティングでトライ&エラーを繰り返していると
- このパターンってもう試したっけ?
- さっきこういう反応してたけど、何変えたらそうなったんだっけ?
- 昨日どこまでやったっけ?
など、何をしたか忘れてしまったり、さらには、やってないことをやったと勘違いしてしまうこともあります。
トライの内容を丁寧に記録することで、やり直し・検証漏れ・解釈違いを防ぐと共に、オペミスによる事故も防ぐことができます。
トライしたら元に戻す
トライ&エラーを行う際には、変更を加えた後に必ず元の状態に戻すことを心がけましょう。
ある変更を加えた後、戻さずに次のトライを行うと、結果が混ざり合い、正確な検証が出来なくなります。
このポイントは、皆さんも常識として理解していることかもしれません。しかし、実際にトラブルシューティングをしていると、早く先に進みたい気持ちが勝ってしまい、ついつい「この変更は次のトライに影響しない変更だからそのままにしちゃおう」などと考えがちです。
しかし、人間は間違うものですので、自分のことを過信せず、ぐっと堪えて丁寧に事を進めましょう。
まとめ
システム開発におけるトラブルシューティングで私が大事にしている3つの心得をまとめてました。これらの心得は、人間がトラブルシュートを行う際に「早く先に進みたくなる誘惑に打ち勝つ」ということに焦点を当てています。
そのため、心得それぞれ自体も大事ですが、トラブルシュートを行う際には自分自身を見つめ、「自分は焦っていないか?」、「一足飛びに対応を進めようとしていないか?」を自問することで、結果的に短時間で確実なトラブルシュートができるようになると思います。

NTT DATA公式アカウントです。 技術を愛するNTT DATAの技術者が、気軽に楽しく発信していきます。 当社のサービスなどについてのお問い合わせは、 お問い合わせフォーム nttdata.com/jp/ja/contact-us/ へお願いします。
Discussion