Elastic Beanstalk on Amazon Linux 2
Run command or script after deployment
Aug 5, 2021
I have written about this or similar topics before in both Post-Deployment script on Elastic Beanstalk: Restart Delayed Job as well as Convenient way to run app commands in Elastic Beanstalk.
But at the time it applied to the first generation of Amazon Linux image. Nowadays I believe that image has been deprecated and new applications will use the Amazon Linux 2 image when deploying on Elastic Beanstalk and there are some breaking differences when it comes to configuring deployments.
New folder structure for hooks
The first thing you'll notice if you try any of the previous guides is that your hooks are not running. The reason is that /opt/elasticbeanstalk/hooks
is no longer the correct place for hooks to live. In fact, that folder does not even exist anymore (unless created by your own ebextensions).
The good news is that hooks have now become a configurable platform extension (with proper documentation from AWS).
To summarise, you can commit shell scripts in your application source code under .platform/hooks/[hook-folder]/[shell-script]
and they will execute during the deploy lifecycle.
To quote the documentation, the currently available hook folders are
prebuild
– Files here run after the Elastic Beanstalk platform engine downloads and extracts the application source bundle, and before it sets up and configures the application and web server. Theprebuild
files run after running commands found in the commands section of any configuration file and before runningBuildfile
commands.predeploy
– Files here run after the Elastic Beanstalk platform engine sets up and configures the application and web server, and before it deploys them to their final runtime location. Thepredeploy
files run after running commands found in the container_commands section of any configuration file and before runningProcfile
commands.postdeploy
– Files here run after the Elastic Beanstalk platform engine deploys the application and proxy server. This is the last deployment workflow step.
In my opinion, this is a big improvement and I feel confident that even if future platform upgrades will change folder locations in the filesystem, any properly configured platform extension should be adapted and work fine.
Environment Variables
The next thing you will likely run into is that even when your shell scripts are running, they don't have access to any environment variables you might have configured in your application.
This is not something new to Amazon Linux 2, the same was true for the previous Amazon Linux AMI. However, the previous one did have a pre-generated shell script in the support folder called envvars
which basically was a prepared script with export statements for all the environment variables. In the new version, this file no longer exists.
Unlike the hooks scenario, I have not been able to find any official article from https://docs.aws.amazon.com/. I did however find an article from their Premium Support knowledge center.
How do I use environment variables from an Elastic Beanstalk instance shell?
So while not an actual platform feature, I still consider this to be an officially endorsed solution.
This solution includes adding an .ebextension
to your application that looks like this.
commands:
setvars:
command: /opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""' > /etc/profile.d/sh.local
packages:
yum:
jq: []
What happens here is a command that is run on deploy that first uses the get-config utlity to get a list of all the environment variables in JSON format.
It then pipes that output to the jq executable. This is a command-line package that can parse JSON. This particular command converts the JSON format into an export syntax for the shell.
Lastly the output from jq
is written to a file in the folder /etc/profile.d/
. If you are unfamiliar with this folder, just know that any script placed here is run when a new shell is started.
The end result is that our hooks can now have access to any environment variables configured in the application.
Example Script
To put this in use with a scenario I have used before, here is an example of a postdeploy
hook that will restart DelayedJob
in a Rails application. Make sure the above .ebextension
is implemented, then add the following file in your application bundle under .platform/hooks/postdeploy/01_restart_delayed_job.sh
#!/usr/bin/env bash
# Loading environment data
EB_APP_USER=$(/opt/elasticbeanstalk/bin/get-config platformconfig -k AppUser)
EB_APP_CURRENT_DIR=$(/opt/elasticbeanstalk/bin/get-config platformconfig -k AppDeployDir)
EB_APP_PIDS_DIR=/var/pids
# Now we can execute the command. The -l flag sources makes sure to source everything
# in /etc/profile.d/
su -l ${EB_APP_USER} -c "cd $EB_APP_CURRENT_DIR; bundle exec bin/delayed_job --pid-dir=$EB_APP_PIDS_DIR restart"
That's it for now
Thank you for reading and happy coding!