こんにちわ、LITALICOで技術分野の執行役員をやっている@yuyaichihashiです。
この記事は『LITALICO Engineers Advent Calendar 2019』の21日目の記事です。
はじめに
同じくらいのレベルの技術知識があっても、トラブルシューティングできる人できない人がいるなぁと思うことがしばしばあり、少しでもトラブルシューティング力を上げる助けになればと書いてみました。
この記事ではトラブルの原因を特定するまでのプロセスをスコープにします。
本番システム障害時の復旧作業や、原因特定後の解決などのプロセスはスコープ外です。
システム系のトラブルは、本番システムの障害だけでなく、日常的に大小様々発生します。
- 書いたコードが意図した通りに動作しない
- 他システムと結合したらうまく動作しない
- 新しく使う技術の検証をしていて期待通りの動作をしてくれない
- データベースのクエリが遅い
- ネットワークルーティングが設計通りに動作しない
- ミドルウェアが意図しない動作をする
- etc,etc,etc
本記事では、本番システムでの障害だけでなく、そういった日常的なデバッグも含めてトラブルシューティングとして、広く活用できればと思って書いています。
問題を正しく認識しよう
そもそもトラブルって何だろう?
システムトラブルの場合、システムに期待されている動作と、実際に発生した動作にギャップがある場合、そのギャップがトラブルであると言えます。
解決すべき問題は合っていますか?
これは、すでに運用されているシステム/機能であれば、発生事象の影響範囲や発生条件を明確にする等のプロセスは必要ですが、基本的に何が問題かは明確です。
ところが、アプリケーションでもインフラでも、開発段階においては、問題を見誤ってハマり続けているケースをしばしば見かけます。
ほとんどのケースで、目指す状態が何であったのかの意識が薄れ、そのための手段の実現に固執してしまっているようです。
当たり前だろって話なのですが、ハマってる最中には近視眼的になってしまうものです。
原因特定のプロセスって何だろう?
発生事象(=問題)からその発生原因を特定するプロセスは、発生事象という解像度の低い事実から、様々な可能性を排除し、発生原因という解像度の高い事実へと、事実の解像度を上げる作業と言えます。
事実の解像度を上げる手法あれこれ
事実を可能性の集合に分解する
「あるWebシステムで、ブラウザ上のボタンAをクリックし結果が表示されるまでに5秒かかった」
これは事実ですが、例えば、以下のような可能性の集合です。
- ブラウザの処理が遅い
- ユーザーのネットワーク環境が遅い
- ユーザーからWebシステムまでのネットワーク経路が遅い
- Webシステムのネットワークが遅い
- Webシステムの処理が遅い
分解は始めから細かくし過ぎず、被疑箇所を絞り込みながら段階的に進めましょう。
例えば、上記の各可能性を確認して、「Webシステムの処理が遅い」が1段階解像度を上げた事実であったならば、
- NginxやApacheなどのWebサーバーミドルウェアの処理が遅い
- アプリケーションロジックの処理が遅い
- データベースクエリが遅い
さらに、「データベースクエリが遅い」なら、
- RDBMSとのコネクション確立が遅い
- RDBMSのクエリの処理が遅い
- RDBMSからクエリの結果セットを受信し終えるのが遅い
さらに、「RDBMSのクエリ処理が遅い」なら、
- クエリキャッシュのヒット率が悪い
- オプティマイザーが決定する実行計画が非効率
- エグゼキューターの処理に時間を要している
- ストレージエンジンの処理に時間を要している
- データキャッシュのヒット率が悪い
- インデックスキャッシュのヒット率が悪い
- ディスクI/Oに時間を要している
- etc
と分解を繰り返し、解像度を高めながら原因に近づいていきます。
また、被疑箇所がネットワーク関連であった場合は、リクエストとレスポンスという観点を入れて分解しましょう。
同一の解像度の他の可能性について確認する
そもそも、発生事象自体の認識を正確に持つことも大切です。
その時点である程度被疑箇所が絞られ、その後の分解の方向性も正しく認識できます。
先ほどの「あるWebシステムで、ブラウザ上のボタンAをクリックし結果が表示されるまでに5秒かかった」で言えば、
- ボタンAはどうか?
- ボタンBはどうか?
- ボタンCはどうか?
という可能性の集合もありますし、
- ブラウザAはどうか?
- ブラウザBはどうか?
- ブラウザCはどうか?
という可能性の集合もあります。
他にもユーザーのネットワーク環境であったり、時間帯であったり、対象のデータ量であったり、ユーザーのシステム上の種別や利用傾向であったり、色々な可能性の集合が考えられます。
これらの可能性について事象発生の有無を確認することで、発生事象の理解がより正確になり、システムの構成と照らすことで、段階的に分解し可能性を検証するプロセスをある程度ショートカットして被疑箇所が絞り込めます。
可能性を事実に変える
ある可能性について、事実を検証できる情報を確認していくのがベースとなります。
ログやDBデータ、収集しているメトリクス、アプリケーション、ミドルウェア、OSなどの仕様/設計やコンフィグ、ソースコードなどすでに存在している情報を確認するだけでなく、特定の状況を作り出したり操作をして新たに得られる情報を確認することもあります。
また、確度の高い可能性を検証対象として選択することが、トラブルシューティングの効率を上げます。
以下のヒントを参考に原因の仮説を立て、検証すべき可能性を選択しましょう。
判明している事実を並べる
すでに判明している事実は何か整理しましょう。
この時、それぞれの事実について、どういう解像度の事実なのか意識しましょう。
事実と事実の関係が因果関係なのか相関関係なのか区別する
因果関係の連鎖をたどり、最後まで辿れるルートが正解ルートです。
因果関係が判明している関係、まだ相関関係としかいえない関係を整理し、どのルートが正解である確度が高いか、どの相関関係の因果を確認すべきかを見極めましょう。
対象の技術分野の知見を高い解像度で持っているほど、確度が高いルートの判別がしやすくなります。
仮定を置く
複雑な問題や、得られる情報が少ない環境においては、事実と事実の距離があり関係を結べなかったり、相関関係を結べても何ホップもの因果関係がないと2つの事実の間の因果関係を確定できなかったりします。
そういう時は、特定の可能性について、「こうだったら」と仮定を置いて、因果関係の仮説を立ててみましょう。
補足
これらの手法は個々に扱うとか、順番に扱うというものではありません。
発生原因を特定するまでのプロセスで、これらの手法を状況に応じて使い分けたり、相互に援用しながら進めます。
おまけ - 事実誤認に気をつける
難易度が高い問題ほど、事実の解像度をより上げていかないと原因特定にいたりません。
その際に気をつけたいのが、事実として扱っていた事柄が、実は事実の近似値であるという落とし穴です。
例えば、ログメッセージの場合、メッセージの内容を事実として扱って問題ないことが大半ですが、厳密には、ここからわかる事実は、メッセージ内容に書かれたことそのものではなく、それは事実の近似値に過ぎません。
そのログメッセージが出力されるソースコードが実行されたというのが事実であり、ソースコード上のログの出力条件が、この事実が内包する可能性の集合です。
おわりに
自分がふだんトラブルシューティングしてる時に、頭の中で何が起きてるっけかなーと整理してみました。
人によってやり方は違うかもしれませんが、何かしら情報を整理してわかってることわかってないことを分別しつつ思考するプロセスを持っていることが、トラブルシューティングに必要なスキルかなと思います。
もちろん、技術知識はあればあっただけ原因特定にいたることはできますが、正しい思考のプロセスがあってこそだと思います。
明日は@n-sugimotoさんの「Unityで楽しく物理数学入門」です。お楽しみに!