Ghetto Single-Instance Cron jobs on Elastic Beanstalk

in code

Before you read this article, go and read about Elastic Beanstalk’s worker environments. A worker environment is a copy of your application which you configure to handle certain background tasks. In the case of Ruby on Rails, you can configure ActiveJob to interact with the worker tier through the active-elastic-job gem.

If a worker tier sounds like too much work, then Elastic Beanstalk also supports cron jobs which are executed on all instances. I understand the intent behind Amazon’s cron setup (perform a task against the instance), but the decision leads to pain. A cron job which executes an ActiveJob tasks will execute the task on all instances, a situation which leads to undesireable race conditions.

Our ghetto solution is to employ leader-only container commands which create the cron files for us:

    mode: '000755'
    owner: root
    group: root
    content: |

      APP_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir)

      # Executes every two hours at the 0th minute.

      cat << END > $CRON_RUNNER
        0 */2 * * * root $CRON_SCRIPT


      cat <<\ END > $CRON_SCRIPT

        # Import ENV vars and set Ruby version.
        source /opt/elasticbeanstalk/support/envvars
        source /opt/elasticbeanstalk/support/scripts/

        # Go to the app dir and execute BlogFeedJob.
        cd $(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir)
        bundle exec bin/rails runner -e production 'RssJob.perform_later'

      chmod +x $CRON_SCRIPT
    command: 'rm -f /etc/cron.d/*.bak'
    command: 'rm -f /usr/local/bin/*.bak'
    leader_only: true
    command: '/usr/local/bin/setup_job'

So, the file setup_blog_feed_cron is written out on all instances; the command (02_setup_blog_feed_cron) which turn it into a cron only executes on the leader. The script writes out two further files using HEREDOC blocks:

  1. The cron timer (0 */2 * * * root $CRON_SCRIPT) with trailing newline.
  2. The actual cron script, written out to /usr/local/bin.

It works. ¯\_(ツ)_/¯

March 20

in me

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