docker-compose.ymlでcronを実行したい

投稿者: Anonymous

Railsのwheneverを利用して、docker-compose.ymlのcommandでcronを実行後、ジョブの登録をしたいのですが、ジョブ登録コマンドの動作がうまくいかない状況です。

command: >
      bash -c "
      cron -f; # cron起動
      bundle exec whenever --update-crontab; # ジョブ登録
      "

docker-compose up時に、cron起動コマンドからジョブ登録コマンドまで一通りは実行されるものの、
docker-compose exec railsコンテナのサービス名 bashでbashに入り、登録されたジョブを確認(crontab -l)すると、何も登録されていないと表示されてしまいます。

no crontab for root

ただbashに入ってから、ジョブ登録コマンドを実行すると正常に登録されるのが不思議でわかりません。
一般的にDocker上でcronを扱うのは結構ネックのようですが、このやり方でもできないのでしょうか?

ご教示いただけると助かります。

Debian環境ですので、手動で行う分にはスケジューラー自体の動作は正常であることを確認しました。

ソースコード

docker-compose.yml

version: '3'

services:
  ruby_base:
    build: .
    tty: true
    stdin_open: true
    volumes:
      - .:/var/www/
    working_dir: /var/www/rails_api
    command: >
      bash -c "
      cron -f;
      bundle exec whenever --update-crontab
      "

Dockerfile

FROM ruby:2.7.1

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo
ENV LC_ALL=C

# 必要なパッケージ & スケジューラー & Railsインストール
RUN apt-get update -qq
RUN apt-get install -y build-essential cron
RUN gem install -v 5.2.4 rails --no-document

# プロジェクトディレクトリ名を変数化
ARG project_name="rails_api"

# Gemfileのインストール
WORKDIR /var/www/${project_name}
COPY /${project_name}/Gemfile Gemfile
COPY /${project_name}/Gemfile.lock Gemfile.lock
RUN bundle install

追記 (himenonさん) 2020/06/03

debian:busterをimageにして、railsとbusyboxを1つのコンテナとして起動しています。

main.sh

#!/bin/sh
# 初期のディレクトリ場所がrootディレクトリにいるため、cdでrailsのディレクトリまで移動しています
cd /var/www/プロジェクト名/rails_api;
bundle exec rake testtask:タスク1

解決

質問に対する直接的な回答にはなりませんが、cronをdockerで利用するときにハマりがちなので別のやり方を紹介します。

dockerでcronを利用する場合、いろいろ設定をする必要がありますが、
busyboxに含まれるcrondを利用すると比較的簡単にcronジョブを仕込むことができます。

この最小サンプル紹介します。

ディレクトリ構成

.
├── Dockerfile
├── crontab
├── docker-compose.yml
└── main.sh

Dockerfile

  • ruby:2.7.1のBase Imageをたどるとdebian:busterにたどり着くので、これをベースファイルとしてDockerfileを組み立てます。
  • busybox crond --helpを実行するとオプションが確認できます。適宜指定してください。
FROM debian:buster
ENV TZ Asia/Tokyo
RUN apt-get update && apt-get install -y busybox-static

WORKDIR /app

COPY ./main.sh /app/

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

crontab

デバッグ用に1分毎に実行します

* * * * * /app/main.sh

docker-compose.yml

  • crontabファイルをマウントすることで外部から実行時間を指定できるようにしています。
  • Dockerfile内でCMDを指定しているので、もしdocker-compose.ymlでcommand指定したい場合はDockerfileを編集してください。
version: '3'

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

main.sh

  • chmod +x main.shを実行済み
  • 質問にあるコマンドを例えばこのファイルに記述することも可能です。
  • この例では日付を出力します。
#!/bin/sh
date

実行

docker-composeを利用する場合

docker-compose build   # Docker Image絵をビルド
docker-compose up -d   # docker-composeをdaemon化
docker-compose logs -f # ログ出力

dockerコマンドで実行する場合

docker build -t my-busybox:0.0.1 .
docker run -d --rm -v $(pwd)/crontab:/var/spool/cron/crontabs/root my-busybox:0.0.1
docker logs -f [container id]

参考

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *