151
123

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

前置き:Rubyのキューイングシステム

Rails(Ruby)で非同期にキューイングしてくれるライブラリといえばSidekiqですよねっていうくらいSidekiqが好きなんですが、世の中のシェア的にはResqueなのかな。でも、Sidekiqの方が安定的に動かせているので僕は好きです(delayed_jobは知らない)。

ShoryukenというAWS SQSを使ったキューイングシステムもあり、RedisじゃなくてSQS使いたいっていう場合は、これも良いのかもしれないですね。まぁ、ローカルの場合面倒そうな感じもありますけど。

まぁともかくSidekiqを導入してキューイングするっていうのは、もう超絶簡単にできるわけです。できるんですけど、実際にラフに運用していくと、キューがいつの間にかこけて処理待ちで埋まってたり、レアなケースで例外投げて死んでたりするわけです。そんなSidekiqと仲良く暮らしていくために、死にゆくキューを知る方法をまとめてみました。

Sidekiq Proという存在

Sidekiqにはエンタープライズ向けなPro版があります。年間$950。それなりにしますね。このProと通常版、どう違うのかというと…

  • Reliability: プロセスが死んだりRedisが死んでも、キューを復元できる
  • Batches: 複数のジョブをグループ化して進行管理できる
  • Support: サポート!!!

ここらへんは、Sidekiq Proを1年ほど使ってみて良かったところ、困ったところという@sumyappさんのスライドが非常に参考になります。

失敗したキューを知る

普通にコードがミスってたり、データがおかしかったりして例外投げて死んでいったキューを知りたいというケースであれば、Pro版を使わなくともなんとかなります。

以降のサンプルスクリプトはRailsのActiveJob経由での話になります。が、まぁ適当に置き換えてもらえれば素のSidekiqでも問題ないと思います。

まず、Sidekiqを起動します。

$ bundle exec sidekiq -q default

適当なジョブファイルを作成します。そして、実行したら必ず例外を投げて死亡するようにしておきます。

jobs/hoge_job.rb
class HogeJob < ActiveJob::Base
  queue_as :default

  def perform(hoge_id:)
    raise "error!" # 明示的に例外投げる
  end
end

rails consoleからキューを積みます。すると、Sidekiqを起動しているコンソール上では、当然のようにエラーログが出ます。

> HogeJob.perform_later(hoge_id: 100)

=> Enqueued HogeJob (Job ID: e0693a53-9860-4e73-9479-674dd84ccaac) to Sidekiq(default) with arguments: {:hoge_id=>100}

次に、rails consoleでSidekiq::RetrySetを取得します。ここにはリトライ内容が入っています。countで件数を見ると1件ありますね。

> rs = Sidekiq::RetrySet.new
> rs.count
=> 1

次は中身を見てみましょう。

> rs.first
=> #<Sidekiq::SortedEntry:0x007ff571fece00
 @item=
  {"class"=>"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
   "queue"=>"default",
   "args"=>
    [{"job_class"=>"HogeJob",
      "job_id"=>"e0693a53-9860-4e73-9479-674dd84ccaac",
      "queue_name"=>"default",
      "arguments"=>[{"hoge_id"=>100, "_aj_symbol_keys"=>["hoge_id"]}]}],
   "retry"=>true,
   "jid"=>"1044bc2eb37b250b8eea6137",
   "created_at"=>1450159336.0921764,
   "enqueued_at"=>1450159336.0922325,
   "error_message"=>"error!",
   "error_class"=>"RuntimeError",
   "failed_at"=>1450159336.0988874,
   "retry_count"=>0},
 @parent=#<Sidekiq::RetrySet:0x007ff57208ef48 @_size=2, @name="retry">,
 @queue="default",
 @score=1450159361.0989082,
 @value=
  "{\"class\":\"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper\",\"queue\":\"default\",\"args\":[{\"job_class\":\"HogeJob\",\"job_id\":\"e0693a53-9860-4e73-9479-674dd84ccaac\",\"queue_name\":\"default\",\"arguments\":[{\"hoge_id\":100,\"_aj_symbol_keys\":[\"hoge_id\"]}]}],\"retry\":true,\"jid\":\"1044bc2eb37b250b8eea6137\",\"created_at\":1450159336.0921764,\"enqueued_at\":1450159336.0922325,\"error_message\":\"error!\",\"error_class\":\"RuntimeError\",\"failed_at\":1450159336.0988874,\"retry_count\":0}">

