LoginSignup
1
1

docker上のrails7でwhenever

Posted at

wheneverをdockerで動かすには

  • rails taskをcronに登録してくれる便利なgem、whenever
  • rails taskはActiverecordを使えて便利
  • dockerは原則1コンテナ-1daemon
  • railsコンテナではpumaでhttpをdaemon化するため、cronをコマンドで常駐できない
  • そのためdockerの起動方法を工夫する

dockerのCMD注意点

  • DockerfileにCMDを複数行記述しても最後の1行しか実行されない
  • docker composeの場合、compose.yamlのcommandの方が優先される

docker上でwheneverを動かす

とりあえずここから

  • dockerでrails7が起動するところまで

動作確認のためのscaffoldを実行

docker compose run --rm --no-deps webapp rails g scaffold croncheck check_at:datetime

migrate

docker compose run --rm webapp rails db:migrate

一旦compose upして確認

docker compose up -d

http://localhost:3000/cronchecks
(確認終わったらcompose down)

rake taskを作成

 DBにチェック時刻を追加するだけの処理

  • batchという名前でrake taskを作成する
docker compose run --rm --no-deps webapp rails g task batch

lib/tasks/batch.rakeができるので編集

lib/tasks/batch.rake
namespace :batch do
    desc "batch:test"
    task :test => :environment do
        Croncheck.new(check_at: Time.now).save
    end
end

動作確認

エラーが出なければOK
DBに書き込むため、--no-depsは付けないでrun

docker compose run --rm webapp rails batch:test

wheneverを追加

railsapp/Gemfileを編集してwheneverを追加(ルートにあるGemfileではない)

railsapp/Gemfile
# cron
gem 'whenever', require: false

require: falseについては以下

webappコンテナのみを起動させ、bundle install

docker compose run --rm --no-deps webapp bundle install

whenever初期化

./config/schedule.rbを作成

docker compose run --rm --no-deps webapp bundle exec wheneverize .
[add] writing `./config/schedule.rb'
[done] wheneverized!

wheneverにrake taskを登録

./config/schedule.rbを編集し、先ほどのrake taskを1分毎に動かす設定を書く。

./config/schedule.rb
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron

# Example:
#
# set :output, "/path/to/my/cron_log.log"
#
# Rails.rootを使用するために必要
require File.expand_path(File.dirname(__FILE__) + "/environment")
# cronを実行する環境変数(:developmentを初期値とする)
rails_env = ENV['RAILS_ENV'] || :development
# cronを実行する環境変数をセット
set :environment, rails_env
ENV.each { |k,v| env(k,v) } #これが超大事
# cronの標準出力先(必ずしもlogでなくても良い)
set :output, "#{Rails.root}/log/cron.log"

# 1分毎に回す
every 1.minute do
    rake "batch:test" #rakeの場合
end

config/application.rbに追加

config/application.rb
  class Application < Rails::Application
    #wheneverのタスクはlibの中に置く
    config.autoload_paths += Dir["#{config.root}/lib/**/"] #lib配下でディレクトリ分けする場合
    #本番はeagar_load
    config.eager_load_paths += Dir["#{config.root}/lib/**/"]

whenever確認コマンド(cronに登録はされない)

※[message]はこれで正常

docker compose run --rm --no-deps webapp bundle exec whenever
* * * * * /bin/bash -l -c 'cd /railsapp && RAILS_ENV=development bundle exec rake batch:test --silent >> /railsapp/log/cron.log 2>&1'

## [message] Above is your schedule file converted to cron syntax; your crontab file was not updated.
## [message] Run `whenever --help' for more options.

cronに登録

ここからcronに登録するが、いきなりwhenever --update-crontabをするとエラーになる。docker image自体にcronがインストールされていないため。

cronのインストール

Dockefileに以下を追記(RUNのところ)し、buildする。
cronとnanoがインストールされる。
nanoはcrontabの編集に必要。

Dockerfile
RUN apt update -qq \
 && apt install -y build-essential mariadb-client nodejs \
 && apt install -y cron nano \
 && npm install --global yarn

build

docker compose build

cronがインストールされたか確認

docker compose run --rm --no-deps webapp which cron
/usr/sbin/cron

この時点でdocker runしてもcronは起動していない。

docker compose run --rm --no-deps webapp service cron status
cron is not running ... failed!

startup.shを作成

touch startup.sh

startup.shを編集

startup.shの中で

  1. wheneverの設定とcronの起動
  2. ./bin/devの実行(puma httpdの起動)
#!/usr/bin/env bash
cd $APP
bundle exec whenever --update-crontab
service cron start
rm -f tmp/pids/server.pid 
./bin/dev

Dockerfileでentrypoint.shを起動

最終的なDockerfile

Dockerfile
FROM ruby:3.2.0
ENV APP /railsapp
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt update -qq \
 && apt install -y build-essential mariadb-client nodejs \
 && apt install -y cron nano \
 && npm install --global yarn
RUN yarn add @fortawesome/fontawesome-free @fortawesome/fontawesome-svg-core @fortawesome/free-brands-svg-icons @fortawesome/free-regular-svg-icons @fortawesome/free-solid-svg-icons
WORKDIR $APP
COPY Gemfile $APP/Gemfile
COPY Gemfile.lock $APP/Gemfile.lock
RUN bundle install
ADD startup.sh /
RUN chmod +x /startup.sh
CMD ["/startup.sh"]

compose.yamlのcommandをコメントアウトする

DockerfileのCMDよりもcompose.yamlのcommandが優先されるため

compose.yaml
    #command: bash -c "rm -f tmp/pids/server.pid && ./bin/dev"

再ビルド

docker compose build

晴れてdocker compose up

docker compose up -d

確認

  • crontabがserviceで動いているか
    cron is runnning.が返ってくればOK。
  • cron is not running ... failed!だと起動に失敗している
docker compose exec webapp service cron status
cron is running.
  • whenever --update-crontabが動いているか
docker compose exec webapp crontab -l
#環境変数がずらずらっと
* * * * * /bin/bash -l -c 'cd /railsapp && RAILS_ENV=development bundle exec rake batch:test --silent >> /railsapp/log/cron.log 2>&1'

# End Whenever generated tasks for: /railsapp/config/schedule.rb at: 2024-02-14 22:21:19 +0900

確認がOKだと

http://localhost:3000/cronchecks
を1分ごとにリロードすると、登録されたデータが増えていることでwheneverが動いていることが確認できる。

おまけ

  • cronから削除
    ※当たり前だけどバッチ処理はrails webサーバとは関係なく動くので
    開発中に止めたい場合は以下のコマンドでcronから削除する
docker compose run --rm --no-deps webapp bundle exec whenever --clear-crontab

参考

  • CMDとentrypoint

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