theHacker's Blog
– It's just a glitch in the Matrix –

Docker-Anwendung mit docker-compose und GitLab-CI automatisch bauen

Ich bin gerade dabei, probeweise meine HotS-Team-Webseite mit Docker zu bauen. Immer, wenn Blizzard einen neuen Helden ins Spiel bringt, muss ich ein paar (viele) Handgriffe machen und diese erst lokal ausprobieren und schließlich auf meinem Produktivserver erneut ausführen. Als Informatiker bin ich faul und möchte diesen Vorgang automatiseren. Hier kommt Docker ins Spiel…

In diesem Artikel zeige ich euch, wie ihr mit Docker und GitLab eine Anwendung bauen könnt.

Installation eines Runners

Als ersten Schritt richte ich einen neuen Server mit Ubuntu 18.04 ein. Ich installiere dort einen GitLab-Runner und verwende den Shell-Executor.

In diesem Artikel könnt ihr Details finden, wie man einen GitLab-Runner installiert.

Warum den Shell-Executor? Im obig erwähnten Artikel steht drin, dass der Shell-Executor viele Nachteile hat und der Docker-Executor doch besser wäre. Das ist erstmal richtig. Gucken wir uns aber einmal an, was wir mit diesem Runner machen möchten:

Ziel ist es, die Befehle docker-compose bzw. docker zu verwenden. Das klappt mit dem Docker-Executor nicht. Man kann einen Docker-Daemon nicht so einfach in Docker laufen lassen. Ihr werdet im Internet Anleitungen finden, wie es mit dem --privileged-Flag klappt, aber das möchte ich nicht. (Es erlaubt dem Container auszubrechen!)

Mit dem Shell-Executor haben wir dieses Problem nicht.

Ich registriere meinen neuen GitLab-Runner am GitLab und weise ihm zwei Tags zu: shell (um zu markieren, dass dies ein Shell-Executor ist) und docker-daemon (um zu markieren, dass hier ein Docker-Daemon läuft). Damit kann ich später in meinen Projekten für die jeweiligen Jobs speziell diesen Runner auswählen.

Installation von Docker und docker-compose

Als root installiere ich eine aktuelle Version von docker-compose gemäß der Anleitung auf docs.docker.com:

curl -L "https://github.com/docker/compose/releases/download/1.25.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Docker selber installiere ich aus den Ubuntu-Paketquellen:

apt install docker.io

Zum Schluss kurz prüfen, ob Docker läuft:

root@shell-gitlab-runner:~# docker version
Client:
 Version:           18.09.7
 API version:       1.39
 Go version:        go1.10.1
 Git commit:        2d0083d
 Built:             Fri Aug 16 14:20:06 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.09.7
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.1
  Git commit:       2d0083d
  Built:            Wed Aug 14 19:41:23 2019
  OS/Arch:          linux/amd64
  Experimental:     false

Build mit GitLab-CI durchführen

In meinem Projekt habe ich eine docker-compose.yml angelegt, auf die ich jetzt nicht weiter eingehe. Mit dem docker-compose-Tool und dieser Datei kann ich meine Anwendung mit Docker verwenden.

Ich lege nun eine Datei .gitlab-ci.yml im Stammverzeichnis meines Projekts mit folgendem Inhalt an:

stages:
  - build

build:
  stage: build
  tags:
    - shell
    - docker-daemon
  script:
    - docker-compose build

Die Anweisungen in dieser Datei sind relativ einfach:

  • Der Bereich stages definiert nur eine Stage mit dem Namen build.
  • Es wird ein einziger Job mit dem Namen build definiert, der innerhalb der Stage build abläuft.
  • Wir fordern vom GitLab, dass es einen Runner zuteilt, der die Tags shell und docker-daemon besitzt. Damit selektieren wir unseren eben installierten Runner.
  • Im Bereich script wird eine einzige Befehlszeile angegeben, die der Runner ausführen soll: docker-compose build. Dies baut mir meine Anwendung in Form von Docker-Images.

