GitLab CI/CD ist eine Plattform für Continuous Integration und Delivery, die direkt in GitLab integriert ist. Anders als externe CI-Tools, die separate Konfigurationen und Webhooks benötigen, liest GitLab CI/CD eine einzige .gitlab-ci.yml-Datei aus dem Repository und führt Pipelines automatisch bei jedem Push aus. Diese enge Integration bedeutet, dass Code, Issues, Merge Requests und Deployment-Pipelines an einem einzigen Ort zusammenleben.
Diese Anleitung führt Schritt für Schritt durch die Pipeline-Konfiguration — Stages, Jobs, Runner, Caching, Artefakte, Variablen, Environments und Deployment-Strategien, die in der Produktion funktionieren.
Voraussetzungen
- Ein GitLab-Account (gitlab.com oder selbst gehostete Instanz)
- Ein auf GitLab gehostetes Git-Repository
- Grundkenntnisse der YAML-Syntax
- Docker installiert (für Runner mit Docker-Executor)
- Vertrautheit mit CI/CD-Konzepten (Build, Test, Deploy)
Die Struktur einer GitLab CI/CD Pipeline verstehen
Eine Pipeline besteht aus Stages, die sequenziell ablaufen, und Jobs innerhalb jeder Stage, die parallel ausgeführt werden. Hier ist die grundlegende Struktur:
# .gitlab-ci.yml
stages:
- build
- test
- deploy
build-app:
stage: build
image: node:20
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
unit-tests:
stage: test
image: node:20
script:
- npm ci
- npm run test:unit
coverage: '/Lines\s+:\s+(\d+\.?\d*)%/'
integration-tests:
stage: test
image: node:20
services:
- postgres:15
variables:
POSTGRES_DB: test_db
POSTGRES_USER: runner
POSTGRES_PASSWORD: testing
DATABASE_URL: "postgresql://runner:testing@postgres:5432/test_db"
script:
- npm ci
- npm run test:integration
deploy-staging:
stage: deploy
script:
- ./scripts/deploy.sh staging
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
Die build-Stage läuft zuerst. Wenn sie erfolgreich ist, werden beide Test-Jobs parallel ausgeführt (sie befinden sich in derselben Stage). Erst wenn alle Tests bestanden haben, wird das Deployment fortgesetzt.
GitLab Runner konfigurieren
Runner führen die Pipeline-Jobs aus. Es gibt zwei Möglichkeiten:
Shared Runners (Schnellstart)
GitLab.com stellt Shared Runners kostenlos bereit. Sie sind sofort verfügbar — kein Setup erforderlich. Navigiere zu Settings → CI/CD → Runners, um zu prüfen, ob sie aktiviert sind.
Self-Hosted Runner (Produktion)
Für bessere Performance und Kontrolle kannst du einen eigenen Runner installieren:
# GitLab Runner auf Ubuntu installieren
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner
# Den Runner registrieren
sudo gitlab-runner register \
--url "https://gitlab.com/" \
--registration-token "YOUR_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "production-runner" \
--tag-list "docker,linux" \
--run-untagged="true"
Vergleich der Runner-Executors
| Executor | Isolation | Geschwindigkeit | Anwendungsfall |
|---|---|---|---|
| Docker | Hoch (Container pro Job) | Schnell | Standardwahl für die meisten Projekte |
| Shell | Keine (läuft auf dem Host) | Am schnellsten | Einfache Skripte, Legacy-Anwendungen |
| Docker Machine | Hoch + Autoscaling | Variabel | Cloud-basierte Autoscaling-Runner |
| Kubernetes | Hoch (Pod pro Job) | Mittel | Kubernetes-native Deployments |
| VirtualBox | Vollständige VM | Langsam | Wenn vollständige OS-Isolation benötigt wird |
Erweiterte Pipeline-Konfiguration
Abhängigkeiten cachen
Caching verhindert, dass Abhängigkeiten bei jedem Pipeline-Lauf neu heruntergeladen werden:
variables:
NPM_CONFIG_CACHE: "$CI_PROJECT_DIR/.npm"
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
policy: pull-push
Die key.files-Direktive generiert einen Cache-Schlüssel aus der package-lock.json. Wenn sich Abhängigkeiten ändern, wird der Cache automatisch invalidiert.
Artefakte zwischen Stages
Build-Ergebnisse von einer Stage an die nächste weitergeben:
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
- coverage/
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 1 week
Pipeline-Rules und bedingte Ausführung
Mit rules lässt sich steuern, wann Jobs ausgeführt werden:
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
when: manual
- when: never
Dieser Job erscheint nur bei Versions-Tags (v1.2.3) und erfordert eine manuelle Genehmigung.
Parallele Matrix-Jobs
Gleichzeitig über mehrere Versionen testen:
test:
stage: test
image: node:${NODE_VERSION}
parallel:
matrix:
- NODE_VERSION: ["18", "20", "22"]
DATABASE: ["postgres", "mysql"]
script:
- npm ci
- npm run test
Das erzeugt 6 parallele Jobs (3 Node-Versionen × 2 Datenbanken).
CI/CD-Variablen und Secrets
Sensible Werte sicher speichern:
deploy:
stage: deploy
script:
- echo "$DEPLOY_KEY" > key.pem
- chmod 600 key.pem
- scp -i key.pem dist/* user@server:/var/www/
after_script:
- rm -f key.pem
Praxisbeispiel: Dein Team deployt eine Node.js-Anwendung in drei Environments — Development, Staging und Production. Jedes Environment hat unterschiedliche Datenbank-Zugangsdaten, API-Schlüssel und Server-Adressen. Diese werden als GitLab CI/CD-Variablen gespeichert, die dem jeweiligen Environment zugeordnet sind. Dieselbe Pipeline-YAML verwaltet alle Deployments; nur die Variablen unterscheiden sich. Wenn ein Entwickler auf develop pusht, deployt die Pipeline automatisch auf Staging. Das Production-Deployment wird nur bei Tags mit manueller Genehmigung durch einen Team-Lead ausgelöst.
Variablentypen und Scoping
| Scope | Eingestellt in | Verfügbar für |
|---|---|---|
| Project | Settings → CI/CD → Variables | Alle Pipelines im Projekt |
| Group | Group → Settings → CI/CD | Alle Projekte in der Gruppe |
| Instance | Admin → CI/CD → Variables | Alle Projekte der Instanz |
| Environment | Settings → CI/CD → Variables (Env-Filter) | Jobs, die auf dieses Environment abzielen |
| Job | variables: in .gitlab-ci.yml | Nur dieser spezifische Job |
Markiere Variablen als Protected (nur auf geschützten Branches/Tags verfügbar) und Masked (in Job-Logs verborgen) für Zugangsdaten.
Deployment-Environments und Strategien
Review Apps (Dynamische Environments)
Für jeden Merge Request eine temporäre Umgebung erstellen:
review:
stage: deploy
script:
- deploy_review_app "$CI_MERGE_REQUEST_IID"
environment:
name: review/$CI_MERGE_REQUEST_IID
url: https://$CI_MERGE_REQUEST_IID.review.example.com
on_stop: stop-review
rules:
- if: $CI_MERGE_REQUEST_IID
stop-review:
stage: deploy
script:
- teardown_review_app "$CI_MERGE_REQUEST_IID"
environment:
name: review/$CI_MERGE_REQUEST_IID
action: stop
rules:
- if: $CI_MERGE_REQUEST_IID
when: manual
Production-Deployment mit Rollback
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
auto_stop_in: never
rules:
- if: $CI_COMMIT_TAG
when: manual
allow_failure: false
GitLab CI/CD im Vergleich mit anderen CI-Plattformen
| Feature | GitLab CI/CD | GitHub Actions | Jenkins | CircleCI |
|---|---|---|---|---|
| Konfigurationsdatei | .gitlab-ci.yml | .github/workflows/*.yml | Jenkinsfile | .circleci/config.yml |
| Integrierte Registry | Ja (Container + Package) | Ja (Container) | Nein | Nein |
| Self-hosted Option | Ja (vollständiges GitLab) | Ja (nur Runner) | Ja | Nein |
| Review Apps | Nativ | Manuelles Setup | Manuell | Manuell |
| DAG Pipelines | Ja (needs-Schlüsselwort) | Ja (nativ) | Ja | Ja |
| Auto DevOps | Ja | Nein | Nein | Nein |
| Kostenlose CI-Minuten | 400 Min./Monat | 2.000 Min./Monat | Unbegrenzt (self-hosted) | 6.000 Min./Monat |
| Lernkurve | Mittel | Niedrig | Hoch | Niedrig |
GitLab CI/CD glänzt, wenn alles integriert sein soll — Code, CI, Container-Registry, Environments und Monitoring auf einer Plattform. GitHub Actions punktet mit der Breite seines Marketplace-Ökosystems.
Fallstricke und Sonderfälle
-
YAML-Anchors funktionieren nicht mit
include: Wenn YAML-Anchors (&anchor/*anchor) in eingebundenen Dateien verwendet werden, lösen sie sich nicht über Dateigrenzen hinweg auf. Verwende stattdessen dasextends-Schlüsselwort. -
Cache ist nicht garantiert: GitLab-Caches sind eine Best-Effort-Funktion. Runner teilen sich möglicherweise keinen Cache. Entwirf Pipelines immer so, dass sie auch ohne Cache funktionieren (nur langsamer).
-
Services-Netzwerk: Service-Container (wie
postgres:15) sind über ihren Image-Namen als Hostname erreichbar —postgres, nichtlocalhost. Das verwirrt viele Entwickler. -
only/exceptvsrules: Die veralteteonly/except-Syntax ist nicht mitruleskompatibel. Niemals beides im selben Job mischen. Bevorzugerules— es ist mächtiger und der empfohlene Ansatz. -
Job-Timeout standardmäßig 1 Stunde: Lang laufende Jobs wie E2E-Tests oder große Builds schlagen am Timeout stillschweigend fehl. Setze
timeout: 2hexplizit für bekannte lang laufende Jobs. -
Variablen für geschützte Branches: Als “Protected” markierte Variablen sind auf Feature-Branches nicht verfügbar. Wenn ein Job Zugangsdaten auf nicht geschützten Branches benötigt, verwende eine separate Variable ohne das Protected-Flag.
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Pipeline bleibt bei “Pending” hängen | Keine verfügbaren Runner mit passenden Tags | Runner-Tags prüfen oder tags: [] setzen, um beliebige Runner zu verwenden |
| ”Cache not found” beim ersten Lauf | Cache wurde noch nicht erstellt | Erster Lauf lädt alles herunter; Cache wird für den nächsten Lauf befüllt |
| Services nicht erreichbar | Falscher Hostname im Connection String | Den Image-Namen des Services als Hostname verwenden (z. B. postgres, nicht localhost) |
| “Variable is not set” in Jobs | Variable ist geschützt, Branch ist es nicht | ”Protected” deaktivieren oder auf einen geschützten Branch pushen |
| Artefakte in der nächsten Stage fehlen | artifacts.paths stimmt nicht mit der Ausgabe überein | Genaue Pfade prüfen; ls -la im Skript zur Fehlersuche verwenden |
Zusammenfassung
- GitLab CI/CD liest
.gitlab-ci.ymlaus dem Repository und führt Pipelines automatisch aus — keine externe Tool-Konfiguration nötig - Stages laufen sequenziell, während Jobs innerhalb einer Stage parallel ausgeführt werden — das gibt sowohl Reihenfolge als auch Geschwindigkeit
- Self-hosted Runner mit Docker-Executor bieten die beste Performance und Kontrolle für Produktionsteams
- Caching von Abhängigkeiten und die Weitergabe von Artefakten zwischen Stages reduziert die Pipeline-Laufzeit erheblich
- Umgebungsvariablen mit Maskierung, Schutz und Environment-Scoping halten Secrets über alle Deployments hinweg sicher
- Review Apps erstellen temporäre Environments pro Merge Request und ermöglichen so eine visuelle Überprüfung vor dem Merge