Continuous deployment with GitLab, Docker and Heroku
Tags: devops, tutorial, docker, react
Published: 2019-12-29 18:07:24

---
title: Continuous deployment with GitLab, Docker and Heroku
published: true
tags: devops,tutorial,docker,react
canonical_url: https://alediaferia.com/2019/12/07/continuous-deployment-gitlab-docker-heroku/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/69ei1iyl0m9aegdbxv80.jpg
---

_Continuous Deployment_ refers to the capability of your organisation to produce and release software changes in short and frequent cycles.

[![](https://alediaferia.com/wp-content/uploads/2019/12/cd-graph.png)](https://www.martinfowler.com/bliki/FrequencyReducesDifficulty.html)<figcaption>Pain vs Frequency relationship – https://www.martinfowler.com/bliki/FrequencyReducesDifficulty.html</figcaption>

One of the ideas behind _Continuous Deployment_ is that increasing the frequency of deployment of your changes to production will reduce the friction associated with it. On the contrary, _deployment_ is often an activity that gets neglected until the last minute: it is perceived more as a necessary evil rather than an inherent part of a software engineer’s job. However, shifting deployment left, as early as possible in the development life cycle, will help surfacing issues, dependencies and unexpected constraints sooner rather than later.

For instance, continuously deploying will make it easier to understand which change caused issues, if any, as well as making it easier to recover. Imagine having to scan through hundreds of commit messages in your version control system history to find the change that introduced the issue…

Automatism is key to achieve continuous deployment.

## The project

In this article we’re gonna explore how to leverage tools like [GitLab Pipeline](https://docs.gitlab.com/ee/ci/pipelines.html), [Heroku](https://heroku.com) and [Docker](https://docker.com) to achieve a simple continuous deployment pipeline.

Let’s start by creating a simple _Hello World_ application. For the purpose of this article I’m gonna use [Create React App](https://create-react-app.dev/docs/getting-started/):

```
$ npx create-react-app continuous-deployment
$ cd continuous-deployment
$ npm start
```

Now that we have a running application, let’s build a Docker image to be able to deploy it to Heroku.

## The container image

We’re going to write a simple [Dockerfile](https://docs.docker.com/engine/reference/builder/) to build our app:

```
FROM node:10.17-alpine
COPY . .
RUN sh -c 'yarn global add serve && yarn && yarn build'
CMD serve -l $PORT -s build
```

First of all, [two things to keep in mind when building images for Heroku](https://devcenter.heroku.com/articles/container-registry-and-runtime#dockerfile-commands-and-runtime):

- Containers are **_not_** run with root privileges
- The port to listen on is fed by Heroku into the container and needs to be consumed from an environment variable

As you can see from the `Dockerfile` definition, we are starting the app by passing the `PORT` environment variable. We can now test the image locally.

```shell
$ docker build . -t continuous-deployment:latest
$ docker run -e PORT=4444 -p4444:4444
```

The `-e PORT=4444` specifies which port we’re going to listen to. You can now try your application at [http://localhost:4444](http://localhost:4444).

Additionally, I’ve added a `myuser` user at the end of the Dockerfile, just to make sure everything still works with a non-root user.

## Deploy to Heroku

Before building our continuous deployment pipeline, let’s deploy manually to make sure our image is good. Create a new application on Heroku and give it a name. In my case it’s gonna be [cd-alediaferia](https://cd-alediaferia.herokuapp.com).

![Screenshot 2019-12-05 at 15.04.37](https://alediaferia.com/wp-content/uploads/2019/12/Screenshot-2019-12-05-at-15.04.37.png)

Now let’s tag and push our image to the Heroku Registry after logging in.

```shell
$ heroku container:login
$ docker tag <image> registry.heroku.com/<app-name>/web
$ docker push registry.heroku.com/<app-name>/web
```

And release it straight to Heroku:

```shell
$ heroku container:release -a web
```

You should now have your app successfully up and running on Heroku at this point.

## The GitLab Pipeline

In this paragraph, we’re going to configure the pipeline piece on GitLab so that we can continuously deploy our app. Here follows the `.gitlab-ci.yml` file that I have configured for my [repository](https://gitlab.com/alediaferia/continuous-deployment).

{% gist https://gist.github.com/alediaferia/f53c0a9805f4c010cd6d3ca1ed6cd032 %}

In the above snippet we have defined two jobs: `build_image` and `release`.

### `build_image`

This job specifies how to build our Docker image. If you look closely, you’re actually going to notice that I’m not using Docker specifically but [Buildah](https://github.com/containers/buildah). `Buildah` is an OCI-compliant container building tool that is capable of producing Docker image with some [minor configuration](https://major.io/2019/08/13/buildah-error-vfs-driver-does-not-support-overlay-mountopt-options/).

### `release`

This job performs the actual release by pushing to your Heroku app.

## Additional configuration

Before trying our pipeline out, let’s configure the `HEROKU_API_KEY` so that it can get picked up by the `heroku`cli that we’re going to use in the pipeline definition.

![Pipeline Variable Setting](https://alediaferia.com/wp-content/uploads/2019/12/Screenshot-2019-12-05-at-22.52.20.png)<figcaption>GitLab pipeline variable setting</figcaption>

## Pushing to GitLab

Now that we have set everything up we are ready to push our code to the deployment pipeline.

![](https://alediaferia.com/wp-content/uploads/2019/12/Screenshot-2019-12-05-at-23.15.34-1024x482.png)<figcaption>GitLab pipeline in action</figcaption>

Let’s have a look at the build step that GitLab successfully executed.

![](https://alediaferia.com/wp-content/uploads/2019/12/Gitlab-pipeline-1024x451.jpg)<figcaption>GitLab pushing to the Heroku Registry</figcaption>

The first line uses `buildah` to build the image. It works pretty much like `docker` and I’ve used `--iidfile` to export the Image ID to a file that I then read from the command-line in the subsequent invocation.

The second line simply pushes to the Heroku Registry. Notice how easily I can log in by doing `--creds=_:$(heroku auth:token)`: this tells `buildah` to use the token provided by Heroku to log into the registry.

The deployment job, finally, is as easy as:

```
$ heroku container:release -a cd-alediaferia web
```

## Conclusion

My app is finally deployed, and everything happened automatically after my push to `master`. This is awesome because I can now continuously deliver my changes to production in a pain-free fashion.

[![](https://alediaferia.com/wp-content/uploads/2019/12/Screenshot-2019-12-07-at-12.20.49-1024x748.png)](https://cd-alediaferia.herokuapp.com/)<figcaption>My successfully deployed app</figcaption>

I hope you enjoyed this post. Let me know in the comments and follow me on [Twitter](https://twitter.com/alediaferia) if you want to stay up-to-date about DevOps and Software Engineering practices.

---
[This post](https://alediaferia.com/2019/12/07/continuous-deployment-gitlab-docker-heroku/) appeared first on [Ale's main thread](https://alediaferia.com).