Septeni Engineer's Blog

セプテーニ・オリジナルのエンジニアが綴る技術ブログ

【SRE本読書会】効果的なトラブルシューティング

中途三年目、社内でSREの風を吹かせております堀越です。

ここ最近、弊社の中でも割と長寿なプロダクトの保守を行ってきました。そんな中、自身のトラブルシューティングスキルに課題感を感じていましたのでSRE本の 「12章 効果的なトラブルシューティング についてざっくりとまとめていこうと思います。

トラブルシューティングスキルは学習可能

トラブルシューティングスキルは生まれ持ったスキルのように思われがちだが、頻繁にトラブルシューティングを行う人にとっては染み付いたプロセスになっているのでそのように見えるだけとなっている。トラブルシューティングは学ぶことも教えることも可能なはずである。

トラブルシューティングスキルの構成要素

トラブルシューティングスキルは下記の2つの要素に依存する。

  1. 一般的なトラブルシューティングの手法の理解
  2. システムに関するしっかりとした知識

一般的なプロセスと大原則の応用だけでも調査することもできるが、本来あるべき動作を理解しておく方が効率が良い。

理論

トラブルシューティングのプロセスは仮説演繹法の応用と捉えられ、下記プロセスの繰り返しとなる。

  1. システムの観察
  2. 観察結果と挙動の把握
  3. 障害原因の仮説立てる
  4. 仮説の検証

理想的なモデル

トラブルシューティングは下記のような手順化されたモデルとして表現できる。

f:id:t_horikoshi:20190626221309p:plain
トラブルシューティングのプロセス

仮説の検証

仮説の検証方法としては下記の2つ。

  1. システムの状態と仮説とを比較し考察する
  2. システムに変更を加え、その結果を観察する

後者のアプローチはシステムの状態と考えうる障害の原因についての理解を高めてくれる。

トラブルシューティングにおける一般的な落とし穴

トラブルシューティングの効率が悪いのは、システムに対する理解が欠けていることが主な原因。下記は注意すべき落とし穴。

  1. 関係ない症状を観察している
  2. メトリクスの意味を取り間違える
  3. 仮説検証のためのシステムへの入力値、設定変更方法を誤解している
  4. ありえない仮説を思いつく
  5. 過去の問題の原因に固執する
  6. 共通の原因を持つ事象に対し、間違った関係性を追求してしまう

実践

実際には、トラブルシューティングは理想化されたモデルの示す通りにいくようなクリーンなものではないが、苦痛の少ない生産的なものにする手順は存在する。

手順1. 問題のレポート

問題への対応は必ず問題のレポートから始まる。その種類はさまざま。

  • 自動化されたアラート
  • 同僚が「システムが遅くなってると言い出す」
  • 顧客からの障害報告
効果的なレポートとは

効果的なレポートには下記のような内容が記されているべき。

  • 期待される動作
  • 実際の動作
  • 問題となっている現象を再現させる手順

さらに、レポートは一定の形式で作成し、検索可能な場所に保存しておくのがよい。

Google におけるバグチケット作成の粒度

メールやチャットで受信したものであっても、1つの問題ごとにバグチケットを切るのが主流。人への直接の報告は下記のような理由から推奨されていない。

  • 問題報告をバグチケットに書き直す手間を増やす
  • チーム内の他のメンバからは見えない低品質のレポートを生み出す
  • 問題対応を受け持つメンバ以外のメンバに対しても問題解決の負荷を与えてしまう

手順2. トリアージ

問題のレポートを受けた次のステップは何をすべきかはっきりさせること。 その問題の影響範囲に応じて適切に対応しなければならない。大規模な障害なら総力をつぎ込む緊急事態であることを宣言することが適切だが、小規模の場合に同じことをするのはやりすぎ。

大切なのはシステムの動作を止めないこと

大きなサービス障害に対し、いきなり根本原因を突き止めたくなる本能は無視しなければならない。行うべきはシステムがその環境下でできるかぎりうまく動作するようにすること。これは二次三次の災害を防ぐため。根本原因を探っている間にシステムが止まってしまってはユーザーを助けられない。

手順3. 検証

システムが正しく動作しているかどうかを理解するには、システム中の各コンポーネントの動作を検証できなければならない。その際、モニタリングツールによるメトリクスやロギングによるリクエストのトレースなどが問題解決に役立つ。

手順4. 診断

システム全体の設計を理解しているのは仮説を立てる上で必ず役に立つが、特定領域に固有の知識抜きでも役立つような一般的プラクティスもある。

純化と削減

コンポーネント間を流れるデータを見て、そのコンポーネントが正しく動作しているか判断できるはず。効果的なのはテストデータを与えて各ステップでの結果が期待通りになっているか調べること。再現性のあるテストケースがあればデバッグを早く進められるようになり、同じテストケースをプロダクションでない環境でも使える。そうすることでリスクの高いテクニックを使うことができる。

複数のレイヤーを持つシステムにおいては分割統治法二分法が問題解決のテクニックとしては非常に有効で。障害を起こしているコンポーネントを限定できる。

「何が」「どこで」「なぜ」と尋ねる

異常を起こしているシステムは、依然として何かをしようとし続けていることが多い。それが 何を しているのかを探り、 なぜ そういった動作をしているのか、どこで リソースが消費されているのか、出力は どこへ 送られているのかを探れば症状を理解するのに役立つ。

最後に行われた修正

直近に生じた変化は問題を特定する上で効率的な出発点になる。システムの変化をシステムや環境における他の出来事と関連付けたモニタリングダッシュボードなどがあると役に立つ。*1

特定の診断

自分らが受け持っているサービスを診断するためのシステムを構築することは有益。車輪の再開発を避けるためサービスやチーム間での共通性は意識するべき。

手順5. テストと対応

根本原因の調査

潜在的な原因をある程度調べたら根本原因を調べる。

例えば、アプリケーションロジックのサーバーとデータベースサーバー間のネットワーク障害か、データベースが接続を拒否しているかのどちらかと考えているものとする。まず、アプリケーションロジックが使用しているものと同じクレデンシャルでデータベースに接続すれば後者を検証できる。一方でデータベースサーバーに ping を飛ばしてみれば、ネットワークやファイアウォールのルール、あるいは他の要因として前者を検証できる。

テスト設計

テスト設計には考慮すべきことがたくさんある。

  • 理想的なテストは排他的な複数の選択肢を持つこと
  • 明らかなことから考え始める
  • アプリケーションへのアクセス許可がIP制限の場合は混乱を招きやすい
    • 特定のマシンからしかアクセスできないなど
  • テストによる副作用
    • テストのために仕込んだコードが悪化させていないか
  • テストの結果がハッキリせず、妥協しなければいけない場合もある

実行したテスト、およびその結果は記録しておくこと。同じステップを繰り返さずにすむし、このドキュメントが重要になる。

否定的な結果の素晴らしさ

否定的な結果の定義

「否定的な結果」 とは、実験の成果として期待された効果が得られなかった結果のこと。新しい設計、経験則、人間的なプロセスによるアプローチでの改善がうまくいかなかった場合も含む。

否定的な結果は、無視したり過小評価してはいけない

間違いを理解することは、設計上の問題に解をもたらすことがある。否定的な結果が出た実験は決定的なもの。 そういった実験は、プロダクションや設計の領域、パフォーマンスの限界について確実なことを示す。それらの実験は、独自の実験や設計を行う価値があるかどうかを他者が判断する助けになる。

否定的な結果を直感的に実験に適用できなくても、収集された補完的なデータが、他者が新しい実験を選択したり、それまでの設計に潜む落とし穴を避けたりするのに役立つこともある。マイクロベンチマーク、ドキュメント化されたアンチパターン、ポストモーテムなどはここに分類される。実験の設計を行う際には、否定的な結果のスコープを考慮にいれなければならない。

