GitLab: Installation und Konfiguration - ein Tutorial

GitLab - ein Werkzeug, so wichtig wie fast nichts anderes. Ich benutze GitLab nun schon seit 2014. Bei der damaligen Installation habe ich einige Fehler gemacht, und mich immer wieder darüber geärgert. 2017 der erste Umzug mit allen Projekte auf einen neuen Server. Oh Du rosige Zukunft. Was mir nicht bewusst war: Die Fehler sind mit umgezogen. 2019 der zweite Umzug. Auf einen neuen Server natürlich. Und die Fehler? ...rate mal....

2021. Das Jahr, das wohl alles in den Schatten stellt und alles von Grund auf ändert. Denn: Die Fehler ziehen nicht mit um. Nein, diesmal soll alles besser werden, die Unzulänglichkeiten von 7 Jahren werden nach dem Umzug einfach hinfort formatiert. Im wahrsten Sinne des Wortes. Aber was genau war passiert, was ist der schlimmste Fehler, den man eigentlich machen kann? Das ist sehr einfach zu beantworten.

Man installiert GitLab nicht auf einem Server auf dem schon was darauf ist. Nein, das macht man wirklich nicht. Jeder der denkt, "Ach, ich hab da noch diese Kiste, da laufen nur ein paar Stagings drauf...", denen sei geraten: LASST ES! Denn spätestens, wenn auf dem Server bereits ein Dienst läuft, auf dem Port 80 und 443 verwendet werden, darf man sich mit dem Thema: "Apache als Reverse Proxy für den GitLab NGINX" befassen. Das klingt am Anfang noch besonders motivierend, aber wenn das Thema Container Registry und andere Nettigkeiten ins Spiel kommen, darf man sich immer wieder mit der Frage beschäftigen: Und welche Ports müssen jetzt wohin? Und für den Docker Login gibt es wohl keine zuverlässige Lösung, diese durch einen Apache zu streamen. Und wer will schon einen blöden Port in seiner GitLab URL.


Vorbereitung: EC2 Instanz

Es gibt im AWS Marketplace bereits fertige Vorlagen zur direkten Nutzung auf einer EC2 Instanz, allerdings sind diese relativ schlecht bewertet. Aus diesem Grund wird die Instanz ein aktuelles CentOS 8 sein, und GitLab wird als normale CE Edition darauf installiert und konfiguriert. Für eine normale TYPO3 Agency wird auch nicht gleich die c5.xlarge Instanz benötigt, welche aktuell mit über $ 120 zu Buche schlägt, sondern für den normalen Gebrauch als Repository, Registry und Ticketsystem / Service Desk reicht schon alles ab der Instanz t3a.medium, welche ich für diesen Artikel verwende. Die Merkmale dieser Instanz sollten wie folgt sein:

  • Instanz-Typ: t3a.medium und aufwärts
  • Amazon AMI: CentOS 8 (x86_64)- with Updates HVM (Link)
  • Volume: GP3 mit 20 GiB oder mehr nach Belieben
  • Sicherheitsgruppe:
    • SSH, Port 22 auf eigene IP
    • HTTP, Port 80 von überall
    • HTTPS, Port 443 von überall

Als Nächstes wird eine feste IP-Adresse für die Instanz benötigt. Dazu lege ich eine IP-Adresse aus dem AWS Pool mit Elastic-IP an. Diese IP-Adresse wird dann der Instanz zugewiesen. Nun können die A-Records erstellt werden:

  • gitlab.example.com > soeben erstellte IP-Adresse
  • registry.gitlab.example.com > soeben erstellte IP-Adresse

Im Folgenden wird nun der Server vorbereitet und die benötigte Software installiert

Vorbereitung: Server einrichten

Tatsächlich wird gar nicht so viel benötigt, um GitLab ans Laufen zu bekommen. Erst mal steht der Connect auf den Server an:

 

$ ssh -A centos@gitlab.example.com

 

Update des Servers:

 

$ sudo dnf update

 

Anschließend werden benötigte Pakete installiert:

 

$ sudo dnf install -y git curl policycoreutils openssh-server perl

 

Für den GitLab Runner, welcher Docker Images lädt, wird noch Docker installiert:

 

$ sudo yum-config-manager \
    --add-repo \
    download.docker.com/linux/centos/docker-ce.repo


$ sudo dnf install docker-ce docker-ce-cli containerd.io

 

