Here's a guide to deploy GitLab with:
- Docker Swarm mode for cluster management and orchestration.
- Docker Registry with HTTPS, TLS (SSL) handled automatically, using GitLab credentials and integration with GitLab CI.
- Traefik proxy to handle domain based redirection, HTTPS communication and automatic certificate generation with Let's encrypt. You don't need to build a custom Nginx proxy or anything similar, it's all handled by Traefik.
- Automatic generation and configuration of GitLab / Registry internal communication certificates.
Set up a Docker Swarm mode cluster with a main global Traefik load balancer following the guide at DockerSwarm.rocks.
It will take you less than 20 minutes to follow it to deploy a cluster (of one or more machines) and have it ready for the next steps.
Configure your DNS domain records to point one subdomain for your GitLab instance and one subdomain for the Docker Registry to the new server.
For example, a DNS A
record for gitlab.example.com
and a DNS A
record for registry.example.com
.
If you have a cluster with several nodes, make sure those DNS records point to the IP of the node that will host the gitlab
and registry
services.
This is because gitlab
has to listen on port 22
for Git to work, but we will configure it to make it listen on port 22
only on the server that has GitLab.
That way, if you have other servers in your cluster, you won't have to change the default SSH port of all of them.
As by default Git uses the same SSH port 22
, and you want your GitLab container to use that port, modify your server SSH configuration to use a different port. This guide will assume you will use port 2222
for your server SSH and port 22
for your GitLab.
Connect to your remote server as normally, e.g.:
ssh root@gitlab.example.com
Create a backup of your SSH config file:
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
Modify your SSH config.
Warning: if something is broken after modifying the SSH configuration, you could lock yourself out of the server.
You need to have a line Port 2222
and make sure there's no line Port 22
.
You can use this command to do it automatically, it will check for a line with Port 22
or #Port 22
and replace it with Port 2222
.
sed -i 's|^#\?Port 22$|Port 2222|' /etc/ssh/sshd_config
Or you can modify it with nano
by hand, with:
nano /etc/ssh/sshd_config
Confirm that there's a single line with Port 2222
with:
grep "^Port" /etc/ssh/sshd_config
Then restart the SSH server:
systemctl restart sshd.service
Warning: at this point, if you lose your connection and something was wrong in the configuration, you could lock yourself out of the server. Run the following steps in a new terminal session, without closing the existing one, so that, if something was wrong, you can use the current session to edit the configurations, revert them, and restart the SSH service, before being locked out.
In a different terminal session, without closing the existing one, try connecting with SSH to your server using the new port, e.g.:
ssh -p 2222 root@gitlab.example.com
If you get connected to the remote server normally, everything is working correctly.
- Download the Docker Compose stack file:
curl -L https://raw.githubusercontent.com/sameersbn/docker-gitlab/master/docker-compose.swarm.yml -o docker-compose.swarm.yml
Set and export the environment variables GITLAB_HOST
and REGISTRY_HOST
to the subdomains you configured.
For example:
export GITLAB_HOST=gitlab.example.com
export REGISTRY_HOST=registry.example.com
You will use the domain for GITLAB_HOST
to access GitLab in your browser and to commit and push with Git.
And you will use the domain for REGISTRY_HOST
to store, push, and pull Docker images, e.g.:
docker pull registry.example.com/mygroup/myproject/imagename:sometag
These environment variables will be used by the file docker-compose.swarm.yml
.
They are used inside of the stacks and are also used to configure the domains for the Traefik load balancer. Because of that, you need to export them for them to be available when deploying the stack.
There are many additional environment variables with different configurations.
Read the main README for all the options.
For Registry specific options and details, check the main GitLab Registry documentation in this repo.
You can configure them by editing de file docker-compose.swarm.yml
.
You can do it in the command line with a program like nano
, e.g.:
nano docker-compose.swarm.yml
If you want anyone to sign up instead of only people with invitation, change GITLAB_SIGNUP_ENABLED
to true
:
export GITLAB_SIGNUP_ENABLED=true
There are several environment variables that require random strings for keys and passwords.
For the sections that require generating random strings for keys and passwords, each time, run the following command and copy the output:
openssl rand -hex 32
# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f
You can copy it and set it in the file like:
- GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_SECRETS_ENCRYPTED_SETTINGS_KEY_BASE=long-and-random-alphanumeric-string
There are several other settings that you might want to configure, like email accounts for notifications, SMTP credentials to send emails, etc.
If you modified the file locally, make sure you copy it to your remote server, e.g.:
scp -P 2222 docker-compose.swarm.yml root@gitlab.example.com:/root/
and connect via SSH to your remote server, e.g.:
ssh -p 2222 root@gitlab.example.com
If you modified the file locally and then connected to your server later, make sure you export the environment variables GITLAB_HOST
and REGISTRY_HOST
that are needed even if you modified the Docker Compose file (as those are used in the Traefik labels).
Because the Docker Swarm cluster may have more than one single node (machine) in the cluster, we need to make sure that the services that need to save and read files from volumes are always deployed to the same node.
For example, the service for redis
uses a volume, you can check it on the docker-compose.swarm.yml
file:
volumes:
- redis-data:/var/lib/redis:Z
To make sure redis
is always deployed to the same node that contains the same volume redis-data
, we have a constraint:
deploy:
placement:
constraints:
- node.labels.gitlab.redis-data == true
This tells Docker that the service redis
should be deployed to a Docker node (a machine in the cluster) with the label node.labels.gitlab.redis-data=true
.
Then we can make one node (only one) have this label, and Docker Swarm will always deploy the redis
service to the same node. That way, the service will keep reading the same volume every time. Even if you re-deploy or upgrade the stack.
Now we are going to add the needed labels to satisfy those constraints, to make sure the volumes work correctly.
-
Connect to a manager node in your Docker Swarm cluster. It could be the same server that will run GitLab, or it could be a different one.
-
If you are deploying the stack in the same current manager node, get its node ID and store it in an environment variable:
export NODE_ID=$(docker info -f '{{.Swarm.NodeID}}')
- Otherwise, you can check the current available nodes with:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
m48gz5e8ucmk59af4m6enmnaz * dog.example.com Ready Active Leader 19.03.9
4w456u9lnanau629v3y456k9d cat.example.com Ready Active 19.03.9
mue36qqwqnzrqt4iqi0yyd6ie gitlab.example.com Ready Active 19.03.9
And select the node where you want to deploy the main gitlab
service. In this example, in the node that has a HOSTNAME
with value gitlab.example.com
, with node ID mue36qqwqnzrqt4iqi0yyd6ie
.
So, you could export that environment variable using the node ID with something like:
export NODE_ID=mue36qqwqnzrqt4iqi0yyd6ie
- Create a label in that node, so that the service
gitlab
andregistry
are always deployed to the same node and use the same volumes:
docker node update --label-add gitlab.certs-data=true $NODE_ID
We need to make sure gitlab
and registry
are deployed on the same node because they share the same volume with the TLS certificates generated by gitlab
.
Now create the label for redis
. You could use another node in your cluster if you have more than one, for simplicity we are going to use the same node, e.g.:
docker node update --label-add gitlab.redis-data=true $NODE_ID
And add the label for postgres
:
docker node update --label-add gitlab.postgresql-data=true $NODE_ID
Note: you only have to set those labels once. Not every time you want to re-deploy your stack.
Now, having the labels set in the Docker nodes, and the environment variables exported, you can deploy your stack:
docker stack deploy --compose-file docker-compose.swarm.yml gitlab
Note: the environment variables GITLAB_HOST
and REGISTRY_HOST
have to be available every time to deploy the stack. But the node labels can be set only once, the first time you deploy.
You can check the status of the deployment with:
docker stack ps gitlab
Or check the logs, for example for the service gitlab_gitlab
:
docker service logs gitlab_gitlab
GitLab and the Docker Registry have public facing HTTPS certificates generated with Let's Encrypt for each one. But to communicate between themselves they use an additional self-signed certificate.
To tell GitLab to generate those self-signed certificates for the internal communication with GitLab, the gitlab
service has an environment variable:
- GITLAB_REGISTRY_GENERATE_INTERNAL_CERTIFICATES=true
GitLab will generate the certificates and store them in the location given by:
- GITLAB_REGISTRY_KEY_PATH=/certs/registry.key
And that location, /certs
, is mounted as a named volume:
volumes:
- gitlab-data:/home/git/data:Z
- certs-data:/certs
So, the self-signed certificates will be generated inside the named volume gitlab-certs
.
And the Registry also has that named volume mounted:
volumes:
- registry-data:/registry
- certs-data:/certs
And the Registry is configured to look for the certificate in that same location that GitLab used to generate the certificate:
- REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE=/certs/registry.crt
If you use GitLab and want to integrate Continuous Integration / Continuous Deployment, you can follow this section to install the GitLab runner.
You should create the runner using Docker standalone instead of in Docker Swarm mode, as you need the configurations to persist, and in Docker Swarm mode, the container could be deployed to a different server and you would lose those configurations.
For testing, the GitLab runner can run in any node.
But if you want to deploy another runner for deployment (or use the same one), it has to run on a manager node in the Docker Swarm cluster.
To install a GitLab runner in a standalone Docker run:
docker run -d \
--name gitlab-runner \
--restart always \
-v gitlab-runner:/etc/gitlab-runner \
-v /tmp/builds:/tmp/builds \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
Then, enter into that container:
docker exec -it gitlab-runner bash
- Go to the GitLab "Admin Area -> Runners" section.
- Get the URL and create a variable with it in the bash session inside of your Runner's Docker container, e.g.:
export GITLAB_URL=https://gitlab.example.com/
- Get the registration token and create a variable in the bash session inside of your Runner's Docker container, e.g.:
export GITLAB_TOKEN=WYasdfJp4sdfasdf1234
- Run the next command editing the name and tags as you need, you can also edit them later in the web user interface.
gitlab-runner \
register -n \
--name "Docker Runner" \
--executor docker \
--locked false \
--access-level not_protected \
--builds-dir /tmp/builds \
--docker-image docker:latest \
--docker-volumes /tmp/builds:/tmp/builds \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock \
--url $GITLAB_URL \
--registration-token $GITLAB_TOKEN \
--tag-list dog-cat-cluster,stag,prod
- You can edit the runner more from the GitLab admin section.