Beim nächsten Push wird GitLab die .gitlab-ci.yml erkennen und im Bereich „CI / CD“ unter „Pipelines“ eine Pipeline anlegen und ausführen.

Versuch 1: Couldn’t connect to Docker daemon at http+docker://localhost – is it running?

Ich gucke in mein GitLab… der Job läuft… und scheitert aber 🙁

$ docker-compose build
Couldn't connect to Docker daemon at http+docker://localhost - is it running?
If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
ERROR: Job failed: exit status 1

Wir bekommen keine Verbindung zum Daemon. Doch vorhin hab ich extra kontrolliert, dass Docker läuft!? Es kann sich also nur um ein Permission-Problem handeln.

Problem untersuchen und lösen

Um das Problem zu lösen, logge ich mich auf dem Runner per SSH ein. Wichtig ist nun, die Schritte genau so durchzuführen, wie es auch der GitLab-Runner selber tun würde. Aus dem Log des Jobs erfahre ich das Arbeitsverzeichnis, in dem Job ausgeführt wurde. In das wechsle ich hinein und sehe mich um:

cd /home/gitlab-runner/builds/GR1f_yAH/0/thehacker/hots-team.com
ll

Ich sehe, dass alle Dateien dem Nutzer gitlab-runner gehören. Also wechsle ich in diesen Nutzer, um dieselben Rechte wie der Runner zu haben und probiere die nötigen Befehle aus:

root@shell-gitlab-runner:/home/gitlab-runner/builds/GR1f_yAH/0/thehacker/hots-team.com# su gitlab-runner
gitlab-runner@shell-gitlab-runner:~/builds/GR1f_yAH/0/thehacker/hots-team.com$ docker-compose build
ERROR: Couldn't connect to Docker daemon at http+docker://localhost - is it running?

If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
gitlab-runner@shell-gitlab-runner:~/builds/GR1f_yAH/0/thehacker/hots-team.com$ docker version
Client:
 Version:           18.09.7
 API version:       1.39
 Go version:        go1.10.1
 Git commit:        2d0083d
 Built:             Fri Aug 16 14:20:06 2019
 OS/Arch:           linux/amd64
 Experimental:      false
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.39/version: dial unix /var/run/docker.sock: connect: permission denied

Man erkennt, dass der gitlab-runner nicht die nötigen Rechte hat.

Wir lösen das Problem, indem wir dem gitlab-runner-Nutzer der Gruppe docker zuweisen:

gitlab-runner@shell-gitlab-runner:~/builds/GR1f_yAH/0/thehacker/hots-team.com$ exit
root@shell-gitlab-runner:/home/gitlab-runner/builds/GR1f_yAH/0/thehacker/hots-team.com# usermod -aG docker gitlab-runner 
root@shell-gitlab-runner:/home/gitlab-runner/builds/GR1f_yAH/0/thehacker/hots-team.com# su gitlab-runner
gitlab-runner@shell-gitlab-runner:~/builds/GR1f_yAH/0/thehacker/hots-team.com$ docker version
Client:
 Version:           18.09.7
 API version:       1.39
 Go version:        go1.10.1
 Git commit:        2d0083d
 Built:             Fri Aug 16 14:20:06 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.09.7
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.1
  Git commit:       2d0083d
  Built:            Wed Aug 14 19:41:23 2019
  OS/Arch:          linux/amd64
  Experimental:     false

Wir erhalten nun eine Antwort vom Docker-Daemon 🙂

Versuch 2: Job succeeded

Da wir keine Änderungen am Quellcode selber gemacht haben, können wir im GitLab bei dem fehlgeschlagenem Job einfach auf den „Wiederholen“-Button klicken, um den Job erneut laufen zu lassen.

Diesmal klappt alles.

Fazit

Ich habe damit auf dem GitLab-Runner meine Docker-Images für meine Anwendung gebaut, die ich jetzt auf einen beliebigen Server pushen und dort ausführen lassen kann.

Dies wird Gegenstand eines weiteren Artikel sein.

Schreib einen Kommentar hierzu

Deine eMail-Adresse wird nicht veröffentlicht.