Dienst aktivieren:

 

$ sudo systemctl enable docker

 

und starten:

 

$ sudo service docker start

 

 

GitLab Installation durchführen

Auch die Installation von GitLab selbst ist mit ein paar wenigen Schritten erledigt. Ich verzichte hier auf zusätzliche Serverpakete wie Postfix oder Apache, da der Mailversand über den AWS SES von Amazon geleitet wird, und GitLab für alle Dienste NGINX als Webserver mitbringt, und der da auch perfekt konfiguriert ist.

Installieren des GitLab CE Repos:

 

$ curl packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash

 

Damit das SSL Zertifikat gleich erstellt wird, wird der Hostname des Servers vor der Installation via EXPORT gesetzt und erst dann installiert:

 

$ sudo EXTERNAL_URL="https://gitlab.example.com" dnf install -y gitlab-ce

 

Nach der Installation erscheint dann die Meldung, dass GitLab installiert und konfiguriert wurde:

 

gitlab Reconfigured!

       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.



     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/


Thank you for installing GitLab!
GitLab should be available at gitlab.example.com

For a comprehensive list of configuration options please see the Omnibus GitLab readme
gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md

Help us improve the installation experience, let us know how we did with a 1 minute survey:
gitlab.fra1.qualtrics.com/jfe/form/SV_6kVqZANThUQ1bZb


  Überprüfung läuft     : gitlab-ce-13.12.3-ce.0.el8.x86_64                                                                             1/1

Installiert:
  gitlab-ce-13.12.3-ce.0.el8.x86_64

Fertig.

 

 

Erster Log-In und Admin Account

Das GitLab ist jetzt unter der Domain https://gitlab.example.com erreichbar. Die URL kann und sollte jetzt im Browser geöffnet werden. Für den Admin Account wird das Passwort hinterlegt. Nach absenden des Formulars wird man zum Log-In weitergeleitet. Der Benutzername ist root, das Passwort ist das von gerade eben. Nach dem Log-In sollte der SSH-Key hinterlegt werden. Dieser wird zum Interagieren via SSH benötigt (Push, Pull etc).

Der öffentliche Schlüssel wird unter User Settings > SSH Keys hinterlegt. Erhalten kann man den Schlüssel mit cat ~/.ssh/id_rsa.pub oder man erzeugt einen neuen mit ssh-keygen.

Der GitLab ist jetzt bereits einsatzbereit. Neues Projekt erstellen, lokal ein git init und git push über SSH funktioniert ab diesem Zeitpunkt ohne Probleme. Ich werde den GitLab noch etwas von der Konfiguration her anpassen. 

AWS SES: SMTP Service für den Versand von E-Mails

Ich nutze absichtlich keine lokalen Postfix oder anderen E-Mailserver, da ohne die ganzen Pakete und die zusätzlich benötigte Konfiguration das System einfacher upzudaten ist, und entsprechend weniger Wartungsintensiv. Da diese Instanz sowieso nur für den GitLab Server genutzt wird, sollte es eine Überlegung Wert sein, GitLab E-Mails über einen SMTP Service zu versenden. Wer bereits einen SMTP Service nutzt, findet auf dieser Seite einige Konfigurationsbeispiele für verschiedene Anbieter. AWS SES ist ebenfalls enthalten. Dennoch zeige ich die Vorgehensweise hier auf.

In der Managementkonsole ist der Service unter dieser URL erreichbar. Neue Accounts sind bei SES immer in einer Sandboxumgebung, siehe das Bield. Durch den Button "Request production access" wird der Account dann freigegeben und die Limits werden erhöht. Zusätzlich muss man begründen, weshalb man den Dienst nutzt, und eine URL mitsenden, von wo aus E-Mails versendet werden sollen. In diesem Fall reicht ein Link zum frisch installierten GitLab mit dem Hinweis auf "Customer Support communication" oder ähnliches. Da die Anträge von Mitarbeitern geprüft werden, dauert es einige Stunden, bis der Account zur Verfügung steht. Auf dem Dashboard kann man SMTP Zugangsdaten erzeugen. Dies geschieht über eine IAM Rolle. Die Einrichtung ist sehr gut erklärt und dauert nur wenige Minuten.

Die Konfiguration für den SMTP Dienst wird in /etc/gitlab/gitlab.rb erledigt. Dort wird im Bereich der "Gitlab email server settings" folgende Konfigration hinterlegt:

 

gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "email-smtp.eu-central-1.amazonaws.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "IAM_USERNAME"
gitlab_rails['smtp_password'] = "IAM_PASSWORD"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true

gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_display_name'] = 'Gitlab on example.com'
gitlab_rails['gitlab_email_reply_to'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_subject_suffix'] = 'Gitlab notify'

 

Nach dem einfügen und speichern der Datei wird ein "reconfigure" ausgeführt:

 

sudo gitlab-ctl reconfigure

 

Testen, ob E-Mails versendet werden, geht nur über die Konsole, im Admin Bereich gibt es leider keine Möglichkeit. Die Konsole wird wie folgt gestartet:

 

sudo gitlab-rails console

 

Test E-Mail versenden:

 

Notify.test_email('eddi.doe@example.com', 'Message Subject', 'Message Body').deliver_now

 

Wird die Mail versendet, sollte die Konsole wie folgt aussehen:

 

$ sudo gitlab-rails console
--------------------------------------------------------------------------------
 Ruby:         ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
 GitLab:       13.12.3 (757327a59bc) FOSS
 GitLab Shell: 13.18.0
 PostgreSQL:   12.6
--------------------------------------------------------------------------------
Loading production environment (Rails 6.0.3.6)
irb(main):001:0> Notify.test_email('eddi.doe@example.com', 'Message Subject', 'Message Body').deliver_now
Notify#test_email: processed outbound mail in 1.2ms
Delivered mail 60c60b9b39bf5_16e265a28967cc@ip-172-31-9-223.eu-central-1.compute.internal.mail (342.1ms)
Date: Sun, 13 Jun 2021 13:43:55 +0000
From: GitLab <gitlab@gitlab.example.com>
Reply-To: GitLab <noreply@gitlab.example.com>
To: eddi.doe@example.com
Message-ID: <60c60b9b39bf5_16e265a28967cc@ip-172-31-9-223.eu-central-1.compute.internal.mail>
Subject: Message Subject
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit
Auto-Submitted: auto-generated
X-Auto-Response-Suppress: All

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Message Body</p></body></html>

=> #<Mail::Message:197560, Multipart: false, Headers: <Date: Sun, 13 Jun 2021 13:43:55 +0000>, <From: GitLab <gitlab@gitlab.example.com>>, <Reply-To: GitLab <noreply@gitlab@example.com>>, <To: eedi.doe@example.com>, <Message-ID: <60c60b9b39bf5_16e265a28967cc@ip-172-31-9-223.eu-central-1.compute.internal.mail>>, <Subject: Message Subject>, <Mime-Version: 1.0>, <Content-Type: text/html; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>, <Auto-Submitted: auto-generated>, <X-Auto-Response-Suppress: All>>
irb(main):002:0>

 

 

Eingehende E-Mails: Sub-Addressing für Issues

Ein anderes interessantes Thema sind die eingehenden Mails auf den GitLab Server. Wird dem GitLab ein Postfach zugewiesen, dass Sub-Addressing kann, können auf Tickets, die der GitLab versendet, geantwortet werden. Die Antworten werden automatisch an das Ticket als Kommentar angehängt. Viele Dienste unterstützen das Sub-Addressing, unter anderem Gmail, Google Apps, Yahoo! Mail, Outlook.com und iCloud. Der vermutlich wichtigste unterstützt per Default nicht: Microsoft Exchange Server. Aber auch da gibt es eine Möglichkeit, über die PowerShell kann das Feature für Exchange aktiviert werden. Um die E-Mails in GitLab empfangen zu können, wird der Exchange Server via Microsoft Graph abgefragt. Eine Anleitung zur Einrichtung dazu findet sich auf gitlab.com.

GitLab Runner: Docker-Images für GitLab CI nutzen

Das nächste Feature ist das Einrichten eines Gitlab Runner. Der GitLab Runner in diesem Artikel ist vom Typ Docker. Damit lassen sich von Dockerhub Images laden und für die Pipeline nutzen, um Beispielsweise Code zu testen, Frontend Builds laufen zu lassen oder um Deployments durchzuführen. Der Vorteil des Docker Executors gegenüber dem Shell Executor ist deutlich: Auf dem GitLab Host muss nichts installiert werden, was in der Pipeline eventuell benötigt wird. Runner erstelle ich in der Regel immer Gruppenbezogen. Um einen Runner einrichten zu können, muss gitlab-runner zunächst installiert werden:

 

#
$ curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash

#
$ sudo dnf install gitlab-runner

#
$ sudo gitlab-runner start

 

 

Nun wird der eigentliche Runner erstellt. Auf dem Server läuft nun zwar gitlab-runner, aber das ist lediglich der Service, der nachher die Runner startet, die in der Pipeline angefordert werden. Runner lassen sich somit Projektbezogen oder Gruppenbezogen erstellen oder sharen. Um einen Runner zu registrieren, wird die Gruppe in GitLab geöffnet, danach auf "Settings" > "CI/CD" wie im Screen. Dort wird "Runners" aufgeklappt, und GitLab zeigt einen registration Token an. Dieser Token wird zur Registrierung genutzt. Auf der Konsole des GitLab schaut eine Registrierung dann so aus:

 

$ sudo gitlab-runner register --url gitlab.example.com --registration-token QkbEKxiWMsDzsFcgAUf4


Enter the GitLab instance URL (for example, gitlab.com/):
[https://gitlab.example.com/]:
Enter the registration token:
[QkbEKxiWMsDzsFcgAUf4]:
Enter a description for the runner:
[ip-172-12-24-221.eu-central-1.compute.internal]: docker-runner
Enter tags for the runner (comma-separated):
docker
Registering runner... succeeded                     runner= QkbEKxiW
Enter an executor: docker-ssh+machine, kubernetes, custom, ssh, parallels, shell, virtualbox, docker+machine, docker, docker-ssh:
docker
Enter the default Docker image (for example, ruby:2.6):
ubuntu:latest
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

 

Die wichtigsten Eingaben sind:

  • Executor: docker
  • tags: docker

Das mit dem Tag ist für nachher wichtig, wenn der Runner in einer Pipeline aufgerufen wird. 

Damit der Runner nun startet, reicht es aus, beim nächsten mal pushen in den Master die folgende gitlab.ci.yaml im Root des Projektes anzulegen:

 

stages:
  - deploy
release production:
  stage: deploy
  image: "ubuntu:latest"
  script:
    - echo "Hello World"
  tags:
    - docker
  only:
    - master

 

Als Tag wird docker angegeben, damit ist bekannt, dass hier der Runner gemeint ist, der vorhin erstellt wurde. Die Ausgabe des Jobs sollte dann wie im Bild zu sehen ist, erfolgen.

Container Registry: Ein eigenes Dockerhub via GitLab

Gerade wenn Projekte eigene Docker Images benötigen, oder die GitLab Pipeline auf ein eigenes Image aufbaut, ergibt es Sinn, eine eigene Container-Registry zu nutzen. GitLab stellt dafür entsprechende Funktionen bereit. Am einfachsten ist die Konfiguration, wenn die Registry unter einer eigenen Domain läuft. In diesem Fall wird die Registry unter registry.gitlab.example.com veröffentlicht. Dazu ist eine kleine Konfiguration in /etc/gitlab/gitlab.rb nötig, und zwar im Bereich der "Container registry settings":

 

registry_external_url 'https://registry.gitlab.example.com'

 

Danach ein reconfigure des GitLab:

 

sudo gitlab-ctl reconfigure

 

Damit jetzt ein Docker Image in die Registry des Projekts kann, hier ein einfaches Dockerfile für einen ersten Test:

 

FROM centos:8
RUN dnf install -y dnf-utils rpms.remirepo.net/enterprise/remi-release-8.rpm
RUN dnf module reset php
RUN dnf module enable php:remi-7.4 -y
RUN dnf module reset nodejs
RUN dnf module enable nodejs:14 -y
RUN dnf install -y openssh-clients php php-gd php-xml php-mbstring php-curl php-zip wget unzip git gcc-c++ make nodejs rsync libxml2
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php composer-setup.php --install-dir=/usr/local/bin --filename=composer

 

Das docker Image wird mit folgendem Befehl gebaut (Den Befehl gibt es in GitLab unter "Packages & Registries" > "Container Registry"):

 

$ docker build -t registry.gitlab.example.com/test/test .

 

Ist das Image gebaut, kann es in das Registry geladen werden. Zuerst anmelden via SSH:

 

# manfredrutschmann @ MacPro in ~/ddev/gitlab.example.com on git:master x [20:09:01] C:1
$ docker login registry.gitlab.example.com
Username: root
Password:
Login Succeeded

 

Danach das Image pushen:

 

# manfredrutschmann @ MacPro in ~/ddev/gitlab.example.com on git:master x [20:10:41]
$ docker push registry.gitlab.example.com/test/test
Using default tag: latest
The push refers to repository [registry.gitlab.example.com/test/test]
2fc28a350b93: Pushed
3ca5ad08d39d: Pushed
3cb2ac62d60b: Pushed
53ed9eaa6d25: Pushed
b95224d44e7c: Pushed
b3c655eca3c0: Pushed
f729d633f733: Pushed
9845aa05ad48: Pushed
2653d992f4ef: Pushed
latest: digest: sha256:d29e59de6c62229de2cb32b58a116e534d1e941bc6b74ed3a26ae54d2a9a0186 size: 2218

 

 

Ist das alles erledigt, kann das Image in GitLab entsprechend gesehen werden. Pull oder verwenden in einer GitLab PipeLine ist ohne Probleme möglich. Beim Zugriff auf die Registry gilt immer der Benutzerlevel. 

Package Registry: Ein eigenes Packagist via GitLab

Oftmals werden in mehreren Projekten dieselben Pakete benötigt, sie sollen aber privat bleiben, da sie Projektbezogen sind. Man könnte via Composer nun mit VCS Repositories oder Git Submodulen arbeiten, allerdings finde ich beide Varianten nicht sehr schön. Mit der Package Registry lassen sich aber Composer Pakete taggen und über den eigenen GitLab Server wieder in Projekte einbinden, ähnlich wie das mittels Require von Packagist gemacht wird. Das ist dann natürlich besonders praktikabel. Für meine Projekte bezieht sich das in der Regel auf TYPO3 Extensions.

Bevor es losgehen kann, wird im Projekt ein entsprechender Shell Runner registriert, da das Erstellen des Package beim Commit mit einem GitLab Runner via gitlab.ci.yaml durchgeführt wird:

 

$ sudo gitlab-runner register --url gitlab.example.com --registration-token sbfWSkQowTTwMNuorRnN


Enter the GitLab instance URL (for example, gitlab.com/):
[https://gitlab.example.com/]:
Enter the registration token:
[sbfWSkQowTTwMNuorRnN]:
Enter a description for the runner:
[ip-172-31-9-223.eu-central-1.compute.internal]: package-registry
Enter tags for the runner (comma-separated):

Registering runner... succeeded                     runner=sbfWSkQo
Enter an executor: custom, docker, docker-ssh, shell, docker+machine, docker-ssh+machine, kubernetes, parallels, ssh, virtualbox:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

 

Der Runner Executor ist diesmal "Shell".

Damit das Image gebaut wird, wird das Package in ein eigenes Projekt gepusht. Dabei wird die folgende CI Yaml verwendet:

 

# This file is a template, and might need editing before it works on your project.
# Publishes a tag/branch to Composer Packages of the current project
publish:
  image: curlimages/curl:latest
  stage: build
  variables:
    URL: "$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN"
  script:
    - version=$([[ -z "$CI_COMMIT_TAG" ]] && echo "branch=$CI_COMMIT_REF_NAME" || echo "tag=$CI_COMMIT_TAG")
    - insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
    - response=$(curl -s -w "\n%{http_code}" $insecure --data $version $URL)
    - code=$(echo "$response" | tail -n 1)
    - body=$(echo "$response" | head -n 1)
    # Output state information
    - if [ $code -eq 201 ]; then
        echo "Package created - Code $code - $body";
      else
        echo "Could not create package - Code $code - $body";
        exit 1;
      fi

 

 

Nach dem Pushen des Packages in GitLab, baut GitLab automatisch ein Package daraus. Via Git können Versions-Tags vergeben werden, auf die später mit Composer zugegriffen werden können:

composer req some/package ^1.0

Damit Composer das Package findet, wird im Node "repositories" der composer.json das Package Registry angegeben:

 

{ "type": "composer", "url": "https://gitlab.example.com/api/v4/group/1/-/packages/composer/packages.json"}

 

wobei die Zahl nach "Group" die ID der Gruppe ist, in der sich das Paket befindet. Ob Zugriff auf das Paket besteht, wird mit dem SSH Key geprüft. 

Viel Erfolg beim einrichten!