Tech Blog

Deploy Docker image with GitLab

February 18, 2020

I will show you one way to deploy your docker images to your remote server via GitLab CI

In this article

build a docker image
push to Gitlab registry
deploy to your remote server

Build deploy pipeline

First we need to create a .gitlab-ci.yml file at the root of our project. Since we want to build our project into a docker image we will run the pipeline inside another docker. At the very beginning of our pipeline file we define the image in which we will run the pipeline:

image: docker:latest

Next we define some global variables which we can reference throughout the pipeline

variables:
 DOCKER_DRIVER: overlay2
 DOCKER_TLS_CERTDIR: ""
 APP_VERSION: "1.3.0"

 services:
 - docker:19.03.5-dind

We will organize our .gitlab-ci.yml file into different stages:

stages:
  - image
  - transfer
  - deploy

image: at this stage we will build our project into a new docker image
transfer: upload our newly built docker image to Gitlab registry deploy: pull the docker image from Gitlab registry to our own server and run the image

Build docker image

Lets build our image first:

build_image:
  stage: image
  only:
    - master
  script:
    - docker build --no-cache=true --pull -f ./Dockerfile -t $CI_REGISTRY_IMAGE:$APP_VERSION .

What’s happening here?
build_image: This is how I called our first stage. You can name it as you wish
stage: image: This task belongs to the image stage Next we instruct this stage to run only on our master branch

only:
    - master

In the script: section we put our instructions on what this task has to do for us

- docker build --no-cache=true --pull -f ./Dockerfile -t $CI_REGISTRY_IMAGE:$APP_VERSION .

Build our actual docker image and name it $CI_REGISTRY_IMAGE:$APP_VERSION. $CI_REGISTRY_IMAGE is a built in Gitlab global variable and refers to the name of gitlab repo and $APP_VERSION is our application version we defined at the beginning of our config file

Push the image to Gitlab registry

transfer_image:
  stage: transfer
  script:
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
    - docker push $CI_REGISTRY_IMAGE:$APP_VERSION
  only:
    - master

Before we can push anything to Gitlab registry, Gitlab requires that we authorize ourselves. We achieve this with docker login
Then simply issue docker push. $CI_REGISTRY_IMAGE is another predefined global variable which contains the gitlab registry uri.

The only step left is to deploy and run our image on our server.

In order to ssh from Gitlab pipeline to our server we have to setup ssh keys.
In this stage we are also defining custom variables $DESTINATION_HOST and $SSH_PRIVATE_KEY. Generally, we try to avoid hardcoding sensitive data such as keys, passwords… etc. in our application code. Gitlab allows us to configure sensitive data inside their Dashboard.

Configure the project in Gitlab

Login to your Gitlab dashboard, go to your project and navigate to Settings -> CI / CD

Chinese Salty Egg

Navigate to the Variables section and click on the Expand button (top-right). Create $DESTINATION_HOST and $SSH_PRIVATE_KEY variables. As a general note you should mask variables with sensitive information so they don’t appear in your Gitlab logs. In the $SSH_PRIVATE_KEY put your private ssh key that you generated previously in base64 encoding.

You can base64 encode your private key by running in your terminal:
$ cat ~/.ssh/your_private_key | base64

In $DESTINATION_HOST put the IP of your server.

Deploy image

deploy image:
  stage: deploy
  variables:
    DOCKER_HOST: ssh://user@$DESTINATION_HOST
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | base64 -d | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - ssh-keyscan -t rsa $DESTINATION_HOST >> ~/.ssh/known_hosts
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $CI_REGISTRY_IMAGE:$APP_VERSION
    - docker run --name my_image -d $CI_REGISTRY_IMAGE:$APP_VERSION
  only:
    - master

First and very important we define a global variable DOCKER_HOST. When this variable is defined docker will use it automatically to execute docker over ssh commands. We will take advantage of it to deploy the image.

 variables:
    DOCKER_HOST: ssh://user@$DESTINATION_HOST

- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - login to Gitlab from your server to authenticate
- docker pull $CI_REGISTRY_IMAGE:$APP_VERSION - pull the newly created image to your server
- docker run --name my_image -d $CI_REGISTRY_IMAGE:$APP_VERSION - finally we run our image on our server

In the before_script section we load our private ssh key to the ssh agent so we can access our server

Now go ahead and run a new pipeline in the Gitlab Dashboard by navigation to CI/CD > Pipelines and click on the Run Pipeline button.

Attention

As a side note you might want to increase your SSH sessions on your host. By default the ssh config is located in /etc/ssh/sshd_config. Set the MaxSessions to something bigger than the default 10. I usually set this to 1000

Summary

You just saw how to build, push and deploy a docker image to your server. Such build process allows us to concentrate on developing our application code, commit, push and everything will be deployed to your live server by Gitlab CI.


Share with: