こんにちは、サーバエンジニアの夏(なつ)です。今回はミラティブのサーバチームで行っている障害振り返りを紹介したいと思います。
ミラティブのサーバチームではサービスに障害が発生した場合、その後、担当者を決めて障害の振り返りのたたき台を作成し、チーム内で振り返りを行って、今後の改善に活かす努力を続けています。
今回はその振り返りの目的やフォーマット・注意点についてお話したいと思います。
目的
システムを運用していれば障害はつきものです。ましてや改善を続けるならば、その代償として不確実性が障害として表面化し、放置していけば徐々にユーザの信頼を失っていくことになります。かといって、障害の防止にコストをかければかけるほど、費用対効果は見合わなくなり、障害を絶対に起こしてはならないという心理的圧力はメンバーのメンタルを擦り減らしていきます。そのため障害の振り返りでは障害の詳細や原因をチームメンバーと共有しつつ、本来価値提供したいことに対して、障害への対応コストを妥当な範囲に収めるためにはどうしたらいいかが議論されます。
また、チームで振り返ることにより、個人のミスをチームの経験へと昇華することができます。とくに障害の引き金を引いてしまったのが自分であれば、不注意や確認不足を悔やみ冷静に振り返り辛いのは当然ですし、現実から目を背けてしまいたくなる場合も多々あります。チームに共有することで、異なるバックグラウンドをもつメンバーから建設的なアドバイスを聞けるかもしれません。組織全体を1つのシステム、個人のミスもシステム内で一定の頻度で発生する事象として捉えることで、障害振り返りを通して組織の強化を続ける努力をしましょう。
振り返りフォーマット
ミラティブのサーバチームでは障害の振り返りとして「影響」「背景」「発生・復旧フロー」「原因」「被害の最小化・再発防止策」をまとめてConfluenceに蓄積しています。
どの情報も他の人が再現確認できるよう、エビデンスとセットで書くように意識しています。 (Slackのリンク、調査時のコマンドや出力結果、監視画面のリンクやスクリーンショットなど)
影響
発生日時や影響を受けた機能・対象者を書いておきます。
背景
サーバチームを対象に、障害を理解する上で必要なドメイン知識を共有します。エンジニア以外にも伝わるように書く必要はありませんが、他事業部や歴史的経緯を知らないエンジニアにもこの後の章を理解できるように説明する必要があります。
発生・復旧フロー
何がどう作用して障害を引き起こし、その後どう検知され、どう作業して障害が復旧したのかを時系列に沿って書きます。 障害を検知したチャネルを明確にすることで、そのチャネルの透明性を評価できます。
- 前兆が見つかってから、調査開始されるまでのチャネルに不要な伝言ゲームが起きていないだろうか
- 有用そうなチャネル上のノイズが多く、無意識にミュートされていないだろうか
- 今後そのチャネルの優先順位を上げるべきだろうか
また、復旧フローを時系列に沿って書くことで、どこの対応に時間がかかったかが把握でき、支配的かを洗い出すことで復旧時間短縮に向けて優先順位を決めることができます。
原因
どういう因果関係で障害が発生したのかを詳細に書きます。
被害の最小化・再発防止策
発生・復旧フローや原因をもとに、今後組織として再発防止できるような仕組み化や、早期発見や原因の解像度を向上できるような仕組みを検討します。
障害の芽はできるだけ初期段階で潰せれば、それだけ復旧コストが安く済みますので、以下のようにフェーズごとに何かできないかを考える必要があります。
- 開発時
- 危険なコードは型やアーキテクチャレベルで書けないようにする
- CI上の自動テスト時に検知する
- コードレビューで指摘する
- オンボーディングの資料に盛り込む
- 機能検証時
- 過去に何度か障害が発生したケースをQAチームに共有し、検証項目に盛り込んでもらう
- 本番リリース後
- Canaryリリース時に気付ける体制にする
- エラーログを充実させる
- ユーザからのお問い合わせで気付くよりも、エラーログで早めに気付ける方が、影響範囲や復旧コストが少なくて済む
- リリースしてから数年後にデータ量の増加に伴って、計算量が爆発する場合もあるので、レスポンス速度の劣化やslow query、explainの実行結果などを監視する
逆に以下のようなことが再発防止策に入っていないか注意が必要です。
- 場当たり的な対応になっていないか
- HTTP 5xxの監視に引っかかったからといって、エラーを握りつぶすような解法になっていないか
- エラーを握りつぶしたからといってユーザ影響が解消するとは限らないし、むしろ不具合の検知が遅くなる可能性がある
- HTTP 5xxの監視に引っかかったからといって、エラーを握りつぶすような解法になっていないか
- 開発速度を過剰に制限していないか
- 罰則的な対応になっていないか
- 本来価値提供したいことに影響が出ていないか
- 細心の注意で確認を入念に行うなどのような対応になっていないか
- 漠然としており、障害が再発した場合でも、確認が足りていませんでしたと同じ振り返りにしかならない
- 確認やダブルチェックが必要ならば、手順書やチェック項目まで落とし込む必要がある
- 確認箇所が曖昧だと、どこかの現場猫よろしく「後の人がちゃんとみてくれてるだろうからヨシ!」「前の人がレビューをとおしてるんだから絶対ヨシ!」みたいなことになりかねない
- 自動化できるのがベストだが、工数や優先順位の都合上すべてが自動化できるとは限らない
- 重厚長大な再発防止になっていないか
- 解決できるスコープに対して労力を多大にかけていないか
- 一見良さそうに見えるが、銀の弾丸などないので、導入した場合でも別の問題が出る可能性がある
- また、実際にサービスに組み込んで、既存のシステムを刷新するためには相当なコストがかかる
- 但し、重厚長大な再発防止を否定しているわけではなく、短期で結果が出ないし、多大なコストを払うかもしれないが、中長期的な戦略に立った時に改善に繋がるのであれば、コスパを意識した上で導入するのはあり
再発防止策の具体例
ミラティブサーバチームがここ1年半ぐらいの障害振り返りを通して行ってきた代表的な再発防止策としては以下のようなものが挙げられます。
(詳細に関してはまたいつかテックブログに書いたり書かなかったりする予定です)
- Partition Pruningの効いていないクエリの洗い出し・修正
- DDLのマイグレーションツール上のノイズを減らし、人間が確認すべき項目に集中できるように改善
- 特定の時刻に発火する処理は、ゆらぎを自動で付与して負荷が重ならないように改善
- Pull Requestのテンプレートに新規で発行されるクエリのEXPLAINを記入する項目を追加
- Webの負荷がスパイクしないように、全体へのPush通知をゆっくり送る修正を追加
- 開発環境で時刻を変更できる機能を追加
- INSERT IGNOREを非推奨に
- チーム内のレビュー方針に「その機能で障害があった時に自分で調査・復旧できるか」を追加
- OpenAPIのadditionalPropertiesを必須に
- アプリケーションサーバ側でのSlow Queryの通知
- ユーザがいる環境での検証作業を最小限に
- アーキテクチャの再設計・Goへの移行
- マスタデータの運用フロー改善
特に最後3つの「ユーザがいる環境での検証作業を最小限に」「アーキテクチャの再設計・Goへの移行」「マスタデータの運用フロー改善」はミラティブサーバチームにおける中長期での最重要技術戦略だと位置づけており、障害振り返りで培った経験を元に、現在もプロジェクト化してチーム全体で取り組み続けております。
おわりに
新しい挑戦を続けている以上、障害をゼロにすることはできません。また、ときにはチーム内で話しあっても、上手い解決策が見つからない場合も多々あると思います。しかし、個人のミスとして片付けるのではなく、チームの経験として獲得し、向き合い続けることができれば、障害を通して反脆い組織へと成長できると信じています。
We are hiring!
ミラティブでは新機能によるユーザへの価値提供と同じくらい基盤の安定化に向き合えるエンジニアを募集しています。
- Goで大規模サービスの開発をしたい
- サーバーシステムの基盤の整備をしたい
- ゲーム×ライブ配信サービスの開発をしたい