Today we’re starting on a series of posts that delves into everyone’s favorite topic… DevOps 😱! Our goal will be to deploy a simple Phoenix application to AWS. We’ll be using the new Elixir 1.9 release task, along with Docker, and Terraform. This will be a two or three part series; today we’ll concentrate on creating a release and building a docker image. In subsequent posts we’ll deploy the docker image to AWS.
I find one of the challenges with DevOps is all the moving parts. Building the code; containerization; provisioning infrasture; continuous integration… it’s a lot! In this series we’re going to ignore the CI component altogether, but hopefully provide a decent jumping off point for everything else.
Let’s get at it! The first step is to create a simple application.
What we’ll build
Our application will be a trivial “warehouse” application that just contains a list of products.
Create the app
We’ll start from scratch with a new Phoenix application and add some scaffolding for the CRUD functionality.
Y when asked to fetch and install dependencies, then change into the application directory and create the database.
Our basic application is good to go, now let’s add the scaffolding.
Adding some scaffolding
As per the
mix output, let’s update the routes…
/lib/aws_warehouse_web/router.ex …line 16
… and run the migrations.
Our application is complete, let’s have a quick look:
Navigating to http://localhost:4000/products we can see the products page and we are able to create a few products…
Nothing impressive, but it will be sufficient for our purposes. The next step is to create the release.
Create the release
We will be leaning heavily on this excellent article (https://akoutmos.com/post/multipart-docker-and-elixir-1.9-releases/) by Alex Koutmos to guide us through the creation of a release and a docker image. I’d highly recommend you give it a read. It explains the process much better than I could, and since there is already a great resource explaining releases / docker images, I won’t go into an in depth discussion here. I will point out where we deviate from Alex’s article, the main difference being we are creating an application with a database.
Our first step is to create the release files and configuration.
The main point of the release configuration is to specify items we want set at run time. Typically these are environment specific items. For instance, likely a different database user and password would be used on a QA versus a Production environment. Likewise, our
DB_INSTANCE values are all things that will be environment specific.
There are a few more configuration items to take care of.
We can get rid of
config/prod.secret.exs as the
secrets.exs items have all been included in the
releases.exs configuration we created.
We need to uncomment the
server: true line in
/config/prod.exs …line 58
And we also need to get rid of the
prod.secret import on line 65 as we no longer have a
/config/prod.exs …line 65
With that, we are ready to build the release.
Now let’s test the release by running it on our local machine. We’ll need to pass in the required environment settings (note: adjust the values to your local settings, i.e. your local Postgres user etc).
Fantastic, looks like the release is working. If we navigate to http://localhost:4000/products, everything looks good:
Onto the docker image!
Create the Docker image
The first step is to install Docker desktop if not already installed.
Once that is all good, we need a Dockerfile.
This is almost an exact replica of the Dockerfile from https://akoutmos.com/post/multipart-docker-and-elixir-1.9-releases/.
A few differences:
- Since there is now an Elixir 1.9 docker image available, we use it instead of building Elixir from source.
- Instead of starting our application directly via
CMD ["./prod/rel/...."]we use a startup script (
start_commands.sh) via a Docker
ENTRYPOINT. This is so we can run migrations prior to application startup.
We need to create the
We are calling out to a release tasks module prior to starting the application. We don’t have access to
mix tasks in our release, so we need to create a module to run the migrations.
Let’s create the module now.
Pretty simple, all we are doing is running thru the migrations via
We’ve added new code to our application, so we need to rebuild the release in order to include it.
Choose to over-write the existing release:
Good to go… let’s build our Docker image!
Let’s test that the Docker image works locally. In order to also test that the migration in our start up script works, let’s drop and re-create the database.
Now we’ll run the image.
We see the migrations get run and then the application is started. We can view our products page via http://localhost:4000/products
We have no products, since we dropped and re-created the database, but if you create a few products, you’ll see that everything is working, our Docker image is a success!
That wraps things up for today. Thanks to the new Elixir 1.9 release task, creating a release is straight forward. Likewise creating a Docker image from a release is pretty painless.
Next time out we’ll look at how to get our image deployed to AWS.
Thanks for reading and I hope you enjoyed the post!