Pairs Back-endのエラー対応運用を改善する話
この記事は、Eureka Advent Calendar 2022 22日目の記事です。
こんにちは。Eureka Back-end Teamの金井です。
今年もPairsではさまざまな機能がリリースされました。
上記に加え、他にも多くの機能の追加や改修が実施されています。
新しい機能が増えるにつれ、Pairsのコードベースはますます大きくなってきています。このような状況下においてはPairsを支えるシステム全体の運用・保守をうまく進め、その効果を大きくすることは極めて重要になってきています。
Back-end Teamも運用・保守のやり方を改善するべく日々奮闘しており、自分もその一環として数ヶ月前からチームのエラー対応周りの運用改善に着手し始めました。というのもチームのエラー対応の体制にはまだまだ改善できる余地があると思ったからです。
今回の記事では、この改善活動でどのような改善を行い、どんなことを考えていたのか書いてみたい思います。
改善前の状況と問題
弊社Back-end Teamは主にAPIサーバーや管理画面、バッチシステム及びそれらが利用するマイクロサービスを監視しています。SRE Team協力の下、これら監視の仕組みは私が入社した時点で既にある程度整った形で存在しており、その後も改善が続けられてきました。
アプリケーションのエラー検知についても同様で、エラーログは開発担当者が設定したseverityに基づき出力され¹、収集・集計されます。ある期間内の集計値が閾値に到達したerror / warn logはSlack上アラートによってメンバーに周知され、severityがerror以上のログは個別の通知もされます²。
一方で、エラー対応はメンバーの半ば有志制でした。つまり、エラー検知に気づいた人がトリアージし、対応するか、その問題領域に詳しい人に相談するといった具合です。この方法でもある程度はワークしましたが、以下のような問題がありました。
エラー対応に貢献する人に偏りが生じる
エラー対応に貢献できる人と、(他の業務の都合等で)対応できない人に分かれてしまいます。 結果としてチーム全体の責務であるにもかかわらず、メンバー間の負荷に不均衡が生じます。
専門家に知識が集中する
問題領域にある種の専門性を要する対応では、結局その領域に詳しい人しか反応できず、対応することになります。結果として、その領域の知識・経験が特定のメンバーにのみ集約され続けます。
エラーログへの対応の質が、個人の知識・経験に依存する
トリアージやメンバーへの相談など、エラーログに対する個人の対応方法が、その個人のスキルに依存してしまいます。
エラーそのものの存在を一部のメンバーが気づかない
エラーログはSlackで通知されますが、エラー通知をwatchする頻度・程度はメンバーに依存しています。
エラー対応プロセスの変更
上記のような問題が少なからずチームの生産性向上を妨げていたので、下記のようなシンプルなプロセスをチームで運用していくことに決めました。
- 発生したエラーログは定期でまとめて起票する。
- 起票したタイミングで、ローテーションで担当者をアサインし、対応期限を決める。
- 起票されたエラーログを毎朝の定例会議でトリアージし、対応方針を決める。
- 担当者は期日までにエラーログに対応する。必要であれば他のメンバーに質問するか、ペアプロを依頼する。
チームでトリアージする
トリアージだけなら短時間ですみますが、少し時間があれば大まかな対応方針も決めてしまいます。対応方針が決まることで、対応者が着手しやすくなります。朝会のようなメンバーが揃っているタイミングで実施すると方針を決めやすく、また各エラーに対する対応方針が各メンバーへ共有されるので、ナレッジをチーム全体で貯めていくことができます。加えて、メンバー全員でエラーに対してトリアージ・対応方針を行うことで、エラーログ対応に対する温度感をメンバー間で揃えることができました。
以下は、トリアージと対応方針決定の一例です。
・外部API call 時のHTTP status code 504のような、外部APIサービスの一時停止に起因するエラー
→ リトライやタイムアウト調整等の対応でも回避が難しい場合、エラーで検知したい&メトリクス計上したいので、そのまま
・APIサーバーがクライアント起因のHTTP status code 400返戻時に出力されるエラー
→ログのseverityを適切でないとし、warn/infoに落とすなど
・エラーとして出力するのは正しく、かつ対応する必要がある
e.g. 外部API call 5XX error → retryableならリトライ・タイムアウトの調節、非同期化(キューに投げ、同期処理と切り離すなど)
e.g. 実装ミス
→ テストケースやQAで網羅されなかった場合など。各ケースで対応
プロジェクト施策関連の場合プロジェクトboardで管理し、Back-end Team責務と判断したものはBack-end Team用boardで引き続き管理
チーム全体による対応方針の決定によって、対応に関する知識をメンバー全員に溜めていけるので、レビュー時の負荷を下げることができたり、個々人の開発に活かせたりします。
また担当者がアサインされた対応のことを忘れてしまうことも多かったのですが、朝会でのトリアージ・対応方針決定が担当者へのリマインダーを兼ねるようになり、対応完了までのリードタイムが短縮されました。
2. エラー内容に関係なく、メンバー全員がローテーションで対応するようにした
今までは有志制だったエラーログ対応の担当者を起票のタイミングで決めるようにしました。担当者はローテーションで決まり、メンバー全員が均等にエラーログに対応する機会を持ちます。
担当者のエラーログ対応について、以下のようなスタンスを持っています。
- 担当者は最終的にエラーの解決に責任を持つが、すべてをその人がやる必要はない
- トリアージ・対応方針はチームで決める(もちろん担当者がわかれば担当者が決めて良い)
- 対象領域に詳しくなければ、チーム全体や詳しい人に質問して良い
- 実作業でもわからない点があれば、Slackで相談したりmtgを設定したり、ペアプロを設定したりして良い
- プロジェクトタスクなどでどうしても手が回らない場合、他のメンバーと交渉して担当者を変わってもらっても良い
この方針によって、以下の良い点がありました。
- 担当者が詳しくない問題領域に対応していく過程で、その問題領域を学習できる機会を得るようになりました。次回以降、同じ領域の問題をより短時間で対処できるようになり、またその問題領域に詳しくないメンバーの手助けができるようになりました。
- 明示的な期限の設定によって、個人が持つタスクの中でエラーログ対応の優先度が相対的に低くなり続ける傾向を防止しました。
- 各メンバーが均等にエラーログ対応を担当するので、エラーログに対する関心をチーム全体で高めることができました³。
一方で、エラーに係る領域にあまり詳しくない人が担当した場合、そうでない人に比べ時間がかかるという点が懸念されます。これは上記メリットとのトレードオフですが、上記で述べたように長期的には個人の学習によって対応時間の問題は改善でき、またチーム全体にもスケールできると判断し、このやり方を支持しています。
ただ職能チームとプロジェクトチームを兼任するメンバーが多く⁴、後者のスケジュールが過密になると手が回らないので、
プロジェクトタスクなどでどうしても手が回らない場合、他のメンバーと交渉して担当者を変わってもらっても良い
としています。
運用変更後の課題
運用に変更を加えたことで、別の問題も抱えるようになりました。
定期的な起票作業の発生
運用の変更のみ行ったので、起票などはまだ手作業で実施していました。 かなり時間がかかるわけでもないですが、いわゆるToilに該当する作業ではあります。
属人性
上記の起票は自分が担当していますが、もし現場を離れた場合にどのようにエラーを起票したりアサインしてよいかわからない人が出てきたり、メンバーの大半が入れ替わった段階で上記エラー対応(とその活動自体を改善する)文化の消失もありえます。
上記問題は単に起票を自動化すれば解決できるので、運用変更後に自動化も実施することにしました。
自動化
自動化にあたり、エラー管理可能な外部サービスを使用することを検討しました。理由としては上記課題を踏まえ、以下のニーズがあったためでした。
- エラー発生時にIssueを自動起票したい
- 起票したエラーを自動でグルーピングしたい
- 上記を全て自前実装すると、機能の実装と運用に人的コストがサービスの使用よりもかかることが懸念されるため⁵、それを避けたい
今回はSentryを使用することにしました。理由としては上記ニーズを満たせるのに加え、下記が挙げられます。
- 起票したエラーをそのままツール上で管理できる
(アサインやステータス、コメントを残せるなど) - SlackやGithubといったメジャーなプラットフォームと連携可能
他の候補として、DataDog上のError TrackingやGCPのError Reporting等も検討しました。エラーグルーピングの機能やログ管理機能もあり、かつ既に使用しているプラットフォーム上の機能であり導入&統合が楽そうだったので候補としてましたが、運用含めた管理がSentryの方がやりやすそうだったので結果的にSentryを採用しました。
Sentryへの移行によって、たくさんのうれしいことがありました。
- エラーログ対応のIssue起票を手動で行う必要がついになくなりました!また、Sentryがエラー発生時に即時起票してくれるため、エラーの発生から起票までのリードタイムがなくなりました。
- 過去に発生した同様のエラー履歴も管理できるようになりました。これにより、過去対応した内容を参照しやすくなりました。
- トリアージで暫く対応不要と判断したエラーはSentry上で無視できるようになりました。後で再度トリアージしたい場合、一定期間のみ無視することもできます。
- 必要であれば、Github上にIssueを起票することもできます。
Sentryはとても便利で、掘れば掘るほどエラー管理に便利な機能が見つかります。一方で機能の多さ・柔軟性ゆえ複雑なところも多く、チームがSentryにある程度なれる必要性も感じました。
新たな問題と対応
Sentryに移行したことで、新たな問題が出てくるようになりました。
エラーのグルーピングが思い通りにならないことがある
急に具体的な話になってしまいますが、Sentryのグルーピングが思った通りにならないことがあり、大きな問題というほどではないもののある程度悩みの種になっていました。
例えば同じグループにまとめてほしいエラーが別々になったり、逆に別グループにしてほしいエラーが同じグループであると識別されることがあります。
こうなると、アサインされたメンバーがどのエラーに対応すればよいのかわからなくなったり、複数のアサインメンバーが同じ問題だと気づかずに個々に対応してしまったりします。
同じグループにまとめてほしいエラーが別々になってしまう
この問題に対しては、Sentry上のグルーピングルールを設定して対応することにしました。
Sentryはフィンガープリントルールとスタックトレースルールと呼ばれるルールベースでグルーピングをカスタムする機能を提供しています。この機能を使えば、ユーザーは同じグループにまとめてほしいエラーを自身の意向でカスタマイズできます。
また、あまり起票されないようなエラーにいちいちルールを追加するのが面倒な場合、Issue 管理画面上で類似Issueをレコメンドしてもらい一時的にグルーピングを指定することもできます。
上記のルール追加と動的なグルーピング指定により、同じグループにまとめたいエラーに対しては徐々に対応できるようになりました。
別グループにしてほしいエラーが同じグループであると識別される
こちらの問題には、エラーログにヒントを追加するアプローチを選択しました。
私たちのチームではログをJSON、いわゆる構造化ログの形式で出力していますが、Sentryはそれらのfieldをヒントにグルーピングをしています。例えば下記のIssueはfieldmessage
を元にグルーピングされていますが、メッセージ上のヒントが少なく異なる内容のエラーが一つにグルーピングされています。エラーの詳細は別のfieldvalue
に含まれていますが、アサインされたメンバーは一見何をすれば良いのかわかりません。
そこで、エラー内容を表す value
を message
のsuffixに追加してみます。これによりSentryに与えるヒントが増やすことができ、適切に分類させることができました。またエラーログとしても内容がひと目で見えるようになりました。
Sentryのためにエラーログの内容をいじりすぎることに対しては抵抗がありますが、今回はむしろ現在扱っているログを見直す良い機会になりました。ただまだSentryを使い始めてまだ日が浅いので、より良い改善方法が見つかればそちらに切り替えることも検討したいと思います。⁶
おわりに
今回の改善によって生まれた環境は、ともすると組織によっては当たり前なのかもしれません。ただ今回の改善で、そのような環境を自ら作っていくというのはやはり大変なことなんだなということを痛感しました。
一方でチームの動きやプロダクトの質が少しずつ良い状態になっていくのが見えてくると、思い切ってやってみてよかったなと思います。来年も引き続き、組織が抱える課題をうまく解決できる動きができるようやっていきます。
[1] 弊社Back-end Teamで取り扱うログのseverityは基本logrus
に準拠しています。
[2] チームメンバーの尽力により、個々の通知を実施しても耐えうる程度にはエラー頻度を抑えられています。概ね日に1桁くらいです。
[3] 朝会でトリアージと対応方針の決定をするようにしたことも、この傾向を強化しているように思えます。
[4] 弊社は職能に基づくチームと、施策達成のために動くプロジェクトチームがあります。
[5] 特定の目的を解決する、小さな機能を実現するための、依存関係が小さく捨てやすいツールであれば問題ないと考えています。