ツールや方法論は長期的にも役に立つ

ツールや方法論は、実験そのものよりも長く残るものであり、詳細な作業の助けとなる。 繰り返し行われる実験のためのツールを構築することには、間接的なメリットもある。例えばデータベースのインデックスの設定変更を簡単に試せるスクリプトを書いてそれを使ってあまりメリットを得られなかったとしても、次のプロジェクトでは最適化を忘れることはなくなる。

否定的な結果を公開しよう

否定的な結果や、統計的有意性の欠如を明確に説明することが、不確実性を慎重に受け入れる例を他者に示すことになる。すべてを公開することによって、他者にも同じ行動を取ることを推奨することになり、ひいてはソフトウェアに対する知識を誰もが早く学んでいけることになる。

結果を公開しよう。実験の結果に自分が関心を持っているなら、他の人々も関心を持つ可能性が高い。結果を公開すれば、そういった人々は同じような実験を自分たちでやらなくよくなる。

実験が「失敗」だったと受け取られやすいことから、否定的な結果を報告することは避けたくなるものであり、それは一般的なことでもある。実験の中にはそもそも絶望的なものもある。また多くの実験は、否定的な結果は進捗でないという誤った考えから報告されないままになっている。

否定的な結果は、思慮深くリスクを取った結果の一部であり、十分に設計されたあらゆる実験にはメリットがある。逆に失敗に言及していない設計ドキュメント、パフォーマンスレビュー、小論文に対しては懐疑的に接するべき。そういったドキュメントは、フィルタがかかりすぎているか、作者が自分の手法にたいして厳格ではない可能性がある。

対策

続いては、実験の成果から本当に原因であることを確かめる。その確認をプロダクションでやるのは難しいかもしれず、以下の要因で推測しかできないかもしれない。

  • システムは複雑なので単独では原因とはならない要因が相互に関係していることが原因となっている。
  • 再現することが複雑過ぎたり、ダウンタイムを増やせられない。

問題を引き起こした要因を見つけることができたら、システムで生じた問題、問題を突き止めた過程、問題の解決方法、再発防止策をポストモーテムに書く。

トラブルシューティングを容易にするために

最も基本的なものは下記の2つ。

システム全体にわたって、一貫性のある形で情報が利用できることを保証するのが良い。 *2

まとめ

個人的に重要そうなものをピックアップ。

  • トラブルシューティングスキルは学習可能
  • システムの本来あるべき挙動を把握しておくほうが効率が良くなる
  • トラブルシューティングのプロセスは、 システムの観察 → 挙動把握 → 仮説立て → 仮説検証 の繰り返し
  • いきなり根本原因を探すのではなく、まずはシステムを動作させ続ける努力をする
  • モニタリングツールは問題解決に役立つ
  • テストデータによる検証と分割統治法、二分法とを組み合わせて単純化工数を削減する
  • 否定的な結果を公開する
  • サービス診断のための簡単なツールを作ることは有益
  • 観察のための仕組みを構築し、構造化されたログを取得できるようにする
  • システム設計時にコンポーネント間に観察可能なインターフェイスを持たせる
  • 運や経験に頼らずにシステマティックにアプローチすることで、サービスを回復するまでの時間を抑えられる

最後に

トラブルシューティングスキルはある程度、体裁的に学習できるということがわかりました。システム設計時や障害発生時の際、得た知見を活かせたらと思います。

人材募集

株式会社セプテーニ・オリジナルでは一緒に働いていただけるエンジニアを積極募集中ですのでどしどしご応募ください。わたしと一緒にSREの風を吹かせてくれる方、大歓迎です。*3

<中途採用サイト>

*1:たとえば、エラーレートと新しいバージョンのデプロイ開始・終了時刻を組み合わせたグラフなど。

*2:リクエストにユニークな識別子を持たせてロギングするなど

*3:TwitterなどでDMいただいても構いません。