LoginSignup
12
7

More than 5 years have passed since last update.

Sidekiqの安全な再起動方法

Posted at

sidekiqとは、Rubyで非同期処理を行うためのライブラリです。
sidekiqを安全に再起動する構成の一例をここで紹介します。

課題

sidekiqで実行しているワーカーの更新には、sidekiqの再起動が必要になります。
capistrano等でデプロイを自動化している場合、sidekiqの再起動も同時に行いたいです。
しかし、それには問題があります。
sidekiqで実行するようなワーカーは、数十分、数時間かかるものもあるかもしれません。そのようなジョブが実行中にデプロイされることで再起動がかかり、また一から実行する、なんてことになってしまいます。
ですので、ワーカーが終了したかをチェックし、終了を確認してから再起動する必要があります。

capistrano-sidekiqでは、上記の課題を解決できませんでした。
なので独自で再起動の仕組みを実装します。

環境

ubuntu 18.04.2 LTS
rails 5.2.1
sidekiq 5.2.2 (ActiveJobと連携)
supervisord 3.3.1
ruby 2.5.1 (rbenvでインストール)
capistrano 3.11.0

目標

capistranoでデプロイしたときに、ワーカーが全て終了しているsidekiqプロセスはすぐに再起動、ワーカーが実行中のsidekiqプロセスはワーカーが全て終了したら再起動するようにする。
railsサーバは複数ホストの場合も想定。

supervisor

sidekiqのプロセスをsupervisorで管理します。

設定ファイルの例です。

/etc/supervisor/conf.d/sidekiq.conf
# 通常のワーカー
[program:normal]
directory=(railsrootディレクトリ)
environment=RAILS_ENV=production
command=/home/ubuntu/.rbenv/shims/bundle exec sidekiq --config config/sidekiq/process/normal.yml
process_name=normal
numprocs=1
autostart=true
user=ubuntu
redirect_stderr=true
stdout_logfile=/var/log/supervisor/sidekiq_normal.log

# sidekiqを再起動するためのワーカー
[program:restart]
directory=(railsrootディレクトリ)
environment=RAILS_ENV=production
command=/home/ubuntu/.rbenv/shims/bundle exec sidekiq --config config/sidekiq/process/restart.yml
process_name=restart
numprocs=1
autostart=true
user=ubuntu
redirect_stderr=true
stdout_logfile=/var/log/supervisor/sidekiq_restart.log

sidekiqを再起動するためのワーカーを用意するのがポイントです。

sidekiqの設定

railsのsidekiq設定の例です。

config/sidekiq/process/normal.yml
:tag: normal
:verbose: true
:concurrency: 5
:queues:
 - user
 - purchase
 - default
config/sidekiq/process/restart.yml
:tag: restart
:verbose: true
:concurrency: 1
:queues:
 - <%= `hostname`.strip %>
:max_retries: 0

queuesにhostnameを設定するのがポイントです。
複数ホストの場合は、ワーカーがどのhostで実行されるかは、sidekiqが勝手に決めてしまいます。
デプロイしたホストで必ずsidekiq再起動ワーカーを実行してほしいので(supervisorで再起動するため)、このようなテクニックを使っています。
この方法は公式で紹介されています。

sidekiq再起動ワーカー

ワーカーを再起動させるワーカーを用意します。

class RestartSidekiqProcessWorker < ActiveJob::Base
    def perform
      # 新規キューの実行を受付けないようにする
      target_processes.each(&:quiet!)
      sleep 10

      target_ids = target_processes.map {|ps| ps['identity']}
      # 実行中ワーカーが完了するまで監視して安全に終了させる
      while true
        # 全てのワーカーが終了かつすでに再起動済みでないプロセスは再起動する
        target_processes.select{|ps| ps['busy'] == 0 && target_ids.include?(ps['identity'])}.each do |ps|
          restart(ps)
          target_ids.delete(ps['identity'])
        end
        break if target_ids.count == 0
        sleep 10
      end
    end


    private

    def target_processes
      # 自分自身のプロセスは無視
      target = Sidekiq::ProcessSet.new.to_a.delete_if{|ps| ps['tag'] == 'restart'}
      # 自分のhost以外は無視
      hostname = `hostname`.strip
      target.delete_if{|ps| ps['hostname'] != hostname}
    end

    def restart(ps)
      # supervisorで再起動
      ps_name = ps['tag']
      `sudo supervisorctl restart sidekiq:#{ps_name}`
    end
end

ワーカーを実行するスクリプトを用意します。
queueにhostnameを指定することで、必ずこのスクリプトを実行したホストでワーカーが実行されます。

script/sidekiq/restart.rb
RestartSidekiqProcessWorker.set(queue: `hostname`.strip).perform_later

capistranoの設定

taskを用意し、deploy後に上記ワーカーを実行するように設定します。

lib/capistrano/sidekiq.rake
namespace :sidekiq do
  task :restart do
    on roles(:app) do
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rails, :runner, "script/sidekiq/restart.rb"
        end
      end
    end
  end
  before 'deploy:published', 'sidekiq:restart'
end

以上の構成で、capistranoでデプロイした時に、自動で安全に再起動されます。
なお、sidekiq再起動ワーカーのプロセスは再起動されません。このワーカーに変更があった場合は、手動で再起動する必要があります。

ワーカーを再起動するワーカーを用意する、というややこしい構成になってしまいました。
もっとシンプルな方法があればいいのですが…。

参考

https://github.com/mperham/sidekiq/wiki/FAQ#how-do-i-ensure-a-job-processes-on-a-given-machine
http://n8.hatenablog.com/entry/2015/05/01/100424

12
7
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
12
7