Gitlab in Docker

These installation notes are mostly a note to myself, documenting the installation process of a Gitlab Omnibus Container in Docker, plus Gitlab Runners.
OS Setup
We are installing into /export/gitlab
, a 10G xfs slice from the local flash pool:
# lvcreate -n gitlab -L 10G data
# mkfs -t xfs /dev/data/gitlab
# mkdir /export/gitlab
# mount /dev/data/gitlab /export/gitlab
# echo "/dev/data/gitlab\t/export/gitlab\txfs\tbsdgroups,usrquota,grpquota,attr2,nofail,noatime 1 2" >> /etc/fstab
# mkdir /export/gitlab/{gitlab,gitlab-runner}
# mkdir /export/gitlab/gitlab/{config,data,logs}
Docker
We are using docker-compose
to run this, with a .env
(dotenv) like so:
# cat .env
GITLAB_HOME=/export/gitlab/gitlab
GITLAB_DOMAIN=gitlab.home.koehntopp.de
GITLAB_HTTP_PORT=81
GITLAB_HTTPS_PORT=444
GITLAB_SSH_PORT=2222
And a docker-compose.yaml
like so:
---
version: "3"
services:
web:
container_name: "gitlab"
hostname: "gitlab"
image: "gitlab/gitlab-ce:latest"
restart: always
hostname: "${GITLAB_DOMAIN}"
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url "https://gitlab.home.koehntopp.de"
ports:
- '${GITLAB_HTTP_PORT}:80'
- '${GITLAB_HTTPS_PORT}:443'
- '${GITLAB_SSH_PORT}:22'
volumes:
- '${GITLAB_HOME}/config:/etc/gitlab'
- '${GITLAB_HOME}/logs:/var/log/gitlab'
- '${GITLAB_HOME}/data:/var/opt/gitlab'
## vim: syntax=yaml ts=2 sw=2 sts=2 sr et ai
When starting this with docker-compose up
, we can follow the full horribleness of the installation process in the console: The Gitlab Omnibus container collects a large number of processes internally, including a postgres, puma, nginx and a number of additional components, and configures itself internally using Chef. It is the Anti-Container.
gitlab.rb
The initial run will produce a gitlab.rb
config file in /export/gitlab/gitlab/config/gitlab.rb
. The file is over 100KB in size, and will contain deactivated config.
A very minimal, runnable base config for me looks like this:
# grep -v "^#" gitlab.rb | uniq
external_url 'http://gitlab.home.koehntopp.de'
gitlab_rails['smtp_enable'] = false
gitlab_rails['gitlab_email_enabled'] = false
gitlab_rails['gitlab_default_can_create_group'] = false
gitlab_rails['gitlab_username_changing_enabled'] = false
gitlab_rails['gitlab_shell_ssh_port'] = 2222
gitlab_kas['enable'] = false
TLS Forwarding from host to container
The internal ports need to be exported to the home network, so we need an Apache TLS forwarding config.
We are using this:
<VirtualHost *:80>
ServerName gitlab.home.koehntopp.de
ErrorLog ${APACHE_LOG_DIR}/gitlab-error.log
CustomLog ${APACHE_LOG_DIR}/gitlab-access.log combined
Alias /.well-known/acme-challenge /var/lib/dehydrated/acme-challenges
<Directory /var/lib/dehydrated>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<If "!-f '%{REQUEST_FILENAME}'">
RedirectMatch permanent ^/(.*) "https://gitlab.home.koehntopp.de/$1"
</If>
</VirtualHost>
<VirtualHost *:443>
ServerName gitlab.home.koehntopp.de
ErrorLog ${APACHE_LOG_DIR}/gitlab-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/gitlab-ssl-access.log combined
SSLEngine on
SSLCertificateFile /var/lib/dehydrated/certs/home.koehntopp.de/cert.pem
SSLCertificateKeyFile /var/lib/dehydrated/certs/home.koehntopp.de/privkey.pem
SSLCertificateChainFile /var/lib/dehydrated/certs/home.koehntopp.de/chain.pem
# SSLProxyEngine on
ProxyPreserveHost On
ProxyPass "/" "http://127.0.0.1:81/" nocanon
ProxyPassReverse "/" "http://127.0.0.1:81/"
AllowEncodedSlashes NoDecode
DocumentRoot /var/www/gitlab.home.koehntopp.de
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
We are terminating the TLS at the Apache and forward plaintext to the nginx, which then forwards to the internal Ruby. This is silly, but I was not feeling like pulling that ball of string apart.
The ProxyPass ... nocanon
and AllowEncodedSlashes NoDecode
are necessary to avoid internal Error on various URLs that require passing on of //
and /-/
URL fragments (several issues, for example here
).
Basic Setup
Admin Login is with “root”, and will guide you through a password change and some basic setup.
I created users for the family, and groups for my work and for the little one.
Once you have groups, pushing existing repositories into gitlab is quickly done with
# git push --set-upstream ssh://git@gitlab.home.koehntopp.de:2222/kris/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)
This will create a repo for the user or group (here: kris
) that has a name identical to the current directory. The xargs basename
expression can be replaced with the desired literal name instead.
Afterwards, it may be useful to git remote remove origin
, git remote add origin ...
. A quick git pull --rebase
and git branch --set-upstream-to=origin/master master
will exercise and config the local push and pull operations, too.
A “hello-ci” project
We are creating a basic Python project for gitlab-runner, for testing, kk/probe
.
# cat probe.py
#! /usr/bin/env python3
import src
if __name__ == '__main__':
print(f'Hi, {src.my_name()}!')
and
# cat src/__init__.py
from src.main import my_name # noqa
and
# cat src/main.py
def my_name():
return "Kris"
In a src/tests/
directory, we are running pytest
:
#! /usr/bin/env python3
import src
def test_my_name():
assert src.my_name() == "Kris"
At the toplevel, we put our requirements.txt
:
flake8
pytest
and a tox.ini
:
[flake8]
exclude=.git,__pycache__,docs,*venv
[pytest]
addopts = -ra -q
We can now build a .gitlab-ci.yml
, also at the toplevel:
default:
image: python:3.8
before_script:
- python --version
- pip install -r requirements.txt
stages:
- Test
flake8:
stage: Test
script:
- flake8
pytest:
stage: Test
script:
- pwd
- ls -l
- export PYTHONPATH="$PYTHONPATH:."
- python -c "import sys;print(sys.path)"
- pytest src
Yes, the testing cruft in the pytest setup can later go away…
Now, to make this work, we need to install gitlab-runner in a docker variant, and config it.
Gitlab Runner
At this point, the runner still needs to be docker-compose
‘ed. I hacked it for testing like this:
# mkdir -p /export/gitlab/gitlab-runner
# cd !$
# mkdir config
# cat doit
docker run -d --name gitlab-runner \
--restart always \
-v /export/gitlab/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
This will create a config.toml
in the config directory. We can
# docker exec -it gitlab-runner bash
root@ffd124dab4aa:/# gitlab-runner register
# gitlab-runner register
Runtime platform arch=amd64 os=linux pid=47 revision=8fa89735 version=13.6.0
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.home.koehntopp.de/
Enter the registration token:
TheToken
Enter a description for the runner:
[ffd124dab4aa]: A test runner
Enter tags for the runner (comma-separated):
test
The token required for registration can be obtained as described here .
I registered group runners for each of my two internal groups, and a shared runner for the (empty) rest.
All of this will rewrite the config.toml
. I then upped the concurrency to 6 (8 threads available in the hardware).
Later on, it will turn out that the docker images in my Ubuntu are not writeable as needed, a helper image needs to be added.
The helper_image
line has been added manually below, according to this note
:
# cat /export/gitlab/gitlab-runner/config/config.toml
concurrent = 6
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "JX_Snack (Docker)"
url = "https://gitlab.home.koehntopp.de"
token = "TheToken
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
helper_image = "gitlab/gitlab-runner-helper:x86_64-6fbc7474"
tls_verify = false
image = "python:3"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
...
For easier testing, it may be useful to allow CI runs on untagged commits. This can be set up as root
in https://.../admin/runners
for the desired test runner.
Allowing the runner to pick up untagged jobs can be useful for testing. It needs to be disabled later.
With some random committing we can now trigger and debug the pipeline we defined earlier above. Eventually it will actually do something.
Eventually, a testing success.
Having a gitlab and a CI/CD pipeline allows us to package the Python Discord Bot development process for the little one in a way that allows him to focus on the various stages of the development process sequentially. For now, testing and deployment can happen magically, we will visit that only later.