Mackerelを中心とした監視設計

こんにちはアプリケーションエンジニアのid:t_kytです。 好きなMackerelプラグインはmackerel-plugin-accesslogです。

今日は監視設計の話をしたいと思います。タイトルにもあるように使うツールはMackerelで、本文にもMackerelの用語が当然のように出てくるので使ったことない方は少しわかりにくいかもしれません。ですが考え方などは別のツールでも応用できると思いますので監視の設計に興味があれば読んでもらえるとうれしいです。

mackerel.io

抱えていた課題

前提として

  • 我々のチームは複数のサービスを見ている
  • Mackerelのオーガニゼーションは全社共通で、同じオーガニゼーションに我々のサービス以外にも他チームが管理しているサービスがある

という状況があります。今回のエントリでは暗黙的に以上のような制約が入っているため、例えば1サービスしか対象の無いチームだったり、オーガニゼーション内のサービスが全て自分のチームの管轄だったりする場合はまた違う設計になると思います。

抱えていた課題としては

  • 監視ルールの設定が属人化していた
  • 新規にサービスやロールを追加した際の設定コストが高い

が大きかったです。

詳しく説明するとまず、「監視ルールの設定が属人化していた」についてはそもそも標準でなにを監視すべきかが明確にドキュメントになっていないので監視設定が担当者に依存していました。担当しているサービスごとに監視項目がバラバラで、あるサービスでは監視している項目が他のサービスではされていないということがありました。もちろん監視項目についてはサービスの特性を把握した上でサービスごとに作って上げるのが理想ですが、その一歩手前にベースラインとして最低限の監視は確保しておきたい状態でした。

「新規にサービスやロールを追加した際の設計コストが高い」については、最近はAWSへの移転やインフラの刷新をしているためサービスやロールの追加が頻繁に起こる状態でしたが、その際の監視設定追加のコストが高いと感じていました。例えば新しくapp-xxxのようなロールのPlackサーバーを追加したときplack workerの監視を手で設定しなくてはならず大変でした。また追加作業が大変なため抜け漏れなく設定するのが大変な状態でした。

以上のような課題を解決するため監視設計を見直す取り組みをしました。

用語や設定方法の整理

上記の課題に取り組む前にまずMackerelの用語の整理と社内的にどのように設定しているかの説明をチーム内で確認しました。例えばMackerelには

などいろいろな監視方法があります。

またメトリック投稿にもデフォルトでMackerelエージェントが投稿してくれるシステムメトリックとユーザーがプラグインや自前のスクリプトを使って投稿できるカスタムメトリックの2種類があります。

この辺りの用語の整理やどういうユースケースに向いているかをチームメンバーに勉強会形式で共有しました。例えば

  • チェック監視はメトリックで表現できない監視に向いている、逆に数値として記録を残したいのであればカスタムメトリックを投稿した上でメトリック監視を使ったほうがいい
  • mkr wrap を使えば簡易的にcronの監視ができるが、もっと凝ったcron監視したければhorensoを噛ませて投稿時間などをメトリック監視したほうがいい

などMackerelの持つ機能を実運用ではどう適用すればいいいかなど実践的な使い方の知識の共有です。

同時にどういうフローで設定ファイルである /etc/mackerel-agent/mackerel-agent.con/etc/mackerel-agent/conf.d/xxx.conf が配布されるかなど社内での仕組みについても解説しました。こういう既存の仕組みへの理解やシームレスに変更できる術がないと結局こうしたいというイメージが出来ても手を出せず終わってしまうと思ったからです。

チームメンバーの知識の底上げは継続的に監視設計の継続的な改善、属人化の防止のために重要だと考えます。監視というのは役割ではなくスキルでありチーム全員で取り組むべきことというのもは『入門 監視』の言葉でもあります。

方針

本題の設計についてです。

メトリック投稿について

取れるメトリックについては極力取る という方針です。Mackerelには豊富なプラグインが用意されているので、ミドルウェアや言語ごとのプラグインは入れられるものは入れました。またAWSインテグレーションも存在するものは設定します。

メトリックを取るホストについてもロール内で1台だけということはせずに基本的に全台で取ります。これについては(チェック)監視についても同様です。

重要なのはメトリック投稿とそれに対して監視を設定するかは切り分けて考えることです。アラートを設定せずともメトリック自体はインシデント事後の調査時に役に立ちます。そして調査のときまで何が役に立つのかわからないため、取れるものはできるだけ取るという方針をとります。またホストごとに傾向が違うということはままあることなので、全ホストでメトリック取れることが望ましいと思います。

サービス/ホストメトリック監視

まず共通で使える比較的緩い基準の監視を作りました。例えば XPF::Common::custom.plack.workers.idle_workers という名前で custom.plack.workers.idle_workers が一定数以下になったらwarningを出す監視です(このメトリックはmackerel-plugin-plackのメトリックです)。

いくつかポイントを解説すると

  • 監視ルール名は機械的に決める
    • custom.plack.workers.idle_workers はメトリック名
    • XPF はチーム名で Common は共通のルールであるという意味
  • 全サービス共通で使えることを意識する
  • 絞込では担当サービスをすべていれロールは * (すべてのロールを対象)にする
  • アラートはwarningのみでcriticalは出さない

という感じです。

名前については数が多くなると名前を考えるのが大変なので機械的につけるようにしました。監視ルール名には日本語も付けられるので監視の説明的なものをかけますが、それはルールのmemo欄に書くことにし名付けコストを減らすことを優先しました。また前提にも書いたように他チームとも共用の一つのMackerelオーガニゼーションであるため XPF という接頭辞を付けてパっと見で自分たちが作った監視ルールであることをわかりやすくしました。

