Search code examples
ruby-on-railsdocker-composecronwhenever

Rails Whenever gem not executing repetitive crontab task with Ubuntu and Docker Compose


I'm trying to run a repetitive task using the Whenever gem for my rails app. It is running in a Docker container created using Docker Compose and hosted on an Ubuntu server.

I can successfully create and update the crontab file using the Whenever gem but it doesn't seem to be executing the task.

The task I want to execute repetitively in the background while the app is running is:

rake searchkick:reindex CLASS=Property

I usually execute this command successfully from the terminal after creating a web run container using 'docker-compose run web bash'. This reindexes my ElasticSearch server using the searchkick gem.

The relevant versions are below:

- ruby 2.7.2p137
- rails (6.0.3.6)
- elasticsearch (6.8.3)
  - elasticsearch-api (= 6.8.3)
  - elasticsearch-transport (= 6.8.3)
- searchkick (4.4.4)
  - activemodel (>= 5)
  - elasticsearch (>= 6)
  - hashie
- whenever (1.0.0)
  - chronic (>= 0.6.3)

Cron is installed using the Dockerfile:

RUN apt-get update -qq && \
    apt-get install -y curl \
    build-essential \
    libpq-dev \
    cron \
    vim \
    nano \
    postgresql \
    postgresql-contrib \
    postgresql-client

The Dockerfile runs a script when the container is created:

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

The script (entrypoint.sh) creates a crontab file.

#!/bin/bash
set -e

# For development check if the gems as installed, if not, then uninstall them.
if ! [ bundle check ]; then
    bundle install
fi

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Yarn - Check Files.
yarn install --check-files

# Create empty crontab file.
crontab -l | { cat; echo ""; } | crontab -

# Update crontab file using whenever command.
bundle exec whenever --set 'environment=production' --update-crontab

# Run the command - runs any arguments passed into this entrypoint file.
exec "$@"

The 'bundle exec whenever --set 'environment=production' --update-crontab' command above updates the crontab file as per the schedule.rb file below:

set :output, "log/cron_log.log"
env :PATH, ENV['PATH']

# Reindex Property records every 3 mins.
every 3.minutes do
    rake "searchkick:reindex CLASS=Property"
end

I've also tried using this:

every 3.minutes do
    command "cd /myapp && rake searchkick:reindex CLASS=Property"
end

When I execute 'docker-compose run web bash' to use the rails terminal, I see this response at the end:

no crontab for root
[write] crontab file updated

To check that it was created correctly, I view the crontab file in the rails app with 'crontab -l' and it shows:

# Begin Whenever generated tasks for: /myapp/config/schedule.rb at: 2021-04-24 13:07:12 +0000
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57 * * * * /bin/bash -l -c 'cd /myapp && RAILS_ENV=production bundle exec rake searchkick:reindex CLASS=Property --silent >> log/cron_log.log 2>&1'

# End Whenever generated tasks for: /myapp/config/schedule.rb at: 2021-04-24 13:07:12 +0000

Update: After using cron service status and cron service start, the logs in myApp/log/cron_log.log show the following:

bundler: failed to load command: rake (/usr/local/bin/rake)
Bundler::GemNotFound: Could not find rake-13.0.3 in any of the sources
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler.rb:149:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'
bundler: failed to load command: rake (/usr/local/bin/rake)
Bundler::GemNotFound: Could not find rake-13.0.3 in any of the sources
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler.rb:149:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'

So it seems to have set up the crontab file and the cronjob is trying to run but it is not finding rake-13.0.3. I'm going to check if this is an issue with the bundler gem version versus what is in the gemfile.

Appreciate any help.


Solution

    1. you can run cron to start cron service (linux) in your entrypoint.sh
    # Update crontab file using whenever command.
    cron && bundle exec whenever --set 'environment=production' --update-crontab
    
    1. schedule don't know the gems path so the Bundler::GemNotFound error be throw, to solve this, you can avoid missing paths by setting all ENV in schedule.rb
    set :output, "log/cron_log.log"
    ENV.each { |k, v| env(k, v) }
    # ...