Sansan Builders Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

New Relic Alert でサービス全機能のエラー監視を実現させてみた

Eight事業部プロダクト部 Platform Group / Engineering Manager の 藤井洋太郎(yotaro) です。

このエントリは New Relic Advent Calendar 2019 - Qiita の12/13分のものです。

前回、以下のNew Relicを活用したパフォーマンスチューニング事例の記事を書かせていただきましたが、

そこからいろいろとご縁があり、今ではすっかりNew Relic活用大臣と化しています🤵🏻

さて、今回は「Eightサービス上の全ての機能ごとに400系-500系のエラー急増の監視を New Relic Alert にブチ込んだ話」をしたいと思います。

Eight のこれまでの監視

Eight ではこれまでもインフラ基盤やアプリケーション上で発生する「予期せぬエラーや障害」を検知し迅速に対応に入れる仕組みづくりを積極的に行ってきました。

が、先日、ある機能にて、ある条件下において機能が利用できないという不具合が発生してしまいました。

そして予期せぬ事態の際はアラートが上がると思い込んでいたため、この問題に気付くまでに時間がかかってしまいました。(気付いたきっかけはユーザさんからのフィードバックでした🙏🏻)

原因はクライアントエラー

機能停止に至った原因は、アプリのアップデート時にAPI仕様違反が一部条件で発生し400エラーとなるケースがあったためでした。

4xx系エラーはクライアント側の問題ということもあり、これまで Eight では監視対象としてこなかったのでした。 また、今回の件は一部条件下で発生するもので、リリース前の受け入れテストでも見つけられませんでした。

400系も監視対象に

重要機能での機能停止が発生することもある400系エラーは検知できるのに越したことはないということで、以下要件で新たに監視を組み込むことにしました。

  1. 400系と500系のエラーレートを監視対象とする
  2. 機能単位のエラーレートを監視し一定期間で 5% を超えたらアラート
  3. 「すべての」機能(Transaction)に対して監視を入れる

これを New Relic Alert で力づくで実現させたので、ここからはその方法を簡単に共有します💪🏻

New Relic Alert

New Relic Alert では New Relic 上で取得されるデータを用いて、柔軟に条件をカスタマイズして監視機構を作ることができます。 (詳しくは 公式ドキュメント から)

NRQL(New Relic Query Language) を使って New Relic Insight のEventsデータベースに問い合わせることができ、これがもはや何でもできて結構ヤヴァいです。しかもメチャハヤ。

この NRQL を使って4xx-5xx系のエラーレート監視を実現していきます。

①ある機能の HTTP Response Status の4xx-5xxエラーレートをウォッチする

例えば、 HogeController#show というAPIのエラーレートを見たい場合、TransactionhttpResponseCode を集計することでエラーレートを可視化することができます。`

SELECT percentage(count(httpResponseCode), WHERE  '400' <= httpResponseCode AND httpResponseCode <= '599')
FROM Transaction WHERE name LIKE 'Controller/hoge/show'
TIMESERIES 1 hour -- 時間軸で追いたい場合
SINCE 1 weeks ago -- 時間軸で追いたい場合

f:id:yotaro-fujii:20191212114345p:plain
1週間のある機能のエラーレートの推移

なるほど、悪くなさそうですね💡あくまで上記は単なるチャートですが、これを Alert に落とし込めば機能単位でウォッチできそうです。

②NRQLをベースにした Alert Condition を作成する。

Alert Condition は APM, Browser, Mobile などNew Relic各サービスのメトリックと簡単に連携させることができますが、自作で組み立てた NRQL をアラートに組み込むことも簡単にできます。

f:id:yotaro-fujii:20191212123621p:plain

Threshold Type

設定画面をみても分かる通り、閾値のタイプに StaticBaseline というものがあります。

Type 内容
Static 名前のとおり、静的閾値設定ですね。
時間や季節要因によってデータが左右されない場合に大きな効果が期待できそうです。
Baseline ダイナミックベースラインアラートと呼ばれています。
過去のメトリックデータからベースラインが動的に設定されるとのこと。予測精度は使えば使うほど、向上するようですね。すごい! 時間毎に変化していくメトリックデータの場合に使うのが良さそうです。

どちらの設定にするかは、機能の特性を見て決めていく必要があります。

③すべての機能/Transactionに対してConditionを登録する...!

ある機能に対して、エラーレートの設定ができたので、あとは全ての機能/Transactionに設定していくだけです。頑張りましょう!

とは、なりませんね。Transactionは数百ありますし、日々機能は追加され改善されていくため、メンテナンス可能なものにしていかなければなりません。

ということで、API を利用して自動化させていきます。

公式ドキュメントは以下を参考に。
Introduction to New Relic APIs | New Relic Documentation
New Relic API Explorer

