resqueで非同期分散処理を試す
resqueは非同期処理の仕組みを提供するライブラリです。
- Webアプリで、画像変換とかメールの一括送信のような重たい処理を行うときに使ったりします。
- Railsに組み込んで使うこともできる模様。
- 複数ノードでの分散処理にも対応。
- 管理コンソールがついているので、処理ノード(worker)の状態や、積まれた処理の数を簡単に確認できるもポイント。
サンプルのタスクを作って、複数ノードで分散処理するところまで試してみました。
構成
docker上に構築します。用意したコンテナは以下。
- redis
- resqueではバックエンドにredisを使います。
- resque-manager
- resqueの管理コンソール(webアプリ)を動かすコンテナ
- resque-console
- resqueで行わせる処理(job)を置くコンテナです。
- resqueへの処理の登録もこのコンテナから行います。
- resque-worker x2
- 非同期処理を実行するコンテナです。2つ用意します。
図にするとこんな感じ。
- すべてのコンテナからredisにアクセスできるようにします。
- resque-consoleにjobを置き、さらにData Volume Containerにして、resque-workerからもjobを参照できるようにします。
- jobは、タスクの登録時および実行時の両方で必要です。
- workerの実行時に必要なRakefileも、resque-consoleに置いて共有します。
1. redis コンテナを作る
まず、redisコンテナを作ります。
$ mkdir -p ~/work/redis $ vi ~/work/redis/Dockerfile
Dockerfileの中身は以下。
FROM centos:centos7 RUN yum -y update; yum clean all RUN yum -y install epel-release; yum -y install redis; yum clean all EXPOSE 6379 CMD ["redis-server"]
- centos7をベースに使います。
- redisをインストールして、起動します。
Dockerfileを保存したら、Dockere Imageをビルドして実行。
$ sudo docker build --rm -t='redis' redis $ sudo docker run -d --name=redis redis
- -d オプションを指定して、バックエンドで実行します。
- redisへのアクセスはコンテナ内からのみできればOKなので、ホスト側のポートとのマッピングはしていません。
2. resque-manager コンテナを作る
次にresque-manager コンテナを作ります。
$ mkdir -p ~/work/resque-manager $ vi ~/work/resque-manager/Dockerfile
Dockerfileは以下。
FROM centos:centos7 RUN yum -y update; yum clean all RUN yum -y install ruby; yum clean all RUN gem install resque EXPOSE 3000 CMD resque-web -p 3000 -r redis -F
- rubyとresqueのgemをインストールします。
- コンテナ起動時にresqueの管理コンソールを起動するように指定します。
- 「-p 3000」で使用するポートを3000に変更。
- 「-r redis」でredisノードを指定します。
- コンテナ起動時に、「--link redis:redis」を指定することで、resque-managerコンテナの /etc/hosts にredisコンテナのアドレスが追加され接続できるようになる仕組み。(詳しくはこちら)なお、上記の例ではportは省略しています。
- 「-F」でフォアグラウンドで実行するようにします。
ビルドして実行。
$ sudo docker build --rm -t='resque-manager' resque-manager $ sudo docker run -d -p 3000:3000 --link redis:redis --name=resque-manager resque-manager
- 「-p 3000:3000」で、ホストのポート3000へのアクセスをゲストのポート3000にマッピングするようにしています。
起動したら、ブラウザから http://127.0.0.1:3000 にアクセス。resqueの管理コンソールが表示されるはず。
3. resque-console コンテナを作る
管理コンソールの起動が確認できたら、resque-console コンテナを作ります。
$ mkdir -p ~/work/resque-console $ vi ~/work/resque-console/Dockerfile
Dockerfile:
FROM centos:centos7 RUN yum -y update; yum clean all RUN yum -y install ruby; yum clean all RUN gem install resque VOLUME ["/resque-data"] CMD ["/bin/bash"]
- jobの登録時に必要になので、rubyとresqueをインストールします。
- 「VOLUME ["/resque-data"]」で data volume を追加。
- ここにjobの定義ファイルやwoker起動用Rakefileを置いて、他のコンテナと共有します。
ビルドして実行。
$ sudo docker build --rm -t='resque-console' resque-console $ sudo docker run -ti --link redis:redis --name=resque-console resque-console
起動後のコンソールから、jobファイルを作成します。
# cd /resque-data # vi job.rb
job.rb:
class Job @queue = :default def self.perform(time) puts "start #{time}" sleep time puts "end #{time}" end end
- 非同期で行う処理の定義です。
- perform メソッドが処理の本体になります。
- @queue でキューの名前を指定します。
jobが用意できたので、resqueに登録してみます。
add_jobs.rb:
require 'resque' require_relative 'job' Resque.redis="redis" # 接続先とするredisを指定 10.times {|n| Resque.enqueue( Job, n ) }
実行すると、jobが登録されます。
# ruby add_jobs.rb
workerはまだないので、実行はされません。
4. workerの起動時に使用するRakefileを作る
resque-workerコンテナをつくる前にworker起動用のRakefileを用意しておきます。
- workerはRake Taskの形で提供されているので、Rakefileを作る必要があります。
- といっても、job.rbとresque/tasksをrequireするだけですけど。
- あと、接続先とするredisもこの中で指定します。
- Rakefileはresque-consoleコンテナの /resque-data に置き、resque-worker x2 と共有します。
- アクセスできるようにするための設定は後述。
require_relative 'job' require 'resque/tasks' Resque.redis="redis"
5. resque-worker コンテナを作る
最後にworkerコンテナを作ります。
$ mkdir -p ~/work/resque-worker $ vi ~/work/resque-worker/Dockerfile
Dockerfile:
FROM centos:centos7 RUN yum -y update; yum clean all RUN yum -y install ruby; yum clean all RUN yum -y install rake; yum clean all RUN gem install resque CMD TERM_CHILD=2 QUEUE=default rake -f /resque-data/Rakefile resque:work
- ruby,resque と rake も必要なので追加でインストールします。
- CMDで、起動時にresqueのworkerを起動します。
ビルドして実行。別の名前で2つ実行します。
$ sudo docker build --rm -t='resque-worker' resque-worker $ sudo docker run -d --link redis:redis --volumes-from resque-console --name="resque-worker1" resque-worker $ sudo docker run -d --link redis:redis --volumes-from resque-console --name="resque-worker2" resque-worker
- 「--volumes-from resque-console」でresque-consoleに追加した「/resque-data」にアクセスできるようにしています。
管理コンソールをリロードすると、workerが2つ起動しているはず。
jobの実行も始まります。