This is a quick rundown of how we currently deploy a Phoenix project on AWS. It won’t include any code or screenshots — just a high-level overview.
While we use Distillery to build our release, I will not be covering those details here. I recommend reviewing the documentation for Distillery on Hex.
This is the basic flow of how a merge into
master makes it to
AWS CodePipeline >
AWS CodeBuild >
AWS CodeDeploy >
AWS CodePipeline is triggered when a Pull Request is merged into
master. This is done using GitHub Webhooks setup via CodePipeline. The setup is pretty simple and AWS has some great documentation on getting started.
CodePipeline then hands the code off to CodeBuild to build our release.
We build our release using a custom Docker image on CodeBuild. The default Docker image provided by CodeBuild can be used, but we found that building a custom image was a better fit for our needs. Our custom image is very simple and consists of the following:
- Ubuntu 18.04
- NodeJS 10LTS
The release is then passed back to CodePipeline via the artifact configuration option for CodeBuild.
CodePipeline then triggers a deploy using CodeDeploy.
Note: Your Git history is not available when a build is triggered by CodePipeline.
CodeDeploy is used to push the release generated by CodeBuild in the previous step directly to our EC2 instances. This is done by a CodeDeploy agent running on each of our EC2 instances. The agent polls for available deployments and deploys them automatically if one is available.
All of our instances run a custom AMI that has the CodeDeploy agent pre-installed. We build our Custom AMIs using Packer and Ansible. Our EC2 instances are managed with an Auto Scaling Group using a custom Launch Configuration.
The entire process is simple and fast. It takes about 3-5 minutes to go from merged to fully deployed.
Our goal was to treat our EC2 instances as cattle and not pets. This meant that we needed an automated and secure way of passing production credentials and secrets to our EC2 instances. We didn’t want to store a text file or have environment variables on each EC2 instance. We settled on AWS Secrets Manager instead.
I wrote a custom Distillery Config Provider that fetches credentials from Secrets Manager when the Phoenix release is started. Secrets Manager returns TOML to the custom Config Provider, then the TOML is then parsed, and the configurations are set using the Elixir
I would be happy to write up additional details or answer any questions.