Theoretische Grundlagen
Was sind Continuous Integration und Continuous Deployment?
Continuous Integration (CI) und Continuous Deployment (CD) sind Kernkonzepte moderner Softwareentwicklung.
Bei CI werden Codeänderungen regelmäßig in ein zentrales Repository integriert und durch automatisierte Builds und Tests sofort überprüft. Diese Abfolge automatisierter Schritte wird als Pipeline bezeichnet. Neben Tests lassen sich in einer CI-Pipeline auch weitere automatisierte Prüfungen integrieren, wie in unserem Versuch das Filtern nach Passwörtern oder bestimmten Wörtern im Code – ebenso könnten beispielsweise Sicherheitsanalysen oder Formatierungschecks automatisch durchgeführt werden. CI ermöglicht eine frühzeitige Fehlererkennung und fördert kurze, stabile Entwicklungszyklen.
CD erweitert dieses Prinzip, indem nach erfolgreichem Build und Test die Software ohne manuelle Eingriffe in die Zielumgebung bzw. produktive Umgebung überführt wird. Dabei kann es sich in der Praxis zum Beispiel, wie in unserem Versuch, um die automatische Aktualisierung dieses Berichts auf unserer RZ-Website, das Ausrollen einer Webanwendung oder das Aktualisieren eines Cloud-Dienstes handeln.
Beispiel einer CI/CD-Pipeline GitHub DevOps – CI/CD
Das Schaubild zeigt den Ablauf einer CI/CD-Pipeline. Nach der Planung und Implementierung wird der Code in der Phase der Continuous Integration automatisch gebaut, getestet und als lauffähiges Artefakt bereitgestellt. Darauf folgt Continuous Delivery, bei der das Artefakt zunächst in einer Staging-Umgebung getestet und anschließend in die Produktionsumgebung überführt wird.
Eine Pipeline besteht aus mehreren Stages, die nacheinander oder parallel ausgeführt werden. Typische Stages sind Build, Test und Deploy. Innerhalb jeder Stage befinden sich einzelne Jobs, die spezifische Aufgaben ausführen. Sobald ein Job fehlschlägt, wird die Pipeline gestoppt. So lässt sich der Zustand des Projekts in Echtzeit überwachen.
Darüber hinaus lässt sich die Pipeline allgemein durch zusätzliche Verarbeitungsschritte flexibel erweitern, wie auch unser Versuch zeigt.
Architektur von GitLab CI/CD
GitLab CI/CD ist direkt in GitLab integriert, wobei die Konfiguration über eine .gitlab-ci.yml
-Datei im Projektverzeichnis erfolgt, in der Pipelines mit ihren Stages und Jobs definiert werden. Runners führen die definierten Jobs auf dedizierten Maschinen oder in Containern aus.
Kernbestandteile der Architektur sind:
- Repository: Git-Projekt mit Quellcode und Konfigurationsdateien
- Pipeline: Eine vollständige Abfolge von Verarbeitungsschritten, ausgelöst durch Ereignisse wie Push oder Merge
- Runner: Externe Komponenten, die Jobs physisch ausführen
- API: Regelt die Kommunikation zwischen GitLab und den Runners
Die Kommunikation zwischen GitLab und den Runners erfolgt über eine gesicherte HTTPS-Verbindung und basiert auf einem tokenbasierten Authentifizierungsverfahren. Runners werden einmalig mit einem Projekt- oder Instanz-Token am GitLab-Server registriert. Danach arbeiten sie nach dem sogenannten Pull-Prinzip: Sie fragen in regelmäßigen Abständen beim Server an, ob neue Jobs vorliegen. Diese Architektur entkoppelt die Jobausführung vom GitLab-System selbst und ermöglicht eine flexible Skalierung – z. B. über lokale Server, virtuelle Maschinen oder Container. Über die API werden nicht nur Jobs übermittelt, sondern auch Artefakte, Logdaten und Testergebnisse zurück an GitLab gesendet, wodurch eine vollständige und automatisierte Rückverfolgung aller Abläufe innerhalb der Pipeline gewährleistet ist.
Aufbau einer Pipeline
Eine Pipeline beschreibt eine vollständige Kette automatisierter Schritte innerhalb eines GitLab-Projekts. Ein Repository kann mehrere Pipelines enthalten – etwa für unterschiedliche Branches, Umgebungen oder Trigger.
Die Struktur einer GitLab CI/CD-Pipeline wird über eine YAML-Datei namens .gitlab-ci.yml
im Wurzelverzeichnis eines Git-Repositories definiert. Diese Datei bildet das Herzstück der Automatisierung und beschreibt, welche Schritte bei bestimmten Ereignissen – wie z. B. einem Push oder Merge – automatisch ausgeführt werden sollen.
Struktur einer Pipeline
Beispielhafte Struktur einer GitLab CI/CD-Pipeline
- Repository
-
Pipeline: push auf
main
- build-Stage
- Job 1: Installiere Abhängigkeiten (
npm install
) - Job 2: Baue Anwendung (
npm run build
)
- Job 1: Installiere Abhängigkeiten (
- test-Stage
- Job 1: Führe Unit-Tests aus (
npm test
)
- Job 1: Führe Unit-Tests aus (
- deploy-Stage
- Job 1: Deployment auf Produktionsserver
- build-Stage
-
Pipeline: push auf
develop
- build-Stage
- Job 1: Installiere Abhängigkeiten
- test-Stage
- Job 1: Linter ausführen
- Job 2: Führe Unit-Tests aus (
npm test
)
- preview-Stage
- Job 1: Deployment in Testumgebung
- Job 2: Lade Artefakte hoch (
scp dist/
) - Job 3: Benachrichtige Team via Slack
- build-Stage
-
Ein Repository kann mehrere Pipelines enthalten, die jeweils durch unterschiedliche Ereignisse wie Branch-Pushes, Merge Requests oder Zeitpläne ausgelöst werden. Jede Pipeline besteht aus eigenen Stages und Jobs, die unabhängig voneinander ausgeführt werden. Aufbau und Benennung dieser Elemente sind flexibel und lassen sich kontextabhängig anpassen.
YAML-Konfiguration
Die Definition der Pipeline erfolgt in der .gitlab-ci.yml
. Dabei handelt es sich um ein deklaratives, einrückungsbasiertes Format. Innerhalb dieser Datei werden unter anderem Stages, Jobs, Regeln, Bedingungen und Variablen definiert.
Ein einfaches Beispiel:
stages: - build - test
build_job: stage: build script: - echo "Baue Anwendung"
test_job: stage: test script: - echo "Führe Tests aus"
Stages
Stages gruppieren Jobs nach Aufgabenbereichen. Sie werden in der definierten Reihenfolge oder parallel ausgeführt und dienen der strukturellen Gliederung der Pipeline.
Stage | Funktion |
---|---|
check | Überprüfung des Codes (z. B. Linting) |
build | Kompilierung / Erstellung von Artefakten |
test | Durchführung automatisierter Tests |
deploy | Bereitstellung in Zielumgebungen |
Jobs
Jobs sind die kleinste ausführbare Einheit innerhalb einer Pipeline. Jeder Job erfüllt eine spezifische Aufgabe, wie das Ausführen eines Tests oder das Erstellen eines Artefakts. Jobs innerhalb einer Stage können parallel ausgeführt werden, Jobs über mehrere Stages hinweg folgen dem vorgegebenen Ablauf. Abhängigkeiten und Bedingungen lassen sich granular steuern.
Runner und Executor
GitLab Runners sind die ausführende Instanz im CI/CD-System von GitLab. Sie übernehmen das Ausführen von Jobs, wie sie in der .gitlab-ci.yml
-Datei definiert sind. Runners können projektübergreifend (Shared Runners) oder projektspezifisch (dedizierte Runners) eingesetzt werden. Sie unterstützen verschiedene Executor-Umgebungen (z. B. Shell, Docker, Kubernetes), was flexible und isolierte Ausführungsbedingungen ermöglicht. Dies stellt sicher, dass Builds unabhängig voneinander reproduzierbar und konsistent bleiben. Ein Runner verwendet einen festgelegten Executor, um die Jobs auszuführen – das ist die konkrete Ausführungsumgebung, etwa:
shell
– lokale Kommandozeiledocker
– containerbasierte Umgebungkubernetes
– Ausführung in einem Clusterssh
,virtualbox
etc. – je nach Setup
Diese Trennung erlaubt eine flexible, isolierte und reproduzierbare Ausführung der CI/CD-Prozesse – unabhängig von der Infrastruktur.
Caching und Artefakte
Caching reduziert Wiederholungen in der Pipeline, indem häufig genutzte Ressourcen wie Abhängigkeiten lokal gespeichert und wiederverwendet werden. Dies beschleunigt insbesondere Build- und Test-Jobs erheblich. Artefakte hingegen sind spezifische Ergebnisse einzelner Jobs – etwa kompiliertes Programmcode, Testergebnisse oder Reports – die explizit gespeichert, zwischen Stages weitergegeben oder am Ende zum Download bereitgestellt werden können. Beide Mechanismen verbessern die Effizienz, Nachvollziehbarkeit und Wiederverwendbarkeit der Pipeline-Prozesse.
Praktische Anwendung von CI/CD
CI/CD bietet vielfältige Anwendungsmöglichkeiten zur Automatisierung und Optimierung von Entwicklungs-, Test- und Bereitstellungsprozessen.
Beispiele für Continuous Integration (CI)
- Automatisches Bauen (Builden) der Anwendung nach jedem Commit (z.B.
npm run build
- Node) - Unit-Tests zur Überprüfung einzelner Komponenten (z.B.
pytest
,npm test
,junit
,mocha
) - Linting & Code-Style-Prüfungen für einheitliche Formatierung innerhalb eines Projekts (z.B.
eslint
,pylint
,prettier
) - Statische Codeanalyse für Sicherheitslücken, Codequalität (z.B.
semgrep
,bandit
, GitLab-SAST) - Prüfung auf sensible Daten wie Passwörter/ Tokens (z.B.
truffleHog
,gitleaks
) - Automatische Generierung von Dokumentation (z.B.
Sphinx
(Python),JSDoc
(JavaScript),Doxygen
(C/C++)) - Überprüfung von Abhängigkeiten wie veraltete Libraries oder Sicherheitslücken (z.B.
npm audit
,pip-audit
, GitLab Dependency Scanning mitgemnasium
)
Beispiele für Continuous Deployment (CD)
- Automatische Bereitstellung neuer Versionen auf einem Webserver (z.B. GitLab Auto Deploy,
rsync
-basierte Deployments, Docker-Container Push) - Aktualisierung von Berichten oder Dashboards nach Änderungen (wie z.B. in unserem Versuch)
- Veröffentlichung von APIs oder Webdiensten nach erfolgreicher CI-Phase (z.B. Bereitstellung über
kubectl apply
,helm upgrade
in Kubernetes) - Rollout von Updates für Microservices in Cloud-Umgebungen (z.B. mit GitLab Kubernetes Integration oder GitOps-Tools wie ArgoCD)
- Durchführung von Canary Releases oder Blue-Green-Deployments (z.B. mit Feature-Toggles, Traffic-Splitting, Kubernetes Deployment-Strategien)
- Versand automatischer Benachrichtigungen nach erfolgreichem Deployment (z.B. Slack-Nachricht, E-Mail, automatischer Changelog über GitLab Releases)
Grundbegriffe
-
Pipeline: Eine definierte Abfolge von Schritten zur Integration, Prüfung und Auslieferung von Code.
-
Job: Einzelner Arbeitsschritt innerhalb einer Pipeline, z. B. ein Testlauf oder ein Deployment.
-
Runner: Ausführende Instanz, die die Jobs abarbeitet.
-
Stage: Gruppierung von Jobs, die logisch zusammengehören (z. B. Build, Test, Deploy).
-
CI (Continuous Integration): Automatisierter Prozess, bei dem Codeänderungen regelmäßig integriert und getestet werden, um frühzeitig Fehler zu erkennen.
-
CD (Continuous Deployment / Delivery): Erweiterung von CI, bei der getesteter Code automatisch (Deployment) oder nach manueller Freigabe (Delivery) in Zielumgebungen überführt wird.
-
Staging: Zwischenschritt zwischen Test- und Produktionsumgebung, in dem reale Bedingungen simuliert werden, um letzte Prüfungen vor dem Release durchzuführen.
-
Variable: Platzhalter für wiederverwendbare Werte innerhalb der Pipeline, z. B. zur Konfiguration oder Steuerung von Jobs.
-
YAML: Ein einrückungsbasiertes, menschenlesbares Dateiformat zur Konfiguration von Pipelines in GitLab über
.gitlab-ci.yml
. -
.gitlab-ci.yml
: Konfigurationsdatei, in der die Pipeline-Definition festgelegt ist. -
Executor: Gibt an, in welcher Umgebung ein Runner die Jobs ausführt (z. B. Docker, Shell).
-
Environment: Zielumgebung für Deployments (z. B. Staging, Produktion).
-
Artifacts: Dateien, die während eines Jobs erzeugt werden und gespeichert oder weiterverwendet werden können.
-
Cache: Temporäre Speicherstruktur zur Beschleunigung wiederholter Abläufe in Pipelines.
-
Trigger: Mechanismus, um Pipelines automatisch auszulösen (z. B. bei Push, Merge oder Zeitplan).
-
Manual Job: Ein Job, der manuell durch den Nutzer gestartet werden muss.
-
Protected Branches: Git-Branches mit eingeschränkten Zugriffsrechten für sicheres CI/CD.