たろすの技術メモ

Jot Down the Tech

ソフトウェアエンジニアのメモ書き

Sidekiqを導入する方法

概要

ActionMailerでdeliver_laterするため、以下を参考にしながらSidekiq(とRedis)を導入したのでその備忘録です。

prabinpoudel.com.np

gemを追加

gem 'sidekiq'
gem 'redis'

$ bundle installします。

Sidekiqを有効化

# config/application.rb
config.active_job.queue_adapter = :sidekiq

Web UI to Monitor Jobsを有効化

Sidekiq::Webconfig/routes.rbにマウントします。これにより、SidekiqのダッシュボードをWebブラウザから閲覧できるようになります。

# config/routes.rb

require 'sidekiq/web'

Myapp::Application.routes.draw do
  # mount Sidekiq::Web in your Rails app
  mount Sidekiq::Web => "/sidekiq"
end
  • mount:これはRailsのルーティングのメソッドで、特定のパスにRackベースのアプリケーションをマウントするために使用されます。RackはRubyでWebサーバーとアプリケーションを接続するためのインターフェースで、RailsSinatraもRackベースのフレームワークです。
  • Sidekiq::Web:これはSidekiqが提供するSinatraベースのWebアプリケーションで、Sidekiqのダッシュボードを提供します。このダッシュボードを通じて、現在のジョブの状態、ジョブの履歴、統計情報などを閲覧することができます。

設定ファイルを作成

config/initializers/sidekiq.rb

redis_config = YAML.load_file('config/redis.yml')[Rails.env]
redis_config['db'] = redis_config['db']['sidekiq']

Sidekiq.configure_server do |config|
  config.redis = {
    url: "redis://#{redis_config['host']}/#{redis_config['db']}"
  }
end

Sidekiq.configure_client do |config|
  config.redis = {
    url: "redis://#{redis_config['host']}/#{redis_config['db']}"
  }
end

このコードは、Rubyのジョブキュー処理ライブラリであるSidekiqの初期化設定を行うものです。

まず、最初の3行では、config/redis.ymlから現在の環境(Rails.env)に対応するRedisの設定をロードしています。その中で特にSidekiqが使用するデータベース(db)を指定します。

redis_config = YAML.load_file('config/redis.yml')[Rails.env]
redis_config['db'] = redis_config['db']['sidekiq']

次に、Sidekiqがサーバーモードで起動した時の設定を行います。configure_serverメソッドを用いて、Sidekiqがジョブを処理するために接続するRedisサーバーの設定を行います。ここで指定された設定はサーバーモードでのみ有効です。

Sidekiq.configure_server do |config|
  config.redis = {
    url: "redis://#{redis_config['host']}/#{redis_config['db']}"
  }
end

最後に、Sidekiqがクライアントモードで起動した時の設定を行います。configure_clientメソッドを用いて、Sidekiqがジョブをキューにプッシュするために接続するRedisサーバーの設定を行います。ここで指定された設定はクライアントモードでのみ有効です。

Sidekiq.configure_client do |config|
  config.redis = {
    url: "redis://#{redis_config['host']}/#{redis_config['db']}"
  }
end

これらの設定により、Sidekiqは指定されたRedisサーバーを用いてジョブのキューイングと処理を行うようになります。

config/redis.yml

default: &default
  db:
    sidekiq: 0

development:
  <<: *default
  host: redis

test:
  <<: *default
  host: redis

staging:
  <<: *default
  host: localhost

このコードは、アプリケーションのRedis設定を行うYAMLファイルです。

まず、defaultセクションでは、デフォルトの設定を定義しています。ここでは、異なるデータタイプごとにRedisデータベースを分けています。Sidekiqはデータベース0を使用します。これにより、それぞれのデータタイプが他のデータに影響を及ぼすことなく、同じRedisインスタンスを利用することができます。

default: &default
  db:
    sidekiq: 0

次に、各環境(開発、テスト、ステージング)の設定を定義しています。ここでは<<: *defaultを使ってデフォルトの設定を継承し、それぞれの環境でRedisサーバーのホスト名を指定しています。開発環境とテスト環境ではredisを、ステージング環境ではlocalhostをホスト名として使用します。

development:
  <<: *default
  host: redis

test:
  <<: *default
  host: redis

staging:
  <<: *default
  host: localhost

これにより、各環境ごとにRedisの接続設定を独立させることができます。これは、例えば開発環境と本番環境で異なるRedisサーバーを使用したいといったケースで有用です。

config/sidekiq.yml

:verbose: false
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:concurrency: 10
:queues:
  - default
  - test

このコードは、Sidekiqの設定ファイル config/sidekiq.yml の一部です。SidekiqはRubyのバックグラウンドジョブ処理ライブラリで、非同期にタスクを実行するために使用されます。

  • :verbose: false : Sidekiqがログに詳細な情報を出力するかどうかを制御します。ここでは、冗長な(verbose)出力をオフにしています。
  • :pidfile: ./tmp/pids/sidekiq.pid : SidekiqのプロセスIDを保存するためのファイルのパスを指定します。これはサーバーが起動している間、そのプロセスIDを保持します。
  • :logfile: ./log/sidekiq.log : Sidekiqのログが出力されるファイルのパスを指定します。
  • :concurrency: 10 : 同時に実行可能なジョブの最大数を指定します。この場合、10個のジョブを同時に処理できます。
  • :queues: : このセクションでは、Sidekiqがジョブを検索するキューの名前を指定します。Sidekiqは上から順にキューをチェックし、ジョブが存在すればそのジョブを実行します。ここでは 'default' と 'test' という2つのキューが設定されています。

コラム:concurrencyは何を指定するのが妥当か?

Sidekiqの concurrency 設定は、同時に処理されるジョブの数を指定します。適切な値は、アプリケーションの具体的な要件と、ホストシステムのリソース(CPU、メモリ、I/Oなど)に大きく依存します。

一般的なガイドラインとして、以下のような考え方があります:

  1. CPUの数とスレッドの数: CPUコアの数と同じ、またはそれより少し多い数の並行性が良いとされています。ただし、あまりに多くのスレッドを作成すると、システムのオーバーヘッドが大きくなり、パフォーマンスが低下する可能性があります。
  2. I/O待ちの時間: I/O待ちが多い(データベースへのクエリ、ネットワークリクエストなど)ジョブの場合、より高い並行性が有効になることがあります。これは、一部のジョブがI/Oによってブロックされている間に、他のジョブがCPUを使用できるからです。
  3. メモリ: 各スレッドはメモリを消費します。したがって、メモリ不足を引き起こさないためには、使用可能なメモリに基づいて並行性を設定する必要があります。

具体的な値を決めるには、異なる concurrency の値で実際にテストを行い、パフォーマンスを評価するのが最善の方法です。

Dockerを使用している場合

# == ここから ==
redis:
  image: redis:latest
  ports:
    - "6379:6379"
  volumes:
    - "./tmp/cache/redis:/data"
    - "./db/redis.conf:/etc/redis.conf"
# == ここまで ==

# 省略
app: &app
  tty: true
  stdin_open: true
  build: .
  command: bin/dev
  volumes:
    - .:/opt/app
    - app-bundle:/usr/local/bundle
  ports:
    - "3000:3000"
    - "3030:3030"
    - "3035:3035"
  depends_on:
    - db
    - redis
    - mail
  environment:
    REDIS_URL: redis://redis:6379 # 追加

# == ここから ==
sidekiq:
  <<: *app
  command: bundle exec sidekiq
  ports: []
  depends_on:
    - db
    - redis
# == ここまで追加 ==