CronをDocker上で動かす


概要

  • バージョン: Ruby 2.6.5 Rails 5.2.4
  • 開発環境: Docker/docker-compose

実現したいこと

  • cronを使ってRailsのバッチ処理を実現したい
  • Docker環境で動かしたい

BusyBoxの利用がお勧め

元々、Wheneverを使って設定したcronをDocker上で動かそうとしていました。

Docker環境だとcronが動かないようなので、色々な記事を参考にし各種ファイルの設定を行いました。

しかし、cronが動かない。Dockerコンテナに入りエラーログを調べ、対応しても上手くいきませんでした。

ちなみに、エラーログとその対応は以下になります。

log/crontab.log
bundler: failed to load command: bin/rails (bin/rails)
log/crontab_error.log
ActiveRecord::AdapterNotSpecified: 'production' database is not configured. Available: ["default", "development", "test"]

以下の記事を参考にし、
https://www.cotegg.com/blog/?p=1606
schedule.rbに環境変数を渡す記述をしたり、database.ymlにproductionデータベースを構成する記述を追記したりしました。

cronの再起動を行い、再びlogファイルを確認しましたが、再度同じエラーが吐かれてしまいました。

恐らく、環境変数を上手く渡せていなかったり、Docker 上でのcron の実行環境が、うまく開発環境と認識されていないことが原因だったのではないかと思います。

私の場合は、結局Wheneverを使って設定したcronをDocker上で動かすことができませんでした。

そこで、この問題が比較的簡単に解決してくれたのが、
BusyBoxという方法です。

BubyBoxのメリット

以下のDocker上でcronを使用する際の課題を解決し、シンプルに定期実行をしてくれます。

  • cronで実行するプログラムにコンテナに設定した環境変数を渡したい。
    • cronは環境変数が独立している
    • いちいちファイルに書き出し、読み込みが必要
  • ログが標準出力・標準エラー出力されない

BusyBoxのインストール(Debian系)

※AlpineLinuxの場合、crondで使えるようです

apt-get install -y busybox-static

ディレクトリ構成

.
|- Dockerfile
|- crontab
|- docker-compose.yml
|- main.sh

Dockerfile

Dockerfile
FROM ruby:2.6.5

# インストール
RUN apt-get update && apt-get install -y \busybox-static \

# タイムゾーン設定
ENV TZ=Asia/Tokyo

# main.shファイルをコピー
COPY ./main.sh /myapp

CMD ["busybox", "crond", "-l", "8", "-L", "/dev/stderr", "-f"]

crontab

テスト用に1分毎に実行します

* * * * * /app/main.sh

docker-compose.yml

crontabファイルをマウントすることで、外部から実行時間を指定できるようになります。

docker-compose.yml
version: '3'

services:
  busybox:
    build: .
    volumes:
      - ./crontab:/var/spool/cron/crontabs/root

main.sh

権限エラーを防ぐために、ターミナル上で以下を実行します。

chmod +x main.sh

テストとして、Dateを出力します。

main.sh
date

実行

docker-composeで実行する場合

docker-compose build   # Dockerイメージをビルド
docker-compose up -d   # docker-compose.ymlの変更を反映させる
docker-compose logs -f # ログ出力

1分毎に、dateを出力できました!

参考にした記事