SidekiqのキューのレイテンシーをDatadogで監視したい

2024/04/10に公開

Saleshubでエンジニアをしている安田です。
今日はSidekiqのキュー情報の監視についてお話したいと思います。

Sidekiqのキューの状態を監視したい

なんらかの理由でSidekiqのジョブがキューに詰まって、バックグラウンドジョブがなかなか実行されず、お客さんから問い合わせが来る、なんてご経験はありませんか?
今回はそんな問題(ジョブがキューに溜まりすぎる)が発生する前にそれを検知し、速やかに対応するためにはどうしたらいいか?についてRailsアプリを事例にお話ししたいと思います。

なので、今回ここで扱う技術は以下のようなリストとなります。

  • Ruby on Rails(アプリケーション)
  • Sidekiq
    • Middleware
  • Datadog
    • Grok Parser
    • カスタムメトリクス

Sidekiqの有料版について

Sidekiqには無料のコミュニティ版と、有料のPro、Enterpriseがあり、それぞれできることが異なります。当然ではありますが
コミュニティバージョン < Pro < Enterprise
で可能なことが増えていきます。

Sidekiqのキューの監視についても、Enterpriseバージョンを利用している場合は、DatadogのSidekiqインテグレーションで取得できるsidekiq.enqueuedを監視すれば、それで事足りるかもしれません。

なので、今回のお話は、

  • Pro以下のバージョンを利用しているが、Enterpriseバージョンでは取得できるsidekiq.enqueuedと同等の情報が欲しい
  • キューのサイズだけでなく、レイテンシーの情報が欲しい

そういったニーズをお持ちの方々の参考になる情報になるかと思います。

監視実現のために必要な作業

Pro以下ではSidekiqのインテグレーションで取得できる情報に限りがあり、キューに関する情報は取れません。またログを監視したとしてもSidekiq + Railsのデフォルトのログではキューに関する情報は記録されていません。

そのため、以下の戦略を取ることにしました。

  1. Railsの処理内でSidekiqのキュー情報をログ記録する
  2. ログをDatadogに転送する(これは元々設定済みだった)
  3. 転送されたログからGrok Parserを使ってキューの情報を抽出する
  4. 抽出したキューの情報でDatadogのカスタムのメトリクスを作る
  5. そのメトリクスをモニター(監視)する

作業の詳細

Sidekiqのキュー情報をログ記録する

RailsでSidekiqのキュー情報を取得するにはいくつかの方法がありそうです。

  1. cronで定期的にSidekiqのキュー情報をログ書き出しする
  2. Sidekiqのmiddlewareでキュー情報をログ書き出しする

今回は、上記二つのうちでより簡単な方法である2のSidekiqのミドルウェアを利用してみたいと思います。

SidekiqのMiddlewareの書き方については公式ドキュメントに詳しく書かれていますが、今回のケースだと下記のような実装になります。

まずは、ミドルウェアから呼び出されるクラスを定義します。

class SidekiqQueueLogger
  def call(worker_class, msg, queue, redis_pool)
    queues = Sidekiq::Queue.all
    queues.each do |q|
      Rails.logger.info "[#{Time.current}] QueueInfo name: #{q.name} latency: #{q.latency} size: #{q.size}"
    end
    yield
  end
end

クラスの中では、Sidekiq::Queue.allで全てのキューを取り出し、それをループして、

  • キューの名前
  • レイテンシー
  • キューのサイズ

を記録します。
Sidekiq::Statsからも各種統計情報が取得できますので、そちらを参照するでも類似の情報は取得できるかと思います。

次にそのクラスをクライアントミドルウェアのチェーンに加えます。

Sidekiq.configure_client do |config|
  config.client_middleware do |chain|
    chain.add SidekiqQueueLogger
  end
end

上記を実行すると、アプリケーションログに以下のような内容が書き出されます。

[2024-04-10 13:18:20 +0900] QueueInfo name: default latency: 0.00998997688293457 size: 1

ちなみに、これはクライアントミドルウェアyieldの前に処理を記述しているので、redisにジョブの情報が書き込まれる前にログを書いていることになります。
そのあたりの説明はSidkiqのミドルウェアの公式ドキュメントの説明を読まれることをお勧めします。

ログをDatadogに転送する

上記アプリケーションログをどうやってDatadogに転送するかについてはSidekiqインテグレーションのページをご参照ください。

転送されたログからGrok Parserを使ってキューの情報を抽出する

今度は転送されたログから、書き込まれたキューの情報を抽出する必要があります。
これを実現するには、DatadogのGrok Parserを使う必要があります。

今回の場合だとこんな感じのルールを追加することで情報を抽出することができます。
具体的には、DatadogのサイドメニューLogs -> Configurationの中のパイプラインのRubyをCloneし、クローンされたRubyパイプラインの末尾(でなくても本当はどこでも良いです)にAdd ProcessorGrok Parserを追加します。
parsing rulesは今回の場合は、下記のようになります。

latency_rule .*QueueInfo name: %{notSpace:sidekiq.queue.name} latency: %{number:sidekiq.queue.latency} size: %{number:sidekiq.queue.size}

ここでは、レイテンシーとキューのサイズをnumberとして抽出しています。上記で

[2024-04-10 13:18:20 +0900] QueueInfo name: default latency: 0.00998997688293457 size: 1

のようなログから下記のような情報を取り出すことができます。

{
  "sidekiq": {
    "queue": {
      "size": 1,
      "latency": 0.00998997688293457,
      "name": "default"
    }
  }
}

抽出したキューの情報でDatadogのカスタムのメトリクスを作る

今度は抽出した情報からメトリクスを作成します。
メトリクスは
DatadogのサイドメニューLogs -> Generate Metricsからadd new metricで作成することができます。

メトリクスのクエリは

@sidekiq.queue.latency:>10

のような形式になります。
今回は10秒を超えたケースにカウントするメトリクスとしていますが、ログにはキューのサイズも切ろうしていますので、関心ごとがキューのサイズなのであればそちらを参照したメトリクスを作成することもできます。
閾値はそれぞれの環境に合わせて設定するのが良いと思います。

そのメトリクスをモニター(監視)する

あとは上記で作成したメトリクスを監視します。
監視(モニター)はDatadogサイドメニューのMonitors -> New Monitor -> Metricから作成することができます。
Generate Metricsで作成した際のMetric Nameがモニターの対象になります。
あとはそれぞれのニーズに合わせて、メトリクスの情報の評価の仕方やWarning, Alertの閾値を設定していきます。

以上の手順でRailsアプリにおいて、Sidekiq EnterpriseでなくてもSidekiqのキューの状態を監
視できるようになります。

不明点、ご意見あったらDMください

Saleshubではこんな感じで開発メンバーがサービスの信頼性や安定性を確保するために日々創意工夫をしております。

もし

  • 上記手順でもよくわからないから質問したい
  • もっとこうやったほうがいいよ、というご意見がある
  • Saleshub(のプロダクト開発)に興味が湧いた

といった方いらっしゃいましたら@TakataNoToshiまでDMください。
お待ちしています。

Discussion