はじめに
最近、本番環境のシステムの一部移行にあたって、Datadogを利用した監視設計と監視環境の整備を行う機会がありました。
その際の気付き・学びなど振り返りも兼ねて、久しぶりにこのブログを書いています。
特に今回は、アラート対応といった運用プロセスを他チームに移管したこともあり、インシデント対応に少しフォーカスした内容になっています。
(概念的な話がメインとなっており、内容自体はある程度一般化してまとめています。)
監視の基本
前提となる部分で押さえておきたい概念・考え方を記載しておきます。
基本的には「入門 監視」に書いてあることがベースになっています。
www.oreilly.co.jp
正常状態の把握
大前提として、CPU使用率など監視項目の観点で「正常に動いている」とはどういう状態かを定義する必要があります。
特定のAgentやサービスなど稼働を前提とするものがあれば、それも正常状態の定義時に考慮します。
- CPU使用率:oo % 以下
- 通常稼働しているサービス:xxx, xxx
特に、OS関連のメトリクス (ex. CPU usage) はそのままでは利用できないため、正常状態を表現できるように利用する必要があります。
CPU使用率の高騰とサービスが正常に動作しないことは相関がある可能性が高いですが、CPU使用率の高騰 = 正常に動いてない という単純な紐付けは禁物です。
MySQLが継続的にCPU全部を使っていたとしても、レスポンスタイムが許容範囲内に収まっていれば何も問題はありません。
これこそが、CPUやメモリ使用率などの低レベルなメトリクスではなく「動いているか」を基準にアラートを送ることが有益である理由です。
(入門 監視より)
例えば、CPU使用率がoo%を5分連続で超えた場合はサービスが正常ではない可能性が高いと定義して、アラート対応の中でサービスの稼働確認を行う形で利用しています。
OS関連のメトリクスをあえて利用している理由として、正常に稼働しなくなるという予測の観点でもアラートを飛ばすことができるためです。(正確な予測アラートであれば、事象発生前に対応の準備ができるというメリットがあります。)
アラート自体がサービスの稼働確認を行う外形監視も一つの選択肢としてあるかと思います。
継続的な改善
正常状態の把握を踏まえてアラートを設定する場合は、閾値の設定が重要なポイントになります。
oo%をxx分連続で超えた場合は正常ではないのでアラートを発砲する、と定義する際には計測した結果がベースとなります。
過去のインシデントや負荷試験の結果を基に、閾値を仮決めします。
その上で、計測 → 確認 → 閾値の修正 ... といった形で改善フローを回していくと、最終的には正常性を表現できている閾値の設定ができるようになるはずです。
全く同じアプローチで、SLI/SLOの定義と継続的な改善を行うことがベストプラクティスとして紹介されています。
blog.newrelic.co.jp
手順書(runbook)の整備
上記の方法でアラートを設定した上で、アラートに紐づける形で手順書(Runbook)を整備します。
特に、Runbookの整備では属人性を排除することを一番のポイントに挙げています。
より踏み込んだ内容に関しては、この後の部分でまとめます。
インシデント対応を見据えたポイント
次に、インシデント対応に焦点を当てた (Runbookの整備も踏まえた) 監視設計のポイントをまとめていきます。
インシデント対応の全体像
最初に、対応の全体像を把握しておきましょう。
オンコール対応に関する内容なのですが、全体像の把握も含めてPagerDutyのドキュメントが参考になります。
response.pagerduty.com
必要な要素をざっくりまとめると下記の通りです。
- Prepare: アラート設定や対応に必要な権限・環境の整備 etc.
- Triage: 問題の緊急性の判断
- Fix: 事象に対する原因調査・対応
- Improve: 事象の根本解決を想定した対応、ポストモーテムの作成
- Support: オンコールのスケジュール調整、オンコール当番間での引継ぎ
この全体像を踏まえて、インシデント対応を見据えた考慮点をまとめていきます。
アラートの発砲に対する意味づけ
(アラートの重要度の定義にもよりますが) アラートの発砲自体をTriageの第一歩と見なすこともできると考えました。
つまり、アラートによって事象の重大度を表現できるのであれば、それを基に対応フローや別チームのメンバーへのエスカレーションといった後続のプロセスにつなげることができます。
単に事象発生の可能性があるので準備するだけでいいのか、実際に事象が発生しているので早急な対応が必要なのか etc. はアラートそのもので判断できるべきです。
(最近読んだ、Datadogのe-bookにも同じようなことが書かれていました。)
ここで気をつけたこととしては、アラートで扱う事象のscopeを極端に大きくしないことです。
先ほど記載したような、事象対応への準備や対応といった部分も一つのアラートで扱うのではなく、分けて扱うべきと考えました。
Datadogでは、テンプレート変数を利用して単一のアラート (Monitors) の中で条件分岐をさせて、通知するメッセージと重要度をパターン分岐させることができます。
そのため、管理単位 (Monitors) としては単一ですが、アラートとしては複数種類 (ex. Alert, Warning)のアラートを発砲することができます。
単一のMonitorsの定義の中でAlert, Warningの発砲を行う際の閾値を設定します。
Datadogのテンプレート記法の話になりますが、下記のようにAlert, WarnのセクションごとにSlackでの通知時のメンション(@sreteam)と通知先チャネル(@slack-hogehoge-alert-emer)を指定できます。
// alert
{{#is_alert}}
...
{{!-- \@sreteam --}}<!subteam^hogehoge>
@slack-hogehoge-alert-emer
{{/is_alert}}
{{#is_alert_recovery}}
...
{{!-- \@sreteam --}}<!subteam^hogehoge>
@slack-hogehoge-alert-emer
{{/is_alert_recovery}}
// warning
{{#is_warning}}
...
{{!-- \@sreteam --}}<!subteam^hogehoge>
@slack-hogehoge-alert-warn
{{/is_warning}}
{{#is_warning_recovery}}
...
{{!-- \@sreteam --}}<!subteam^hogehoge>
@slack-hogehoge-alert-warn
{{/is_warning_recovery}}
上記の観点でアラートを整理することによって、アラートに紐づくRunbookに関して手順内の複雑性を最小限にすることができます。
コンテキスト (スイッチ) に対する理解
先ほど、Runbookの書き方について触れましたが、Runbook内のパターン分岐に関しても留意しながら手順を整理していました。
特に、緊急対応が必要な時のパターン分岐・その場での判断は非常にリスクが高く、対応する側としても非常にストレスフルな状態になります。
パターンの漏れが無いように単一のアラートで十数種類の対応パターンを用意しても、対応時に適切な判断ができるかは難しいと思います。
なるべく分岐回数は少なくして、上から手順を流して処理できればベストと感じました。
Slack channelについても同様で、通知されるchannelに関してもコンテキスト (どのような意味合いで利用されているか) を確認して利用する必要があります。
このchannelは全てのメッセージをwatchする、これはちゃんと見なくてもいい etc. のようなchannelごとの暗黙的なコンテキストスイッチが存在する可能性があることに留意しましょう。
(自分の場合は、アラートの種別と通知先について移管先のチームに共有した上で認識齟齬が無いかを確認していました。)
終わりに
以上、インシデント対応を見据えた監視設計で考えたことについてまとめました。
監視設計に関しては、メトリクスの扱いからアラートの中身まで考えることが多く、奥深いなと実感しています。
サービスの安定稼働を裏で支えられる監視設計ができるように日々勉強していきます。