The following shares some insights and workarounds for deploying a Rails 6 application with Webpacker to AWS Beanstalk, which hopefully helps others avoid some of the headaches I experienced.
In the past I often deployed my Rails applications to Heroku with little to no headaches, it’s like a perfect marriage of software and hardware.
However for a recent project I wanted to deploy to AWS Beanstalk since we were already running other applications inside of Amazon’s infrastructure. Unfortunately deploying to Beanstalk is not nearly as painless as deploying to Heroku.
As many others have pointed out, you’ll likely run into lots of issues related to running Bundle, Yarn, Webpacker, etc.
Thankfully Austin G. Walters’ article pointed me in the right direction to utilize .ebextenstions to manually execute code during the deployment process. Also some digging through AWS forums, Stackoverflow posts, and other articles helped resolve my issues.
I’d recommending reading Amazon’s documentation on .ebextensions, it will help you better understand the format and options. In my case I created the following files in my Rails project.
.ebextensions/00_bundler_install.config
This helps fix an issue with not having the right version of Bundler installed on Beanstalk. Note, you’ll need to change the -v 2.1.4 to correct version from your Gemfile.lock file.
files:
"/opt/elasticbeanstalk/hooks/appdeploy/pre/09_gem_install_bundler.sh" :
mode: "000775"
owner: root
group: users
content: |
#!/usr/bin/env bashEB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_staging_dir)
EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir)
. $EB_SCRIPT_DIR/use-app-ruby.shcd $EB_APP_STAGING_DIR
echo "Installing bundler..."
gem install bundler -v 2.1.4
.ebextensions/01_rails_setup.config
This does most of the magic, like setting the RAILS_SKIP_MIGRATIONS and RAILS_SKIP_ASSET_COMPILATION to prevent Beanstalk from running migrations and compiling assets since we’ll be running that ourselves. It also installs some packages like git and curl to the environment, downloads and installs Yarn, and finally compiles assets and runs migrations.
option_settings:
aws:elasticbeanstalk:command:
Timeout: 2000
aws:elasticbeanstalk:application:environment:
BUNDLE_PATH: "vendor/bundle"
BUNDLE_WITHOUT: "test:development"
BUNDLE_DISABLE_SHARED_GEMS: 1
RAILS_SKIP_MIGRATIONS: true
RAILS_SKIP_ASSET_COMPILATION: true
LOGGING: debug
packages:
yum:
git: []
curl: []
wget: []
openssl-devel: []
postgresql94-devel: []
commands:
01_node_download:
cwd: /tmp
command: "curl --silent --location https://rpm.nodesource.com/setup_10.x | sudo bash -"
ignoreErrors: true
02_node_install:
cwd: /tmp
command: "sudo yum install nodejs -y"
ignoreErrors: true
03_yarn_download:
cwd: /tmp
test: '[ ! -f /usr/bin/yarn ] && echo "yarn not installed"'
command: "sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo"
ignoreErrors: true
04_yarn_install:
cwd: /tmp
test: '[ ! -f /usr/bin/yarn ] && echo "yarn not installed"'
command: "sudo yum install yarn -y"
ignoreErrors: true
05_mkdir_webapp_dir:
test: '[ ! -p /home/webapp ] && echo "webapp does not exist"'
command: "sudo mkdir -p /home/webapp"
ignoreErrors: true
06_chown_webapp_dir:
test: '[ ! -p /home/webapp ] && echo "webapp does not exist"'
command: "sudo chown webapp:webapp /home/webapp"
ignoreErrors: true
07_chmod_webapp_dir:
test: '[ ! -p /home/webapp ] && echo "webapp does not exist"'
command: "sudo chmod 0744 /home/webapp"
ignoreErrors: true
08_mkdir_logs:
command: "sudo mkdir -p /var/app/current/log/"
09_chmod_logs:
command: "sudo chown webapp:webapp -R /var/app/current/log/"
ignoreErrors: true
10_chmod_log_dir:
command: "sudo chmod 0664 -R /var/app/current/log/"
ignoreErrors: true
11_chown_current:
command: "sudo chown webapp:webapp -R /var/app/current/"
ignoreErrors: true
12_chmod_current:
command: "sudo chmod 0755 -R /var/app/current/"
ignoreErrors: true
13_chown_current:
command: "sudo chown webapp:webapp -R /var/app/ondeck/"
ignoreErrors: true
14_chown_current:
command: "sudo chmod 0644 -R /var/app/ondeck/"
ignoreErrors: truecontainer_commands:
15_config_nokogiri:
command: "bundle config build.nokogiri --use-system-libraries"
16_bundle_install:
command: "bundle install"
17_yarn_install:
command: "yarn install --check-files --force --production"
18_asset_precompile:
command: "NODE_ENV=production bundle exec rails assets:precompile --trace"
19_webpacker_compile:
command: "NODE_ENV=production bundle exec rails webpacker:compile --trace"
20_db_migrate:
command: "bundle exec rails db:migrate"
.ebextensions/02_puma_settings.config
This sets up the configuration for Puma, although the below is the default from Beanstalk, I created it so I can easily customize the options later.
files:
"/opt/elasticbeanstalk/support/conf/pumaconf.rb":
mode: "000644"
content: |
directory '/var/app/current'
threads 8, 32
workers %x(grep -c processor /proc/cpuinfo)
bind 'unix:///var/run/puma/my_app.sock'
pidfile '/var/run/puma/puma.pid'
stdout_redirect '/var/log/puma/puma.log', '/var/log/puma/puma.log', true
daemonize false
.ebextensions/03_rails_console.config
Since Beanstalk doesn’t have a handy helper like heroku run rails console, I added this so I can access the Rails console using ~/rails:console.
files:
"/home/ec2-user/rails:console":
mode: "000777"
owner: root
group: root
content: |
sudo su - -c 'cd /var/app/current; bundle exec rails console'
.ebextensions/04_db_console.config
Similar to the previous extension I can now access the database console using ~/db:console.
files:
"/home/ec2-user/db:console":
mode: "000777"
owner: root
group: root
content: |
sudo su - -c 'cd /var/app/current; bundle exec rails db'
So now with these .ebextensions in place, I was now able to successfully deploy my Rails 6 application to Beanstalk using:
eb deploy my-beanstalk-app-env --timeout 40
Note, I added the timeout option since the default time wasn’t long enough to execute all the commands during deployment.
Webpacker Compile Error
So I was now able to successfully deploy to our Prod environment, however the deployment was failing to the Test environment. Looking at the Beanstalk logs showed that webpacker:compile was failing:
** Execute webpacker:compile
Compiling...
Compilation failed:
/var/app/ondeck/node_modules/rails-erb-loader/erb_transformer.rb:19:in `write': Broken pipe @ io_writev - <STDOUT> (Errno::EPIPE)
from /var/app/ondeck/node_modules/rails-erb-loader/erb_transformer.rb:19:in `puts'
from /var/app/ondeck/node_modules/rails-erb-loader/erb_transformer.rb:19:in `puts'
from /var/app/ondeck/node_modules/rails-erb-loader/erb_transformer.rb:19:in `<main>'
This was confusing since it was executing the same .ebextensions for the same source code. Comparing the Prod vs Test environments, everything appeared the same except Prod was running on the instance type t2.medium, while Test was running on t2.micro. And there was the problem!
The Beanstalk instance must of been running out of memory causing the Broken pipe error. Once I upgraded Test to also run on t2.medium, I was able to deploy there as well.
Hopefully the above information helps others deploy their Rails 6 applications to Beanstalk. 🚀