こんにちは。 普段はEC系サービスの開発をしているsian-izmです。
今回はペロリでの障害再発防止の考え方と実施例について紹介させていただきます。
障害は前向きにとらえる
目指すべきは障害件数は0にすること。 しかし、システムを運用していれば、思わぬタイミングで障害がおきることはあるでしょう。 サービスが拡大している時であれば、自ずと件数も多くなると思います。
よって大切なことは、その障害をよりよいプロダクト作りのきっかけにしていくこと。 そういう点で障害を前向きにとらえていこうということです。
ペロリでは一定レベルのユーザー影響があり、緊急メンテナンスを行ったときには報告を上げる必要があります。 ただそれ以外でも、「よりよいプロダクト作りのきっかけのために」どんどん報告することを推奨しています。
障害報告のフォーマット
ペロリでは以下のようなフォーマットを使っています。
## 障害発生時間 * 障害発生時刻 * 障害対応完了時刻 * 障害対応者 # 障害概要 <!-- 起こったことを簡潔に記載する。--> <!-- (影響対象がユーザーなら)ユーザーに対して何が起きたかという視点で書く--> ## 影響範囲 <!-- 影響のあったユーザー数や、条件を記載する --> <!-- 会社へのインパクトもわかるとよい --> ## 発見経路 <!-- ユーザーからの問い合わせなのか、運用者がある機能を使っていた時に判明したのか等具体的に書く --> # 発生原因 <!-- なぜ発生したのか、根本原因まで掘り下げて書く --> # 対応内容/手順 <!-- 復旧のために行った一次対応内容を書く --> # 再発防止策/恒久対応 ## MUST ## SHOULD (後述)
障害は早急な情報共有が求められるため、なるべく早く報告を書きます。 自然と内容は技術よりになると思いますが、概要と影響範囲は、非エンジニアやその分野に詳しくない人にもわかるように書きます。
発生原因と再発防止/恒久対応は、対応が可能なように技術的にも詳しく書きます。 もし発生直後調査に時間がかかりそうなことがあれば、後から詳しく追記するようにします。
根本原因と再発防止策
再発防止策で特に重要なことは、以下と考えています。
- 根本原因の追求
- 類似障害の削減
- 新たな開発や運用への利用
本エントリーでは、「EC商品一覧で、特定の絞り込みをした場合に500エラーとなった」 という障害を例に、根本原因と再発防止策を考えてみます。
(こちらは、MERYのECサービスの500エラー画面です。)
さて、この障害の直接的な原因は以下であったとします。(言語はRubyとします)
- 絞り込みで参照しているメソッド内で、特定の条件で未定義の変数が参照されてエラーとなった。
次になぜこのエラーが発生したのかを詳しく追います。 そうすると以下の原因(間接的な原因)に辿り着いたとしましょう。
- 仕様変更のため、絞り込みで使っているメソッドの修正を行ったが、その時メソッドにわたす引数を変更した。
- メソッド内の条件分岐で、過去の引数で処理をする部分が残っていた。
- 検証環境でもそのケースの動作確認が漏れていた。
直接原因->間接原因->根本原因という形で解明していくのには、なぜなぜ分析という手法で、何段階か掘り下げて、都度再発防止のアイデアを考えるのも有効です。このエントリーではもう一段掘り下げてみます。
- なぜ検証環境で動作確認漏れがあったのか?
- 影響範囲の洗い出しが不十分だったから。
- なぜ修正漏れがあったか?
- ユニットテストが無く、新たに作ることもないまま改修を行っていたため、気づかなかったから。
- 同様に影響範囲の洗い出しが不十分だったから。
ここまで落とすと、再発防止も考えやすくなることでしょう。
再発防止策のアイデア出し
注意すべきは、「影響範囲の洗い出しをちゃんとするようにする」、「ちゃんと検証環境で動作確認するようにする」といった意識の内容はよくないということです。 こういった内容だと、時間がたつと忘れてしまったり、新しいメンバーが入ってきた時にどうしても同じことが起きてしまうものです。
最も理想的な再発防止策は、人為的なオペレーションが増えることなく、その障害を意識することがなくなることだと思います。 よって、例えば以下のように具体的なシステム改善やルール化の方がbetterでしょう。
- ユニットテストを追加する
- pull reqestに影響範囲を書くようにテンプレートを作り、影響範囲調査とレビューでの確認をルール化する。
再発防止策のMUSTとSHOULD
考えた再発防止アイデアの中には、すぐにやるのが難しいものもあります。 そこで、例えばすぐにできるユニットテスト追加のようなアイデアをMUST、新しいテスト環境(例えばE2Eテスト)の導入をSHOULDといった形で、障害後必ず対応すべきことと、できると望ましいことに分類して、報告書には記述することにしています。
MUST
実施を義務づけており、毎月一回対応状況の棚卸しを行っています。 特に理由がなければ障害発生後1ヶ月以内に終えていることとし、終わっていないものはcloseせず、担当者になぜ実施できなかったのかを再度検討してもらうようにしています。 また中には根本原因がわからなかったり、直ぐに打てそうなアイデアがない場合には、SHOULDのみということも時にはありえます。
SHOULD
数ヶ月や半年に一度棚卸しを行います。 運用周りの障害が多くなっていれば、改善系タスクへのリソース配分を上げて実施するとか、一方で新たなサービスを作るときにこれらを考えて設計する材料となればよいでしょう。
例えば、ペロリでは先の事例のように「テストがあれば防げた」といった障害がいくつかありました。 そのことから、重要な機能だけでも品質担保できるように、ある一定の期間リソースをあててテスト環境改善という試みを行ったこともあります。 こういったことから、SHOULDはブレストレベルに近いものでも、どんどんあげてもらうのがよいと考えています。
なお、ビジネス企画とチームが分かれている場合は、特に新機能開発に自然とエンジニアリソースを割きたくなることが多いため、いつこのような改善にリソースをあてるかの判断が難しいことはあると思います。 そいういう場合、組織レベルで何かルールを決めておくことができれば、ある程度SHOULDを行うタイミングを考えやすくなるかもしれません。 例えば毎月システム稼働率は99.9%以上といったラインをもうけ、これを下回った場合は回復できるように改善系にリソースを増やすようにする等です。
他チームのエンジニアと振り返る
再発防止対応は、基本その対応者のチーム主体で行うことになります。 ただし、複数チームがあれば、似たような対応を行っていることも多くあります。
ペロリでは、週に一度開発部全メンバー(20名程度)でのミーティングの時間があり、その週に起きた障害の共有も行っています。 障害の件数にもよりますが、このような視点で半年程実施してきて、長くて20分程です。
重要なことは、障害の共有は報告者を責めるのではなく、チームや組織で改善を目指すという姿勢で行うこと。 共有事項が少ないなら簡潔な報告のみでよく、よりよい再発防止のアイデアがあれば気楽にインプットしあい、もし発散してしまいそうなら別途ミーティング後にインプットを行いあうという感じでやっています。
おわりに
以上、ペロリで実施している障害からの再発防止策の考え方と実施例を紹介させていただきました。
ペロリでは、特にこの1年で多くの仲間が増え、新規システムも続々とリリースしてきたのですが、それに合わせて障害件数も増えていきました。 障害報告自体はそれより前から書く文化はありました。しかし、再発防止対応状況をはじめとしたトラッキングが全くできていなかったため、今回紹介した取り組み(フォーマットやルールを定めたり、棚卸しをより明確にすること)を始めました。 実施開始からちょうど半年程となりますが、1年前の障害報告に比べるとより再発防止策も妥当な対応が取れるようになってきた傾向はあり、先に述べたような形で新機能開発と改善系のリソース配分を考える上でも今後とも重要なファクターとしていけると思います。
またこの取り組み自体も、始めた当初はなかなか再発防止が実施されずチケットがcloseされないなど問題がいくつもあり、何度もやり方を見直して今はこの形になっています。 大切なことはシステム障害にとどまらず、どんなことでも失敗(Failure=障害)を振り返って、改善していくことなのではないかと思っています。