Sidekiq on Dockerサンプル
この記事では、SidekiqをDockerコンテナで動作させるためのセットアップ、実行手順を説明します。
はじめに
Rubyでジョブ管理を行おうと考えたとき、Resque、Delayed::Job、Sidekiqなどの仕組みがあります。さらに定期的にジョブを実行したいと思い方法を探していると、Sidekiq-Cronやsidekiq-schedulerの解説が多く見られました。このため、Sidekiqを使うことにしました。
なお、Rails 5ではジョブ実装の共通的な仕組みとしてActive Jobが使えますが、Active Jobがsidekiq-schedulerなどで使えるか分からなかったので、今回はSidekiqをそのまま使うことにしました。
今回の手順で作成したプロジェクトは、 u6k/sample-sidekiq にあります。
前提
筆者はRails開発をvim + Dockerで行っており、ホストPCにRubyをインストールしてはいません。Dockerおよびdocker-composeを使用しますので、あらかじめインストールしておいてください。
$ docker version
Client:
Version: 18.03.0-ce
API version: 1.37
Go version: go1.9.4
Git commit: 0520e24
Built: Wed Mar 21 23:10:06 2018
OS/Arch: linux/amd64
Experimental: false
Orchestrator: swarm
Server:
Engine:
Version: 18.03.0-ce
API version: 1.37 (minimum version 1.12)
Go version: go1.9.4
Git commit: 0520e24
Built: Wed Mar 21 23:08:35 2018
OS/Arch: linux/amd64
Experimental: false
$ docker-compose --version
docker-compose version 1.21.0, build 5920eb0
上記は筆者の環境ですが、Docker環境であればOSは問わないはずです。
エディターは、お好みのものを使いましょう。vimでもさくらエディタでもAtomでもVisual Studio Codeでも問題ありません。
手順の説明は、なるべくDockerなどの知識がなくてもコピペすれば動作するように書いてあります。ただ、最低限のシェルの知識は必要になります。環境によっては権限設定により一般ユーザーでは動作しないかもしれません。rootになるなり sudo
するなりしましょう。
RubyやRailsなど他のソフトウェアは、全てDockerコンテナ内でインストールするので、ホストPCにインストールする必要はありません。
セットアップ、実行手順
次に、Railsプロジェクトを作成してからジョブを実行するまでの手順を説明します。
Railsプロジェクトを作成
Railsプロジェクトを作成します。ホストPCにRubyがセットアップ済みであれば rails new .
を実行すれば良いですが、ここではrubyコンテナの中でRailsプロジェクトを作成します。
まず、rubyコンテナを起動します。
$ docker run --rm -it -v $(pwd):/var/myapp -w /var/myapp ruby bash
-v
オプションで、カレント・フォルダをコンテナ内の /var/myapp
フォルダに割り当てます。そして -w
オプションで、コンテナ起動直後の作業フォルダを /var/myapp
フォルダに設定しています。
Ruby on Railsをインストールします。
# gem install rails
カレント・フォルダにRailsプロジェクトを作成します。
# rails new .
作成したら、 ls
でファイルを確認してみます。Railsプロジェクトのファイルが作成されていることを確認したら、 Ctrl-d
でコンテナからログアウトします。
Gemfileに sidekiq
を追加
Sidekiqはgemで提供されているので、 Gemfile
ファイルに追加します。
gem 'sidekiq'
また、 Gemfile
ファイルの余計なコメント行を削除します。結果、次のように修正します(バージョンは時期により異なります)。
$ cat Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.1'
gem 'rails', '~> 5.2.0'
gem 'sqlite3'
gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
gem 'bootsnap', '>= 1.1.0', require: false
gem 'sidekiq'
group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
gem 'capybara', '>= 2.15', '< 4.0'
gem 'selenium-webdriver'
gem 'chromedriver-helper'
end
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Dockerコンテナ化
rails
コマンドを使用したり動作確認するため、早々にDockerコンテナ化します。
簡単にSidekiqを使用したコンテナ構造を説明すると、次のように構成します。ちなみに、今回はRailsアプリケーションが使用するDBとしてSQLiteを指定しているため、DBコンテナは構成しません。
まず、アプリケーション用コンテナの Dockerfile
ファイルを作成します。
$ cat Dockerfile
FROM ruby:2.5
RUN apt-get update && \
apt-get install -y \
nodejs && \
apt-get clean
VOLUME /var/myapp
WORKDIR /var/myapp
COPY Gemfile .
COPY Gemfile.lock .
RUN bundle install
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
次に、上図で説明したコンテナ構造を構成するため、 docker-compose.yml
ファイルを作成します。
$ cat docker-compose.yml
version: '3'
services:
app:
build: .
environment:
- "RAILS_ENV=development"
- "REDIS_URL=redis://redis:6379"
volumes:
- ".:/var/myapp"
ports:
- "3000:3000"
depends_on:
- "redis"
worker:
build: .
environment:
- "RAILS_ENV=development"
- "REDIS_URL=redis://redis:6379"
volumes:
- ".:/var/myapp"
depends_on:
- "redis"
command: sidekiq
redis:
image: redis:4.0
volumes:
- "redis:/data"
command: redis-server --appendonly yes
volumes:
redis:
driver: local
手順が間違えていなければ、イメージをビルドして、Railsアプリケーションを起動することができます。試しに、動作確認してみます。
イメージをビルドします。
$ docker-compose build
コンテナを起動します。
$ docker-compose up -d
Webブラウザで http://localhost:3000 を開くか curl
コマンドでGETリクエストすると、Welcomeページが表示されます。
ダッシュボードへのルートを設定
Sidekiqはジョブがどのような状態かを確認するためのダッシュボードを提供しています。ダッシュボードにアクセスするには、 config/routes.rb
ファイルにルートを設定します。
sidekiq/web
をrequireします。
require 'sidekiq/web'
ダッシュボードへのルートを設定します。次のように設定すると、 http://localhost:3000/sidekiq でダッシュボードにアクセスできます。
mount Sidekiq::Web, at: '/sidekiq'
結果、 config/routes.rb
ファイルは次のようになります。
$ cat config/routes.rb
require 'sidekiq/web'
Rails.application.routes.draw do
mount Sidekiq::Web, at: '/sidekiq'
end
ワーカーを作成
Sidekiqで実行するワーカーを作成します。
rails g
コマンドで、Helloワーカーを作成します。
$ docker-compose exec app rails g sidekiq:worker Hello
これにより、ワーカーとそのテスト・コードが作成されます。今回はワーカーのみ実装します。 app/worker/hello_worker.rb
ファイルに puts "hello"
コードを追加します。結果、次のように修正します。
$ cat app/workers/hello_worker.rb
class HelloWorker
include Sidekiq::Worker
def perform(*args)
puts "hello"
end
end
ワーカーを実行
いよいよ、作成したワーカーをSidekiqで実行してみます。
Sidekiqは変更したソースコードを自動では読み込んでくれないので、いったんコンテナを終了します。
$ docker-compose down -v
コンテナを起動します。
$ docker-compose up -d
appコンテナのRailsコンソールを起動します。
$ docker-compose exec app rails c
ワーカー・クラスの perform_async
メソッドを呼び出すことで、ワーカーをキューに登録します。
> HelloWorker.perform_async
正常に登録された場合、IDのような文字列が表示されます。エラーとなってしまいスタックトレースが表示された場合、これまでに作成したファイルや手順のどこかが間違えています。
今、キューに登録したワーカーはすぐに実行されます。workerコンテナのログを確認します。
$ docker-compose logs worker
Attaching to sample-sidekiq_worker_1
worker_1 | 2018-04-25T10:30:08.008Z 1 TID-gses4fdh1 INFO: Booting Sidekiq 5.1.3 with redis options {:url=>"redis://redis:6379", :id=>"Sidekiq-server-PID-1"}
worker_1 | 2018-04-25T10:30:08.111Z 1 TID-gses4fdh1 INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029)[x86_64-linux]
worker_1 | 2018-04-25T10:30:08.111Z 1 TID-gses4fdh1 INFO: See LICENSE and the LGPL-3.0 for licensing details.
worker_1 | 2018-04-25T10:30:08.111Z 1 TID-gses4fdh1 INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
worker_1 | 2018-04-25T10:30:08.114Z 1 TID-gses4fdh1 INFO: Starting processing, hit Ctrl-C to stop
worker_1 | 2018-04-25T10:30:25.434Z 1 TID-gsesimoz1 HelloWorker JID-4c48fc655e11bc9ee2cbe82b INFO: start
worker_1 | hello
worker_1 | 2018-04-25T10:30:25.441Z 1 TID-gsesimoz1 HelloWorker JID-4c48fc655e11bc9ee2cbe82b INFO: done: 0.005sec
hello
が出力されており、ワーカーが実行されたことが分かります。
また、 http://localhost:3000/sidekiq にアクセスすると、次のようにダッシュボードが表示されます。
SidekiqをCron化
ここまでの作業で、Sidekiqを使ったジョブの実行ができるようになりました。単純にジョブを実行するだけであれば、ここまでで終わりです。
ここからは、sidekiq-cronを使ってジョブを定期的に実行してみます。
Gemfile
と routes.rb
を修正
sidekiq-cronの設定は簡単で、2ファイルを修正するだけです。
Gemfile
ファイルを修正します。 sidekiq
の後に sidekiq-cron
を追加します。
$ cat Gemfile
...(略)...
gem 'sidekiq'
gem 'sidekiq-cron'
...(略)...
config/routes.rb
ファイルを修正します。 require 'sidekiq/web'
の後に require 'sidekiq/cron/web'
を追加します。
$ cat config/routes.rb
require 'sidekiq/web'
require 'sidekiq/cron/web'
...(略)...
これで、ジョブを定期実行する準備は完了です。ワーカーを変更する必要はありません。
ジョブを定期実行
Dockerコンテナを停止して、起動して、Railsコンソールを起動します。
$ docker-compose down -v
$ docker-compose up -d
$ docker-compose exec app rails c
ジョブを定期実行する場合は、 Sidekiq::Cron::Job.create
を呼び出します。
> Sidekiq::Cron::Job.create name: "Hello Job", cron: "* * * * *", class: "HelloWorker"
2018-04-27T04:26:05.562Z 50 TID-govsxyfv2 INFO: Cron Jobs - add job with name: Hello Job
=> true
これでジョブの定期実行が開始されます。しばらくしてから、workerコンテナのログを確認します。
$ sudo docker-compose logs worker
worker_1 | 2018-04-27T04:27:25.246Z 1 TID-grpm2wxhh HelloWorker JID-f746b9ad3c0740cd2bbee4fb INFO: start
worker_1 | hello
worker_1 | 2018-04-27T04:27:25.267Z 1 TID-grpm2wxhh HelloWorker JID-f746b9ad3c0740cd2bbee4fb INFO: done: 0.02 sec
worker_1 | 2018-04-27T04:28:05.080Z 1 TID-grpm2x371 HelloWorker JID-51fce69dc54f378fad381750 INFO: start
worker_1 | hello
worker_1 | 2018-04-27T04:28:05.081Z 1 TID-grpm2x371 HelloWorker JID-51fce69dc54f378fad381750 INFO: done: 0.001 sec
worker_1 | 2018-04-27T04:29:15.094Z 1 TID-grpm2x0dx HelloWorker JID-f0d933d3cda1de168afe4914 INFO: start
worker_1 | hello
worker_1 | 2018-04-27T04:29:15.096Z 1 TID-grpm2x0dx HelloWorker JID-f0d933d3cda1de168afe4914 INFO: done: 0.002 sec
約1分ごとにジョブが実行されています。
どのようなジョブが定期実行されているかは、Sidekiqダッシュボードで確認できます。Sidekiqダッシュボードのメニューに”Cron”が追加されています。
Cronページを見ると、登録したジョブが表示されます。
おわりに
このように、Railsアプリケーションに簡単に定期実行ジョブを追加することができました。アプリケーション固有の、クリーンアップ処理や単純な集計バッチ処理などであれば、これで十分だと思います。
一方、「ジョブ管理システム」として考えると、いろいろと機能が不足しています。ジョブネット、ジョブごとのワークスペース、ジョブ実行履歴、など。そういった機能が必要な場合は、やはりジョブ管理専用のサービスを使うことになると思います。