Looking to streamline your deployment process for Docker Compose services via GitLab pipelines? This guide walks you through deploying services on any server with SSH access, using GitLab to automate and simplify your workflow. Whether you're a seasoned developer or just diving into DevOps, this tutorial has the steps you need to get up and running smoothly!
Initial server preparation
Before you can start interacting with your server you will need to prepare it. In my case, I used Hetzner as they offer very cheap solid servers with a great interface.
Generate SSH key
You will need to generate an SSH key, one specifically for the deployments. Ideally, this would be different from the key you use to SSH into the server. This guide from GitLab is pretty good and should get you started.
Add SSH key to authorized_keys
To be able to communicate with the server and that it allows connections using this ssh key you will need to add it to authorized_keys
.
Installing docker & docker compose
You will need to ensure docker is running on your server, and that docker compose also works. You can follow this guide which explains it very well for all the platforms.
The simple way to test this is to first verify docker can run via:
docker run hello-world
And after that reports all systems ok check your version of docker compose:
docker compose version
Adding GitLab registry and authenticating
You will need to login to the GitLab registry and setup a credentials store. The initial login can be easily done via:
echo "<token>" | docker login registry.gitlab.com -u "<username>" --password-stdin
Here are some useful guides for the different credential store options. This is how your .docker/config.json
would look like usually.
{
"auths": {
"registry.gitlab.com": {}
},
"credsStore": "pass"
}
GitLab Deployment Pipeline
Once you have prepped your server you can verify everything works by creating the first part of the pipeline which SSHes into the server. Provide the SSH settings in the GitLab CI/CD Variables
Base SSH Pipeline
The first part just sets up our ability to SSH into the server and is easily generalizable to multiple servers.
.deploy:
stage: deploy
image: ubuntu
variables:
# you could use whatever user you setup initially
SSH_USER: "root"
# this variable will come from the CI settings of the repo/group
SSH_HOST: "$SERVER_IP"
before_script:
# ensure SSH is installed in the job
- apt-get update -y && apt-get install openssh-client -y
- eval `ssh-agent -s`
# set the private key which will come from the CI settings
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
# setup the ssh settings to avoid warnings about new hosts
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ls -la ~/.ssh
# this step will already fail if your server does not accept the SSH Key
- ssh-keyscan $SSH_HOST >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
Deployment via Docker Compose
We want the deployment to stop the previous containers if they exist, copy over the docker-compose file(s), and then run the up -d
command. The --remove-orphans
command ensures if we change some services in the docker compose and they become orphaned they will get removed.
deploy:core:
extends: .deploy
variables:
SERVER_PATH: "/root/deployment/core/"
script:
# stop the containers
- ssh $SSH_USER@$SSH_HOST "cd $SERVER_PATH && [ -f docker-compose.yml ] && \
docker compose down || true"
# copy over all the files
- scp -r . $SSH_USER@$SSH_HOST:$SERVER_PATH
# restart the containers
- ssh $SSH_USER@$SSH_HOST "cd $SERVER_PATH && \
docker compose pull && \
docker compose up -d --remove-orphans"
Environment variables & secrets
Suppose you need to also copy over the environment variables such as passwords and other variables.
NOTE: You need to have these variables defined in your GitLab CI/CD settings
deploy:core:
extends: .deploy
script:
# Inject postgres password
- sed -i '/^POSTGRES_PASSWORD=/d' .env
- printf "\nPOSTGRES_PASSWORD=%s\n" "$POSTGRES_PASSWORD" >> .env
- printf "\nMONGO_PASSWORD=%s\n" "$MONGO_PASSWORD" >> .env
- printf "\nREDIS_PASSWORD=%s\n" "$REDIS_PASSWORD" >> .env
Or even use this crafty script which will take an .env.example
and extract all the variables defined in the example.
https://gitlab.com/-/snippets/3763150/raw/main/prepare_env.sh