素晴らしい。どんな引数で、どんなエラーが発生したかが分かりますね。

Sidekiqダッシュボードに表示する

手動でSidekiq::RetrySetを見れば失敗したキューの情報を取得できます。が、このままでは使いにくいです。そこで、Sidekiqのダッシュボードに表示するようにします。ここで良い感じにやってくれるGemがあるので紹介します。

基本、こいつを入れればOKです。

gem 'sidekiq'
gem 'sinatra', require: false
gem 'sidekiq-failures'

rails serverを立ちあげて http://exampl.ecom/sidekiq にアクセスすると、「失敗」というメニューが追加されたダッシュボードが表示されます。

sidekiq_failer.png

一覧だとこんな感じ。

FailedJobs1.png

クリックして詳細も見ることが出来ます。

FailedJobs2.png

今すぐ再試行などもできるので、使い勝手もいいですね。

API的にも使えそうなヘルパーメソッドも用意されています。

Sidekiq::Failures.count

Sidekiq::Failures.reset_failures

ダッシュボード拡張Gem

mhfs/sidekiq-failures以外にもダッシュボードを拡張してキューの一覧が見られるGemを後輩エンジニアから教えてもらいました。どちらもGemfileに突っ込んであげるだけで使えるので簡単。

sidekiq-history

sidekiq-history.png

こんな感じで表示されます。失敗成功関係なしに、全キューの情報が見られるようです。これはこれで便利…!

sidekiq-statistic

sidekiq-statistic.png

sidekiq-statisticは高機能で、通常のキューと失敗したキューを見ることができます。また、各キューにかかった処理時間の平均値とか出してくれます。ただ、失敗したキューの詳細を見ることはできないようです。できるのかな?

失敗したらSlackに通知する

今やSlackに通知を送るのはデファクトスタンダード的なノリになっているのでSlackのWebHookを使ってキューが失敗したら通知してあげるようにしましょう。素晴らしいGemがあるので、それを利用します。

gem 'slack-incoming-webhooks'

Incoming WebHooksのAPIキーを発行するには、こちら

jobs/hoge_job.rb
class HogeJob < ActiveJob::Base
  queue_as :default

  rescue_from Exception do |e|
    slack = Slack::Incoming::Webhooks.new 'https://hooks.slack.com/services/poyo/fuga/hogepiyo'

    attachments = [{
      title: "Sidekiq failure",
      text: "%s failure \n QueueName %s \n Error %s \n JobId %s \n Arguments %s" % [self.class, self.queue_name, e.inspect, self.job_id, self.arguments],
      color: "#fb2489"
    }]
    slack.post "", attachments: attachments
  end
  
  def perform(hoge_id:)
    raise "error!"
  end
end

これで、予め設定しておいたSlackチャンネル宛にキューの情報が投げられます。

slack.png

いい感じですね!

あとは、再実行とかをSlackBotに指示できたりすればChatOps的ですね。

Zabbixで監視するためのAPI

ZabbixにSidekiqの状態を教えてあげるAPIサーバを設定します。

config/routes.rb
require 'sidekiq/api'

# 積んでいるキューの数
get "queue-status" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.size.to_s ]] }

# リトライ対象のキューの数
get "queue-retry" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::RetrySet.new.size.to_s ]] }

# キュー待ち時間
get "queue-latency" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.latency.to_s]] }

これで、 http://example.com/queue-retry でリトライ対象のキューの数が取得できます。あとはZabbixの設定をすればOK。nagiosはプラグインがあるみたいです。

AirBrakeを使う

AirBrakeという素晴らしいサービスを使っていればエラーを検知することができるので、こっちもオススメです。無料でも使えますが、有料プランにするか、自分でサーバをたててErrbitを動かすかが良いですね。お金があるなら、有料プランのほうが楽ちんです。

airbrake.png

AirBrakeでも失敗内容が詳しく見られるので、対応が可能です。

以上、こんな感じでSidekiqで静かにそして非業の死を遂げたキューを知る方法をまとめてみました。それぞれ、運用にあったやり方があると思うので、より良い方法あるよーというのがあれば是非教えて下さい。けっこうキューイングシステムを運用していくとぶち当たる壁って多いので…。

151
123
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
151
123

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?