本記事はクービック Advent Calendar 2015の24日目の記事です。
クービック と Popcornでは、ジョブキュー として Delayed Job を導入しています。本記事では、導入から監視までの流れをご紹介します。
なお、アプリケーションは Rails 4.2 で書かれています。
事前調査
Rails4.2 から Active Job が導入されています。
Active Jobの主要な目的は、Railsアプリを即席で作成した直後でも使用できる、自前のジョブ管理インフラを持つことです。これにより、Delayed JobとResqueなどのように、さまざまなジョブ実行機能のAPIの違いを気にせずにジョブフレームワーク機能やその他のgemを搭載することができるようになります。バックエンドでのキューイング作業では、操作方法以外のことを気にせずに済みます。さらに、ジョブ管理フレームワークを切り替える際にジョブを書き直さずに済みます。
様々なジョブキューバックエンドのアダプターを Active Job が引き受けてくれるので、これを利用しない手はないです。
Active Job adapters にサポートしているキューシステムのリストがあります。このリストに独自に調べた Middleware を足した表がこちらです。
Async | Queues | Delayed | Priorities | Timeout | Retries | Middleware | |
---|---|---|---|---|---|---|---|
Backburner | Yes | Yes | Yes | Yes | Job | Global | beanstalkd |
Delayed Job | Yes | Yes | Yes | Job | Global | Global | RDBM, Redis, Mongoid, etc |
Qu | Yes | Yes | No | No | No | Global | Redis, Mongoid |
Que | Yes | Yes | Yes | Job | No | Job | PostgreSQL? |
queue_classic | Yes | Yes | No* | No | No | No | PostgreSQL |
Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes | Redis |
Sidekiq | Yes | Yes | Yes | Queue | No | Job | Redis |
Sneakers | Yes | Yes | No | Queue | Queue | No | CLOUDAMQP |
Sucker Punch | Yes | Yes | No | No | No | No | Celluloid(threads) |
Active Job Inline | No | Yes | N/A | N/A | N/A | N/A | N/A |
Ruby Toolbox の Background Jobs は 以下のようになっておりました。
ジョブキューシステムの選定基準はいろいろあろうかと思いますが、新たな Middleware の管理が不要であること、ならびに 実績が多いことを重視し、Delayed Job を採用しました。
なお、MySQL をキューに使用する 5 つの微妙な方法とその落とし穴 (翻訳版) という記事で指摘されているように、サービス規模が大きくなった場合には見直しが必要かもしれません。
導入
delayed_job を Rails なアプリケーションに導入する記事は世にたくさんあるので、やったことを軽くご紹介するに留めておきます。
Gemfile
gem 'delayed_job'
gem 'delayed_job_active_record'
gem 'daemons'
config/application.rb
config.active_job.queue_adapter = :delayed_job
config/initializers/delayed_job_config.rb
Delayed::Worker.default_queue_name = 'default'
Delayed::Worker.delay_jobs = !Rails.env.test?
Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
delayed_jobs テーブルの migration を忘れずに。ここまででインストールは完了です。
次に、デーモン再起動を自動化する方法を探します。
capistrano-delayed-job と capistrano3-delayed-job があり、「どっちがいいの?」と疑問が湧きますが、Delayed Job の wiki では後者が推奨されているので、素直に capistrano3-delayed-job を使います。
使い方は Capfile と deploy.rb に数行加えるだけ。カンタン。
Capfile
require 'capistrano/delayed-job'
config/deploy.rb
set :delayed_job_workers, 1
set :delayed_job_roles, [:app]
監視
ジョブキューシステムを導入したら、正常に稼動しているかを監視しなければなりません。
弊社では監視に Mackerel を利用していますが、Delayed Job についても チェックプラグインの仕組みを利用して監視を行っています。
/etc/mackerel-agent/mackerel-agent.conf
[plugin.checks.delayed_job]
command = "sudo -uxxxx -i bash -c 'env RAILS_ENV=production bundle exec rails runner ./bin/mackerel_plugin_checks_delayed_job'"
bin/mackerel_plugin_checks_delayed_job
10.times do
sleep 1
# Find pid file
pid_file = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp', 'pids', 'delayed_job.pid'))
# Read pid file
File.open(pid_file, 'r') do |f|
pid = f.read
end
next unless pid && pid =~ /\A\d+\n\z/
# Find process
ps = `ps #{pid}`
next unless ps =~ /delayed_job/
# Find failed jobs
failed_jobs = Delayed::Job.where('last_error IS NOT NULL').where('attempts >= ?', 3)
if failed_jobs.count > 0
puts "failed jobs count: #{failed_jobs.count}"
exit 2
end
puts "running"
exit 0
end
puts "not running?"
exit 2
ほとんど説明不要な気もしますが、プロセスの存在監視と3回以上失敗しているジョブの監視をしています。
Go でプラグインを書くとカッコイイ気がするんですが、時間がなかったので(若干やっつけ仕事で)ruby スクリプトにしてます。
監視に引っ掛かった時は、Slack に Mackerel の bot が通知してくれます。
まとめ
クービック と Popcornにおける Delayed Job の導入〜監視をご紹介しました。
何らかの参考になれば幸いです。