AzureDevOps, Elixir, Docker, CI/CD, and the others — Part 2: Release and deploy.
Tags: continuousdeployment, azure, docker, elixir
Published: 2019-11-22 13:01:15

In the previous post ([part1](https://dev.to/behnam/azuredevops-elixir-docker-ci-cd-and-the-others-part-1-docker-compose-and-testing-o1m)) I tried to use docker-compose doing build and test in AzureDevOps. The app is a [hello world](https://github.com/benyblack/elixir-phoenix-ci) web site written in elixir and phoenix.


##Goal
The goal is to create a __continuous deployment pipeline__ to deploy our web app to an app service in azure by using __releases__ in elixir and a __docker image__.

My todo list is to create and config these services:

1. Azure Container Registry
2. Azure Postgres SQL Server
3. Build & Push pipeline in AzureDevOps
4. App service in Azure
5. Deploy pipeline in AzureDevOps

##Azure
First thing first, we need a __container registry__. It will be used to store our Docker images. Let’s make one in the Azure portal.


![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/1hgxupzt5nox6rob7xnj.png)

Don’t forget to set the __Admin user__ to __Enable__.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/4j7i3hv2zpnf7zgm6s31.png)

Where to put the database? hmmm! since we don’t use docker-compose here then we need to create a __Postgres Server__:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/8azgvihpm4c0xuqd8g2i.png)

The next step is to create a database. To be able to connect to your database, your IP has to be added to the __Allowed list__. Do it in the **Connection Security** section of your **Postgres Server** page on **Azure**. Also, enable **Allow access to Azure services**, we need it later to be able to connect from our web app. An easy way to connect is by using **pgAdmin**. Also, you can use psql in bash and connect with:

```bash
psql --host=**practicing**.postgres.database.azure.com --port=5432 --username=**practiceadmin@practicing** --dbname=postgres
```

And run the create database command:


```sql
CREATE DATABASE practicingdb;
```

Since the **app service** needs to be created based on an image, and wedon’t have it now, I’ll do it later.

##‌Build & Push

Let’s back to our beloved AzureDevOps. I am going to deploy on **Azure**, then I need to add **Azure Container Registry** service connection in **Project Setting**.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/6ekn0fi4xmlwnonkv1xv.png)

Also, I need a Dockerfile to build our production image. This is a **multistage** build docker file that uses the new **Release** feature of ELixir 1.9. It results in a very smaller image, something like 35 MB vs 1.2 GB if we use **Release**.

{% gist https://gist.github.com/benyblack/ca90ec3b9b63eb5f7d285709c075e9ef %}

Create a new pipeline and in the **Configure** step, select Docker. In the **Review** step, go to the Docker task and hit **Settings**. Make sure that your config is like this:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/y3dsbbof9fu1vriwf5a1.png)

Hit **Save and run**. When it got finished, check **Repositories** in the Container Registry. You should see a new one there:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/zi9ojua6tgsbdzp9g2t8.png)

Ok. It’s time to back to Azure Portal and create a new App Service.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/bmwylw9c7ovupzidf3w1.png)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/v93299qbo5miw7b7o7bf.png)

Then go to **Review + create** and hit **Create** button.

##Environment Variables
Before going further, there is something in the app that should be adjusted before going to deploy it. Let’s take a look at **prod.exs** at the **config** folder. It has a reference to **prod.secret.exs** which contains the **connection string** and **secret_key_base**. Also, this file is excluded by **.gitignore**. It is not a good idea to put this file in the source control. Instead, we can feed this data by **Environment Variables**. First, replace the last import_config line of **prod.exs** by this:

```elixir
config :hello_world_ci, HelloWorldCiWeb.Endpoint,  
    secret_key_base: System.get_env(“SECRET_BASE_KEY”)
# Configure your database
config :hello_world_ci, HelloWorldCi.Repo, 
    username: System.get_env("DB_USERNAME"), 
    password: System.get_env("DB_PASSWORD"), 
    database: System.get_env("DB_NAME"), 
    hostname: System.get_env("DB_HOST"), 
    pool_size: 15
```

Go to your App Service page and find Configuration in Settings. Add the above keys and needed values. Database related values can be found in the Postgres page in the portal. Don’t forget to hit the save button.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/w61ksruun8zk84q0bakw.png)

(Please read this section carefully. There are some tricks for using **elixir releases**). Everything looks good but if you deploy your web app now you would realize that the app is unable to read the environment variables! No kidding! The reason is when the release build is used, it evaluates env variables for the prod config file at **build time** and never calls them again at **run time**. There is a good solution for it. You can add a file in the config folder named releases.exs and it will be used in the release build and will be able to read environment variables at runtime. Remove config items, like DB connection string and secret key from **prod.exs** and move them to **releases.exs**. Then you will be able to use __System.fetch_env!__ as normal.

{% gist https://gist.github.com/benyblack/f40c8f96bc3c53d30046635af28e8f5c %}


If you didn’t commit and push your last changes, do it and let the pipeline build it. When finished, click on **Release**. Here we go for the release pipeline.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/laaduvmdkbb1xl82cdry.png)

From the right pan, select **Azure App Service deployment** and **Apply**:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ud30t4fnxfwqza438y4c.png)

Next, go on Tasks tab, Select **Stage 1** and in the right panel click on **Unlink all**.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/2r98jrqmrs4pbg4npg65.png)

Click on Deploy Azure App Service in the left and fill the right panel as shown in this (some values may be different in your account):

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/rawoid27aoo4pn5n9n3z.png)

After clicking on the **Save** button, hit the **Create Release**.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ybtp4j1zpz6zycxkz01i.png)

Fill it like above and hit **Create**.

In this step, I am going to change a file and then see the result on the website. Go to **lib>hello_world_ci_web>templates>page>index.html.eex** and add this line somewhere to be seen later:

```html
<p>MIX_ENV: <b><%= System.get_env(“MIX_ENV”) %></b></p>
```

Commit and push it and wait until the Build pipeline finishes its work. Immediately, the Release pipeline should start.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/9qrmg2ajtbu6pj1lkvu6.png)

Wait until it finishes its job.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/7ay9hjza7n46jqw22y5k.png)

Wait a bit and refresh the website:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/xuj94ucpxdtcdazxd2l1.png)

Awesome! That’s it. It may seem complicated but trust me, it’s easier than it seems. What about the next part? In the next post, I will try to deploy the app on Kubernetes.