This article assumes you are using (or interested in using) GitLab and GitLab CI. While the following explains how to deploy Docker containers, it can easily be adjusted to accommodate any type of project. Feel free to post any questions in the comments section below.

Objectives

Once the production branch (or any branch we’re interested in) is pushed, have the runner build our application’s Docker container. Once the container is built, push to our project’s GitLab private container registry. Deploy the container to our production server using the (free) Stackahoy command-line tool. As a disclaimer, we built Stackahoy and have been using it internally for a few years.

Step 1: Create a runner.

GitLab CI runners are decoupled from the core GitLab CI instance and serve as the workhorses for testing, building, and deploying an application. We are using GitLab’s Multi-runner on a small Google Compute Engine instance.

Follow the steps here for installing the runner in your desired environment, then run:

# Make sure to replace REG_TOKEN and/or the

# --url to match your environment.

-n \

--url

--tag-list "docker" \

--registration-token REG_TOKEN \

--executor docker \

--description "Docker Runner" \

--docker-image "docker:latest" \

--docker-volumes /var/run/docker.sock:/var/run/docker.sock sudo gitlab-ci-multi-runner register \-n \--url https://p ath-to-github.com/ci \--tag-list "docker" \--registration-token REG_TOKEN \--executor docker \--description "Docker Runner" \--docker-image "docker:latest" \--docker-volumes /var/run/docker.sock:/var/run/docker.sock

As of writing this article, there are three ways of using the “docker” command within our runner container. You can read about them here. We chose to volume the docker.sock file.

Step 2: Configure gitlab-ci.yml.

The gitlab-ci.yml file should be placed in the root of the project. This contains instructions used by GitLab CI.

.gitlab-ci.yml:

variables:

REGISTRY: your-gitlab-server:4567

REPO_ID: 574535091c632d0d00f646cc stages:

- build

- deploy build:

image: docker:latest

stage: build

script:

- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $REGISTRY

- docker build -t $REGISTRY/<PROJECT-GROUP>/<PROJECT-NAME> .

- docker push $REGISTRY/<PROJECT-GROUP>/<PROJECT-NAME>

only:

- production

tags:

- docker deploy:

image: stackahoy/stackahoy-cli

stage: deploy

script:

- stackahoy deploy -t $STACKAHOY_TOKEN -b production -r $REPO_ID --skip-delivery

only:

- production

tags:

- docker

Let’s break the file down.

variables:

REGISTRY: your-gitlab-server:4567

REPO_ID: 574535091c632d0d00f646cc

Here we define two variables which are used later in the file. This is simply to make the file more readable and reusable for other projects. REGISTRY is the URL for the private GitLab container registry. REPO_ID is the Stackahoy repository ID — you will retrieve this from the repository URL for the landing page within Stackahoy — https://stackahoy.io/group/<repo-id>/.

stages:

- build

- deploy

The stages property simply allows you to define the order in which the jobs should be executed. In this instance, the jobs are “build” and “deploy”. You could name these anything you’d like. Generally, you’d have a “test” job as well to handle your functional and unit tests and maybe a specific job for building a staging container.

build:

image: docker:latest

stage: build

script:

- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $REGISTRY

- docker build -t $REGISTRY/<PROJECT-GROUP>/<PROJECT-NAME> .

- docker push $REGISTRY/<PROJECT-GROUP>/<PROJECT-NAME>

only:

- production

tags:

- docker

The “build” job does two things. First, it will build the Docker container. Second, it will push the container to the private GitLab registry.

The last thing I’ll note about this snippet is the user that docker logs in as — gitlab-ci-token. This is a special user which is meant to be used with the variable $CI_BUILD_TOKEN. This variable will be defined on any GitLab CI build. This is much cleaner and safer than using your personal credentials for GitLab. The <PROJECT-GROUP>/<PROJECT-NAME> should be set based on the name of the project’s group and name, respectively.

deploy:

image: ubuntu:latest

stage: deploy

script:

- apt-get update

- apt-get install -y -qq npm curl

- npm install -g n

- n stable

- npm install -g stackahoy

- stackahoy deploy -t $STACKAHOY_TOKEN -b production -r $REPO_ID --skip-delivery

only:

- production

tags:

- docker

Update (July 2017): Now you can use the new Stackahoy CLI docker container to speed this up. Doing so would look like this:

deploy:

image: stackahoy/stackahoy-cli

stage: deploy

script:

- stackahoy deploy -t $STACKAHOY_TOKEN -b production -r $REPO_ID --skip-delivery

only:

- production

tags:

- docker

Finally, we’re at the deployment. In order to deploy, we can use Stackahoy which actually handles the secure transfer of files from the repository to the staging or production server. In our case, we’re not transferring files (note the --skip-delivery flag) and only running the docker run commands on the server since the container is already built.