全サービス共通で使えるように意識するというのは XPF::Common::xxx のルールだけでベースラインの監視をまかないたいというモチベーションです。つまり XPF::Common::xxx という監視ルールの絞込に自分たちが担当のサービスを入れればとりあえずOKという状態にしようと考えました。ちなみにMackerelはそのホストに存在しないメトリックの監視ルールは無視されるので、例えばアプリケーションのロールにMySQLのレプリ遅延の監視ルールが設定されていても問題ないです。ホストの画面の監視一覧にもそういうパターンのルールは表示されません。

共通のルールにするポイントとしては CPU% など正規化されたメトリック名を使う/そうでなければ custom.plack.workers.idle_workers などどんなロールでもこれを下回ったらやばいという基準があるものを使うことです。例えばメモリの使用バイトをそのまま監視してしまうとロールによってホストのスペックはバラバラなためうまく共通のものを使えません。

また意味のない監視にならない程度に緩めの基準を設定しアラートもwarningだけにしました。これはもう少し厳しい基準で監視をする必要があればサービスやロールごとに個別に監視を設定するというルールにしたかったからです。XPF::Common はあくまでベースラインを設定し、必要であればそこからサービスごとに特化させていけば良いと考えました。

外形監視

これは使っているドメインについては無条件で設定しhttpsで配信する場合は証明書の監視を入れることにしました。また外形監視は必ずしもサービスに紐付ける必要はないのですが、紐付けるとレスポンスタイムがサービスメトリックとして投稿してくれるので必ず紐付けるというルールにしました。

チェック監視

チェック監視は柔軟性がある一方で何でもできるので逆に難しいですが、概ね以下の2種類は必ず入れるようにしました。

  • プロセスの監視
  • 関係するコンポーネントへの疎通の監視

プロセスの監視についてはそのホストで動くべきプロセスが動いているかの監視です。例えばWebアプリケーションが動いているのであればcheck-httpで監視、cronやunboundの監視が必要なのであればcheck-procsで監視するなどです。Plackアプリケーションもcheck-procsで監視しても良いですが、結局望ましい状態はhttp通信ができる状態であるためcheck-procsではなくcheck-httpで監視しています。この辺りはなんのために監視するのか、どの監視でどの異常パターンをカバーするのかという話になってくるので一つ一つ考えるといいと思います。

疎通の監視についてはそのホストが接続するコンポーネントへの疎通があるかの監視です。例えばappホストであればMySQLのホストに対してcheck-mysqlする、リバースプロキシであればappを束ねるLBへhttp-checkするなどです。

AWSマネージドサービス

RDSやElastiCacheなどは基本的にはAWSインテグレーションでメトリックを取得した上で適切な監視ルールを設定します。その上で足りないメトリックがあった場合は別途監視用のホストを用意しそこからプラグインによって監視してます。エージェントのホストと監視対象が異なる場合custom_identifier機能が使えます。

warningとcritical

どの監視にも共通して

  • warningでいいものはwarningにする
    • 障害でなければcriticalにしない

にしています。いずれ障害につながるメトリックの異常はアラートされるべきですが、緊急の対処が必要でないならばwarningにとどめておくことを意識しています。

似たような監視ルール

たとえばNLBターゲットグループnlb.host_count.TARGET_GROUP_NAME.(un)healthy (ターゲットグループの死んでいる/生きているホスト数)に対するメトリック監視とクライアントからNLBに向けたcheck-httpないしcheck-tcpのチェック監視はクライアントからバックエンドに疎通が通るかという意味では似たような監視になってます。が、前者は クライアント<->NLB 間のネットワーク的な不具合は感知できなかったり、逆に後者はバックエンド1台だけ不調でunhealthyになっていることは感知できません。

監視の方法が変わればカバーできる範囲は当然変わります。一見同じようなことをしてる監視でも、カバーできる範囲が異なっていれば重複する範囲があることは特に気にせず設定します。ある監視ルールがどういうシナリオを想定してるのか(そしてどういうシナリオはカバーできないか)を明確に定義できるのが理想でしょう。

memo欄の活用

監視ルール(あるいはアラートグループ)にはmemo欄がありSlackなどへの通知の際にアラートと一緒に投稿されます。対処方法やサービスごとの留意点などを書いたドキュメントのURLを貼っておくと対応がスムージになるので設定しています。

f:id:t_kyt:20190624121918p:plain

運用してみてよかったこと/課題に感じているところ

良かったところは自分たちの管理しているルール内で最低限の監視は設定してあるという安心が得られ、そこからのインクリメントでより良い設計を目指せるようになったことです。ベースラインを定義したかったと言っているように、今後より良い監視していくための足場が出来たように感じられます。

課題点としてはやはり共有ルールでは難しいパターンがあることです。例えばユーザーリクエストをさばくアプリケーションサーバーでCPU100%使い切ってる状態というのは、理想的にリソースを使っているパターンは稀で、大体は何かしらの問題が起きています。そういう問題を感知したいので例えば90%以上になったらアラート出すルールを設定するわけです。

一方でバッチサーバーの場合100%使い切って処理するというのは割とよくあることですし、時間内に終わるのであればまったく問題ない場合のほうが多いです。こういう同一のメトリックでもロールによって意味合いが異なるパターンは存在しそういうものを今回のような設計で扱うには難があります。Mackerelの場合対象のサービス/ロール設定の他にさらに除外するロールを選べるためそういったもので凌ぐ手もありますが、そうすると"Common"という名前からは離れたものになっていきます。

このあたりは管理コストの低さをとって共通設定にしたデメリットなので、徐々にサービスごとに監視設定を整備していきCommonは少しずつ解体していく、同時に新規にサービスを作った際もれなく監視ルールを設定できる仕組みを別途構築していくことも視野に入れて改善を積み重ねていきたいです。