Bootstrap Scripts
The CloudFormation template uses these parameters to customize the application and environment once the machine starts. One of the most important parameters here is “BootstrapScripts.” Each of the scripts listed here is stored in an S3 bucket and run via CloudFormation. By abstracting various installation tasks into different scripts, we can configure an unlimited combinations of machine types.
Security Groups
A final parameter worth mentioning is “SecurityGroups.” We have created a number of groups that correspond to various use cases (such as _Public HTTP which provides access to port 80 for all source IPs). This allows us to maintain control over our resources instead of creating a different group for every instance which can lead to forgotten rules.
IAM Roles
Within our CloudFormation template, we use another AWS invention called IAM roles. IAM roles are an important security safeguard that allow a resource to access other resources within a restricted environment. Our role utilizes the concept of least privilege and only allows access to the resources it needs. AWS takes care of auto-rotating the AWS keys which allow us to avoid hardcoding AWS keys and secrets into the template or our code.
First Boot
When CloudFormation first runs on an instance, it installs the AWS Command Line Tools which are then used to download and run the bootstrap scripts. When that finishes, it checks to see whether application parameters were provided, and if so, downloads the latest application code from the S3 bucket. Another script is responsible for configuring the code and starting the server.
Part II - New Deployments
Jenkins
At this point in the process, we have an easily-configured environment that can begin running an application when it boots. The next step is to allow developers to deploy new versions of an application without having to worry about manually updating all of the running servers. Although OpsWorks accomplished this task, we ultimately settled on the tried-and-tested Jenkins server. On our Jenkins installation, we integrated GitHub with webhooks so that changes to a repository and branch could trigger a new build on Jenkins. On the Jenkins server, we created several managed scripts that allow us to deploy code to single EC2 instances, as well as all instances within a load balanced group.
When Jenkins detects a change on GitHub, it downloads the code to its workspace. After running any needed tests on the code and ensuring the build passes, it then runs a script we wrote that zips the code and uploads it to a provided S3 bucket (the same bucket the CloudFormation template used to download the code). Here is a small snippet of that code:
zip -q -r deployment.zip * -x "\*.git* \*.log";
aws s3 mv "$WORKSPACE/deployment.zip" "s3://bucket/deployment.zip";
Next, Jenkins logs into each server instance running that application, downloads the latest code, unzips it, stops the previously running server, and starts the new version. Here is a snippet of code that helps accomplish that:
INSTANCES=$(aws elb describe-load-balancers --load-balancer-name "$LBNAME" | grep "INSTANCES" | awk {'print $2'});
cd $WORKSPACE;
for i in $INSTANCES;
do
HOSTNAME=$(aws ec2 describe-instances --instance-ids ${i} | grep INSTANCES | awk {'print $15'});
if [[ $HOSTNAME == *ec2* ]]
then
echo "SSH into $HOSTNAME";
ssh -i "pem.pem" "user@$HOSTNAME" <<-EOF
/usr/local/launch.sh;
exit;
EOF
fi
done;
When this script is complete, the latest version of the application will be running on every instance in the group.
Because Jenkins is integrated with GitHub, we can now update applications across any environment just by pushing to the configured branch. Through a third-party plugin, we have also integrated Jenkins with our chat platform, Slack, allowing us to receive notifications when the builds start and either succeed or fail.
This process is one that has been evolving for quite some time. While there are some smaller details I’ve left out, hopefully it has provided some insight into how Aviary now manages the deployment of applications across a varied and complex infrastructure.
Questions? Feel free to contact the post’s author, Matt Fuller, at matt@aviary.com.
Want to get these updates in your inbox? Sign up here!