Rails8でキュー処理の環境をつくる
Rails8のバックグラウンドキュー処理(ActiveJob)をsidekiqで。
Redisもdockerで構築する。
RailsとSidekiqが連携する仕組み
- Rails側でジョブを登録する
→ 「やってほしい仕事」をキュー(Redis)に入れる - Sidekiq側がそのキューを見てる
→ Redisからジョブを取ってきて、処理を実行する - RailsとSidekiqは「Redisを介して」間接的につながっている
まずはRails8の環境構築
- 参考
- sidekiqディレクトリを作成し、compose.yamlを作成
mkdir sidekiq
cd sidekiq && touch compose.yaml
- backendディレクトリを作成し、更に配下にapiディレクトリを作成
mkdir backend && mkdir backend/api
- 必要ファイル(Dockerfile,Gemfile,Gemfile.lock)作成
cd backend && touch {Dockerfile,Gemfile,Gemfile.lock}
この時点でのディレクトリ構成
sidekiq/
├── backend/
│ ├── Dockerfile
│ ├── Gemfile
│ ├── Gemfile.lock
│ └── api/
└── compose.yaml
- Dockerfile
FROM ruby:3.3.0
ENV APP /api
ENV TZ Asia/Tokyo
WORKDIR $APP
ADD Gemfile $APP/Gemfile
ADD Gemfile.lock $APP/Gemfile.lock
RUN bundle install
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
- Gemfile
source 'https://rubygems.org'
gem "rails", "~> 8.0.0"
- compose.yaml
services:
api:
build: ./backend/
platform: linux/x86_64
environment:
TZ: Asia/Tokyo
volumes:
- type: bind
source: ./backend/api
target: /api
- type: volume
source: gemdata
target: /usr/local/bundle
ports:
- "3000:3000"
volumes:
gemdata:
最低限のRails環境を構築する(apiモード+minimal)
- rails new
まずはノーマルのapiモードでrails new。
minimalだとactive-jobがskipされるので--no-skip-active-jobオプションをつける。
docker compose run --rm api rails new . --api --minimal --no-skip-active-job
ずらずらずら
backend/apiにインストールされる。
- api/config/environments/development.rbに以下を追加
config.hosts.clear
この状態でrailsを起動
docker compose up -d
Redisコンテナの追加
「Redis」は、Railsそのものの機能ではなく、オープンソースのインメモリデータストアです。バックグラウンドジョブのキュー管理: 例えば、Sidekiq などのジョブキューライブラリが、ジョブの状態管理やキューとしてRedisを利用します。
まずredis用のディレクトリを作成
mkdir redis
dockerにredisを設定
redis:
image: redis:latest
volumes:
- type: bind
source: ./redis
target: /data
ports:
- "6379:6379"
docker compose upでredisコンテナを作成
(Railsを起動したままだったら、docker compose down後にup)
- redisの起動確認
docker compose exec redis redis-cli
以下のredisプロンプトが出るのでpingを打ってPONGと返ってきたらOK
127.0.0.1:6379> ping
PONG
これでredisは完了
Railsにsidekiqを追加
- 参考
sidekiqのgemをinstall
bundle addはGemfileに追加してからinstallする
docker compose run --rm api bundle add sidekiq
configを編集/設定ファイル作成
module Myapp
class Application < Rails::Application
# ...
config.active_job.queue_adapter = :sidekiq
#...
config/initializersにsidekiq.rbを作成
sidekiq.rbは無いので作成
touch backend/api/config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://redis:6379' }
end
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://redis:6379' }
end
configにsidekiqの設定ファイルを作成
初期状態ではdefautのキューが1つだけなので、設定ファイルを作成し追加。
touch backend/api/config/sidekiq.yml
# 基本設定
# デフォルトで同時に5つのジョブを並行処理する。
# スレッド数が5本。
:concurrency: 5
# 環境ごとの設定
# staging 環境とproduction 環境では、並行処理数(スレッド数)を10にする。
staging:
:concurrency: 10
production:
:concurrency: 10
# ジョブの優先度
# :queues で、優先的に処理するジョブのキュー順序を指定しています。
# 1. critical(最優先)
# 2. default
# 3. low(最後)
:queues:
- critical
- default
- low
ForemanでRailsサーバーとsidekiqを同時に起動
- foremanをインストール
docker compose exec api bundle add foreman
- Procfileを作成し編集
touch backend/api/Procfile
web: rm -f tmp/pids/server.pid && bundle exec rails s -b 0.0.0.0
worker: bundle exec sidekiq
- Dockerfileの最終行に以下を追記
foremanから起動させる
# foremanで起動
CMD ["foreman", "start"]
- Dockerfileをいじったのでbuild
docker compose build
APIモードのRailsではセッションを有効化する
Sidekiq Web UIをAPIモードのRailsで使いたい場合、
セッションを有効化する設定が必要。
# config/application.rb
module YourApp
class Application < Rails::Application
# 以下を追加
# セッション周り追加
config.middleware.use ActionDispatch::Cookies
# ここのkeyは何でもいい
config.middleware.use ActionDispatch::Session::CookieStore, key: '_sidekiq_session'
end
end
sidekiq Web UIのroute設定
railsのルーティングにsidekiqのキュー確認画面を追加する。
require 'sidekiq/web'
Rails.application.routes.draw do
# 以下を追加
mount Sidekiq::Web => "/sidekiq"
end
- dockerを起動する
docker compose up -d
- railsのURI(http://localhost:3000/sidekiq)
以下のような確認画面になる。
Jobの作成
- 例としてファイルをコピーするだけのジョブを作成する
docker compose exec api rails g job CopyFile
- ジョブファイルができるので中身を次のようにする
require 'fileutils'
class CopyFileJob < ApplicationJob
queue_as :default
def perform(source_path, destination_path)
# ファイルをコピー
FileUtils.cp(source_path, destination_path)
puts "ファイルコピー完了: #{source_path} -> #{destination_path}"
end
end
動作確認
tmpにtest.txtを作成
touch backend/api/tmp/test.txt
rails cコマンドでCopyFileJobを動かす
docker compose exec api rails c
- perform_now
キューに積まないでそのまま実行
sidekiqにも表示されない
api(dev) > CopyFileJob.perform_now('/api/tmp/test.txt','/api/tmp/test_cp.txt')
ファイルコピー完了: /api/tmp/test.txt -> /api/tmp/test_cp.txt
- perform_later
ActiveJob経由でSidekiqキューに積む
戻り値はJobオブジェクトになるのでprovider_job_idを取得すればjob IDが表示される。
api(dev) > job = CopyFileJob.perform_later('/api/tmp/test.txt','/api/tmp/test_cp.txt')
api(dev) > job.provider_job_id
=> "e9160212ef0134245ce7a105"
Sidekiqは完了したジョブの履歴を保存しない
Sidekiqはデフォルトでは完了したジョブの履歴を保存しません。
そのため、標準のAPI (Sidekiq::Queue, Sidekiq::RetrySet, Sidekiq::DeadSet) では完了済みのジョブを取得することはできません。
なんだと...。この辺は別に書きます。