ruby-on-railsdockerwhenever

Unable to Run Cron Job using Whenever Gem Within Docker Container


I am running a Rails 7 application within a Docker container and wish to run cron jobs using the whenever gem. Within the Docker container I can manually call the cron job via whenever

/bin/bash -l -c 'cd /usr/src/app && bundle exec bin/rails runner -e development "Cals1::Sync.start"'

and this works. But the job doesn't run through the Rails application.

Cron displays a message in the error log of

bundler: failed to load command: bin/rails (bin/rails)
/usr/local/lib/ruby/3.2.0/bundler/definition.rb:524:in `materialize': Could not find rails-7.0.5, sprockets-rails-3.4.2, mysql2-0.5.3, passenger-6.0.18, jsbundling-rails-1.1.2, turbo-rails-1.4.0, stimulus-rails-1.2.1, cssbundling-rails-1.2.0, jbuilder-2.11.5, redis-4.8.1, bootsnap-1.16.0, chamber-2.14.3, exception_notification-4.5.0, faraday-2.7.7, whenever-1.0.0, debug-1.8.0, rspec-rails-6.0.3, web-console-4.2.0, capistrano-3.17.3, capistrano-rails-1.6.2, capistrano-rbenv-2.2.0, capistrano-file-permissions-1.0.0, capybara-3.39.2, selenium-webdriver-4.10.0, webdrivers-5.2.0, shoulda-matchers-5.3.0, actioncable-7.0.5, actionmailbox-7.0.5, actionmailer-7.0.5, actionpack-7.0.5, actiontext-7.0.5, actionview-7.0.5, activejob-7.0.5, activemodel-7.0.5, activerecord-7.0.5, activestorage-7.0.5, activesupport-7.0.5, railties-7.0.5, sprockets-4.2.0, rack-2.2.7, msgpack-1.7.1, hashie-3.6.0, thor-1.2.2, faraday-net_http-3.0.2, chronic-0.10.2, irb-1.7.0, reline-0.3.5, rspec-core-3.12.2, rspec-expectations-3.12.3, rspec-mocks-3.12.5, rspec-support-3.12.0, bindex-0.8.1, airbrussh-1.4.1, i18n-1.14.1, sshkit-1.21.4, capistrano-bundler-2.1.0, addressable-2.8.4, mini_mime-1.1.2, nokogiri-1.15.2-x86_64-linux, rack-test-2.1.0, regexp_parser-2.8.1, xpath-3.2.0, rubyzip-2.3.2, websocket-1.2.9, nio4r-2.5.9, websocket-driver-0.7.5, mail-2.8.1, net-imap-0.3.6, rails-dom-testing-2.0.3, rails-html-sanitizer-1.6.0, globalid-1.1.0, builder-3.2.4, erubi-1.12.0, marcel-1.0.2, concurrent-ruby-1.2.2, minitest-5.18.1, tzinfo-2.0.6, method_source-1.0.0, zeitwerk-2.6.8, diff-lcs-1.5.0, net-scp-4.0.0, net-ssh-7.1.0, public_suffix-5.0.1, racc-1.7.1, websocket-extensions-0.1.5, loofah-2.21.3, timeout-0.4.0, crass-1.0.6 in locally installed gems (Bundler::GemNotFound)
    from /usr/local/lib/ruby/3.2.0/bundler/definition.rb:197:in `specs'
    from /usr/local/lib/ruby/3.2.0/bundler/definition.rb:254:in `specs_for'
    from /usr/local/lib/ruby/3.2.0/bundler/runtime.rb:18:in `setup'
    from /usr/local/lib/ruby/3.2.0/bundler.rb:171:in `setup'
    from /usr/local/lib/ruby/3.2.0/bundler/setup.rb:23:in `block in <top (required)>'
    from /usr/local/lib/ruby/3.2.0/bundler/ui/shell.rb:159:in `with_level'
    from /usr/local/lib/ruby/3.2.0/bundler/ui/shell.rb:111:in `silence'
    from /usr/local/lib/ruby/3.2.0/bundler/setup.rb:23:in `<top (required)>'
    from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:56:in `require_relative'
    from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:56:in `kernel_load'
    from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:23:in `run'
    from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:492:in `exec'
    from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
    from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
    from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
    from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:34:in `dispatch'
    from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
    from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:28:in `start'
    from /usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.10/libexec/bundle:45:in `block in <top (required)>'
    from /usr/local/lib/ruby/3.2.0/bundler/friendly_errors.rb:117:in `with_friendly_errors'
    from /usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.10/libexec/bundle:33:in `<top (required)>'
    from /usr/local/bin/bundle:25:in `load'
    from /usr/local/bin/bundle:25:in `<main>'

Also, when I have the cron job output the $PATH it is missing /usr/local/bundle/bin. I have tried setting and appending to PATH within the schedule.rb file but the cron job outputs the below for $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

So my schedule.rb file is

set :output, '/var/log/cron.log' # log location

every 1.minute do
  command "echo $PATH"
  runner "Cals1::Sync.start", :environment => @environment
end

And my Dockerfile is

FROM ruby:3.2.2

# Download latest package information and install packages.
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - && \
    apt-get update -y && apt-get --force-yes install -y --no-install-recommends  \
    build-essential \
    vim \
    curl \
    less \
    libmariadb-dev \
    git \
    cron \
    nodejs && \
    rm -rf /var/lib/apt/lists/*

# Install Yarn globally
RUN npm install --global yarn

# Create and define the node_modules's cache directory.
RUN mkdir /usr/src/cache
WORKDIR /usr/src/cache

# Install the application's dependencies into the node_modules's cache directory.
COPY package.json ./
COPY package-lock.json ./
RUN npm install

# Make this the current working directory for the image. So we can execute Rails \
# cmds against image.
RUN mkdir -p /usr/src/app

COPY Gemfile* /usr/src/app/

# CD or change into the working directory.
WORKDIR /usr/src/app

# Set timezone
ENV TZ=America/Chicago

RUN bundle install

# ADD/COPY app files from local directory into container so they are baked into the image.
# The source path on our local machine is always relative to where the Dockerfile is located.
ADD . /usr/src/app

# Testing if cron will output to a log.
RUN touch /var/log/cron.log
# Create empty crontab file
RUN crontab -l | { cat; echo ""; } | crontab -
# Update crontab file using whenever command
RUN bundle exec whenever --set "environment=development" --update-crontab

# Add a script to be executed every time the container starts.
# Entrypoint files are used to set up or configure a container at runtime.
# Below file needs to be executable: $ sudo chmod +x docker_entrypoint_staging.sh
ENTRYPOINT ["./entrypoints/docker_entrypoint_dev.sh"]

Any help would be much appreciated.

Thank you.


Solution

  • I ended up making a HTTP request to application via curl from within the cron job. And whitelisting the incoming HTTP request. The controller#action is what called the Model method. No matter what I tried, trying to have whenever's runner call the Model method within Docker container would not work for me.