🤖自動化の流れ🔧

  • ① 全ての Transaction を NRQL で取得する。
  • ② 取得したTransactionを Alert API を利用して Condition に登録する。
  • ③ 上記をcronなどで定期実行させる

① 全てのTransaction を取得する

全て と書きましたが、今回は直近2週間以内のアクティブなトランザクションに絞ります。 これは次のような NRQL で取得することができます。

SELECT uniques(name) FROM Transaction LIMIT 2000 SINCE 2 weeks ago

これを Insight API を利用して取得します。以下はRubyでのFardayを使った実装例です。

url = 'https://insights-api.newrelic.com/v1/accounts/<アカウントID>/query'

conn = Faraday.new(url: url) do |builder|
  builder.response :json, content_type: /\bjson$/
  builder.response :raise_error
  builder.adapter Faraday.default_adapter
end

conn.get do |req|
  req.headers['Content-Type'] = 'application/json'
  req.headers['X-Query-Key'] = '<管理画面から取得できるQuery Key>'
  req.params[:nrql] = 'SELECT uniques(name) FROM Transaction LIMIT 2000 SINCE 2 weeks ago'
end

# => { 'results' => [ ['Controller/hoge', 'Controller/fuga', ...] ] }

Transaction名がわかれば、Condition登録時の NRQL Query も動的に作ることができます。

② 各Transactionを Alert API を利用してConditionとして登録する。

以下は Alert API で NRQL Query Condition を追加する実装例です。 取得した全ての Transaction をループさせて、1件1件登録していく感じです。

url = 'https://api.newrelic.com/v2/alerts_nrql_conditions/policies/<Policy ID>.json'

conn = Faraday.new(url: url) do |builder|
  builder.response :json, content_type: /\bjson$/
  builder.response :raise_error
  builder.adapter Faraday.default_adapter
end

conn.post do |req|
  req.headers['Content-Type'] = 'application/json'
  req.headers['X-Api-Key'] = '<管理画面から取得できるAPI Key>'
  req.body = {
    nrql_condition: {
      name: '<Transaction name>', # 64文字以内
      runbook_url: 'https://exmple.com/runbook',
      enabled: true,
      value_function: 'single_value',
      terms: [ { duration: '5', operator: 'above', priority: 'critical', threshold: '5', time_function: 'all' } ]
      nrql: {
        query: "SELECT percentage(count(httpResponseCode), WHERE '400' <= httpResponseCode AND httpResponseCode <= '599')" \
        " FROM Transaction WHERE name LIKE '<Transaction name>'",
        since_value: '3',
      },
    }
  }.to_json
end

③ 上記をcronなどで定期実行させる

①-②を組み合わせた処理を定期実行させることで、直近ActiveなTransaction全てに対してアラートを設定することができます。

注意点として、上記スクリプトをそのまま実行してしまうと、同じ条件のConditionが複数設定されてしまうため、 NRQL Query Condition List で事前にリストを取得した上で、既に設定済みのTransactionがあれば登録しない。といった工夫を入れる必要があります。

効果 / 所感

現在 Eight では数百のTransactionに対してエラーレート監視が稼働しています。 これまでサービス全体の監視がメインだったのですが、機能毎に異常検知を行えるのはかなり魅力的です。今まで気付いていなかった傾向やケースが見えてきそうです。

実際に運用を始めてみての課題もあり、現状は5%で一律で設定しているところの妥当性や、リクエスト頻度が少ない機能の誤検知など、機能毎にチューニングしていく必要はありそうです。

とはいえ、全ての機能を監視できているという安心感はとてもとても大きいです😊

Feature Request

使っていて感じた機能要望です。中の人に届けッ!🕊

  • NRQL Query の Baseline Alert は API から扱えない
    詳細は書かなかったのですが、できないです。そのため、全ての設定をまず Static で登録し、Baseline が適したものは手動で設定し直しました。

  • NRQL で join 的なことができない
    実は、前週と比較してといったような少し凝ったAlert検知をしたかったのですが、Joinできず断念。

  • Transactionの Response Status による Alert 設定、APMでできていいのでは?
    頑張ってスクリプト組んだわけですが、New Relicの公式でサポートされててもいい気がしました。

まとめ

今回は New Relic API を利用して、Eight上で発生する全てのトランザクションに対してエラーレート監視を導入した事例を紹介しました!
一つの障害から、問題を汎化し、対応することができた良いケースであったと思います。

最前線で、監視する

全ての問題を事前に把握できていれば何も問題は無いですが、現実はかなり難しいです。

そういった中で、あらゆるケースにいち早く気づき、影響範囲を最小限に抑える。
どちらもやらなくっちゃあならないってのが、基盤チームのつらいところですね。

みなさん、最前線で頑張っていきましょう!


buildersbox.corp-sansan.com

buildersbox.corp-sansan.com

© Sansan, Inc.