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:
--- files: '/usr/local/bin/setup_job': mode: '000755' owner: root group: root content: | #!/bin/bash APP_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir) CRON_SCRIPT=/usr/local/bin/cron_fetch_articles CRON_RUNNER=/etc/cron.d/fetch_blog_articles # Executes every two hours at the 0th minute. # https://crontab.guru/#0_*/2_*_*_* cat << END > $CRON_RUNNER 0 */2 * * * root $CRON_SCRIPT END cat <<\ END > $CRON_SCRIPT #!/bin/bash # Import ENV vars and set Ruby version. source /opt/elasticbeanstalk/support/envvars source /opt/elasticbeanstalk/support/scripts/use-app-ruby.sh # 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' END chmod +x $CRON_SCRIPT commands: 00_remove_old_cron: command: 'rm -f /etc/cron.d/*.bak' 01_remove_old_script: command: 'rm -f /usr/local/bin/*.bak' 02_setup_blog_feed_cron: 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:
- The cron timer (
0 */2 * * * root $CRON_SCRIPT) with trailing newline.
- The actual cron script, written out to
It works. ¯\_(ツ)_/¯