2. Git und CI/CD¶
2.1. Einleitung¶
In diesem Kapitel beschäftigen wir uns mit Git als Versionskontrollsystem sowie den Grundlagen und praktischen Anwendungen von Continuous Integration (CI) und Continuous Deployment (CD). Diese Werkzeuge und Methoden sind essentiell für moderne Softwareentwicklungsprozesse und ermöglichen eine effiziente, kollaborative und automatisierte Arbeitsweise.
2.2. GIT Allgemein¶
Git ist ein verteiltes Versionskontrollsystem, das 2005 von Linus Torvalds entwickelt wurde. Es ermöglicht Entwicklerteams, gemeinsam an Codeprojekten zu arbeiten, indem es Änderungen nachverfolgt, verschiedene Versionen verwaltet und die Zusammenführung von Code aus verschiedenen Quellen vereinfacht.
2.2.1. Geschichte und Bedeutung¶
Git entstand aus der Notwendigkeit heraus, den Linux-Kernel effizienter zu entwickeln. Nach Konflikten mit dem zuvor verwendeten proprietären Versionskontrollsystem BitKeeper entschied sich Torvalds, ein eigenes System zu entwickeln, das folgende Anforderungen erfüllen sollte:
Schnelligkeit und Effizienz auch bei großen Projekten
Echte Verteilung ohne zentrale Kontrollinstanz
Robustheit gegen Datenverlust und Beschädigung
Einfache Verzweigung (Branching) und Zusammenführung (Merging)
Heute ist Git zum Standard in der Softwareentwicklung geworden und bildet die technische Grundlage für Plattformen wie GitHub, GitLab und Bitbucket.
2.2.2. Wie Git funktioniert¶
Im Gegensatz zu zentralisierten Versionskontrollsystemen speichert Git nicht nur den aktuellen Zustand der Dateien, sondern eine vollständige Historie aller Änderungen:
Snapshots statt Unterschiede: Git speichert komplette Snapshots des Projekts zu jedem Commit-Zeitpunkt, nicht nur die Unterschiede zwischen Dateien.
Lokale Operationen: Fast alle Git-Operationen können offline durchgeführt werden, da jeder Entwickler eine vollständige Kopie des Repositories auf seinem Rechner hat.
Datenintegrität: Alle Objekte in Git werden über ihre SHA-1-Prüfsumme identifiziert, was Manipulation oder versehentliche Beschädigung von Daten verhindert.
Staging-Bereich: Git bietet eine Zwischenstufe („Staging Area“ oder „Index“) zwischen Arbeitsverzeichnis und Repository, die eine präzise Kontrolle darüber ermöglicht, welche Änderungen committet werden sollen.
Grundlegende Konzepte von Git:
Repository: Ein Verzeichnis, das alle Projektdateien und den vollständigen Versionsverlauf enthält. Es besteht aus dem .git-Ordner (mit Metadaten und Objektdatenbank) und dem Arbeitsverzeichnis.
Commit: Eine Momentaufnahme des Projekts zu einem bestimmten Zeitpunkt. Jeder Commit hat eine eindeutige ID (SHA-1-Hash) und enthält Informationen über Autor, Zeitstempel und eine Nachricht, die die Änderungen beschreibt.
Branch: Eine parallele Version des Codes, die unabhängig entwickelt werden kann. Branches ermöglichen es, neue Funktionen zu entwickeln oder Fehler zu beheben, ohne den Hauptentwicklungszweig (main/master) zu beeinträchtigen.
Merge: Die Zusammenführung von Änderungen aus verschiedenen Branches. Git versucht, Änderungen automatisch zusammenzuführen; bei Konflikten ist manuelle Intervention erforderlich.
Remote: Ein entferntes Repository, das als gemeinsamer Speicherort dient. Remotes ermöglichen die Zusammenarbeit mehrerer Entwickler und dienen oft als Backup und als zentrale Anlaufstelle für gemeinsame Änderungen.
Fork: Eine persönliche Kopie eines fremden Repositories, die unabhängig weiterentwickelt werden kann.
Pull Request / Merge Request: Ein Mechanismus, um Änderungen aus einem Branch oder Fork zur Überprüfung und Integration vorzuschlagen.
2.2.3. Die Architektur von Git¶
Git organisiert Daten in vier Hauptbereichen:
Arbeitsverzeichnis (Working Directory): Hier erfolgt die aktive Entwicklung und Bearbeitung von Dateien.
Staging-Bereich (Index): Hier werden Änderungen für den nächsten Commit vorbereitet und ausgewählt.
Lokales Repository: Die Datenbank mit allen Commits und der vollständigen Projekthistorie auf dem lokalen Rechner.
Remote-Repository: Die gemeinsame Version des Repositories auf einem Server, auf die alle Teammitglieder zugreifen können.
Der typische Git-Workflow umfasst das Hinzufügen von Änderungen zum Staging-Bereich (git add), das Festschreiben dieser Änderungen im lokalen Repository (git commit) und das Synchronisieren mit dem Remote-Repository (git push und git pull).
2.3. Git Befehle – Überblick¶
Git bietet eine Vielzahl an Befehlen, mit denen die Versionskontrolle effizient und flexibel gesteuert werden kann. In diesem Abschnitt geht es darum, ein solides Grundverständnis für diese Befehle zu entwickeln und deren Anwendung im Alltag nachvollziehen zu können.
Die Inhalte richten sich an Einsteiger sowie an fortgeschrittene Nutzer, die ihr Wissen festigen oder erweitern möchten. Gerade im Zusammenspiel mit Continuous Integration (CI) und Continuous Deployment (CD) ist ein sicherer Umgang mit Git unerlässlich.
2.3.1. Ziele dieses Abschnitts¶
Einführung in die wichtigsten Git-Kommandos für die tägliche Arbeit
Verständnis für die internen Abläufe hinter add, commit, merge, branch und Co.
Aufbau von Routine beim Umgang mit Branches, Konflikten und Repositories
Förderung von Best Practices bei der Versionsverwaltung in Teams
2.3.2. Kategorisierung der Git-Befehle¶
Die Befehle lassen sich in mehrere Hauptgruppen einteilen:
Initialisierung und Konfiguration - git init, git config, git clone
Änderungen verfolgen - git status, git add, git diff, git commit
Branching und Merging - git branch, git checkout, git merge, git switch, git rebase
Remote-Repositories - git remote, git fetch, git pull, git push
Verlauf und Rücksetzen - git log, git reset, git revert, git stash
Zusammenarbeit im Team - git pull, git merge, git rebase, git push
2.3.3. Typische Fehlerquellen und Stolpersteine¶
Änderungen nicht gestaged (git add) vor dem Commit
Merge-Konflikte beim parallelen Arbeiten an derselben Datei
Versehentliches Zurücksetzen von Commits mit git reset –hard
Verwirrung durch zu viele nicht beschriftete Branches
Unsaubere Commit-Historien durch fehlende Struktur oder unklare Messages
2.3.4. Vorteile durch gezielte Nutzung von Git-Befehlen¶
Nachvollziehbarkeit: Jeder Schritt in der Entwicklung wird dokumentiert und kann zurückverfolgt werden.
Flexibilität: Branches ermöglichen parallele Entwicklungen, Tests oder Experimente ohne Risiko.
Sicherheit: Änderungen können gezielt zurückgenommen, geprüft oder überarbeitet werden.
Zusammenarbeit: In Verbindung mit Remotes und Pull/Merge Requests wird koordiniertes Arbeiten im Team erleichtert.
CI/CD-Unterstützung: Git bildet die Grundlage für Automatisierungstools, die auf sauberen Branches und nachvollziehbaren Änderungen aufbauen.
2.3.5. Praktischer Bezug¶
Im Rahmen dieses Projekts wird ein interaktives Jupyter Notebook bereitgestellt, in dem die vorgestellten Befehle direkt ausprobiert werden können. Das Notebook simuliert typische Git-Abläufe innerhalb eines Übungs-Repositories.
Dadurch ergibt sich eine Verbindung von Theorie und Praxis, die das Verständnis der Konzepte vertieft und den späteren Einsatz im realen Projektkontext erleichtert.
2.4. Pipeline + Runner¶
Eine CI/CD-Pipeline ist eine Reihe automatisierter Prozesse, die Code von der Entwicklung bis zur Bereitstellung durchläuft. Runner sind die Ausführungsumgebungen, in denen diese Pipeline-Jobs ausgeführt werden.
Typische Elemente einer CI/CD-Pipeline:
Source: Code wird aus dem Repository abgerufen
Build: Anwendung wird kompiliert und gebaut
Test: Automatisierte Tests werden ausgeführt
Deploy: Anwendung wird in der Zielumgebung bereitgestellt
Runner-Typen:
Shared Runner: Von der Plattform (z.B. GitLab, GitHub) bereitgestellt, von mehreren Projekten genutzt
Specific Runner: Dedizierte Ausführungsumgebung für bestimmte Projekte
Docker Runner: Nutzt Docker-Container für isolierte Ausführungsumgebungen
Kubernetes Runner: Führt Jobs in einem Kubernetes-Cluster aus
Beispiel für eine einfache GitLab CI/CD-Pipeline-Konfiguration:
2.4.1. Funktionsweise der GitLab CI/CD Pipeline¶
Die GitLab CI/CD Pipeline basiert auf einer Konfigurationsdatei namens .gitlab-ci.yml, die im Wurzelverzeichnis des GitLab-Repositories liegt. Diese Datei beschreibt in deklarativer Form:
die einzelnen Stages (z. B.
build,test,deploy),die zugehörigen Jobs je Stage,
das verwendete Docker-Image pro Job,
und optionale Regeln, wann ein Job ausgeführt werden soll (z. B. nur bei
main-Branch oder manuell).
Ein Beispieljob könnte z. B. ein Python-Backend bauen, testen und anschließend automatisch auf die VM deployen.
2.4.2. Verwendung eines GitLab Runners¶
Um die Pipeline lokal auf unserer VM auszuführen, wurde ein GitLab Runner in einem Docker-Container eingerichtet. Dieser Runner registriert sich bei unserem GitLab-Projekt und führt eingehende Jobs selbstständig in isolierten Containern aus. Dadurch konnten wir beliebige Docker-Images nutzen – je nach Dienst, z. B.:
python:3.11für das Backend,node:18für ein Streamlit-Frontend,alpinefür Deployment- und SSH-Jobs.
2.4.3. Übersicht: Runner-Typen¶
GitLab unterscheidet grundsätzlich zwei Runner-Arten:
Shared Runner - Wird von GitLab (z. B. GitLab.com) bereitgestellt - Kann von mehreren Projekten gleichzeitig genutzt werden - Gut für kleine Projekte oder Tests - Kein direkter Zugriff auf eigene Infrastruktur
Specific Runner (auch: Private Runner) - Wird von uns selbst bereitgestellt (z. B. auf der VM) - Kann für ein bestimmtes Projekt oder eine Gruppe konfiguriert werden - Ermöglicht Zugriff auf lokale Ressourcen (z. B. Docker, Datenbanken) - Ideal für produktionsnahe Deployments
Einordnung in unserem Projekt: Wir haben einen Specific Runner mit Docker-Executor auf der Ziel-VM verwendet, da wir die Kontrolle über die Build- und Deployment-Umgebung benötigen.
2.4.4. Installation und Registrierung eines GitLab Runners¶
Nach der Docker-Installation auf der VM kann der Runner mit folgendem Befehl als Container gestartet werden:
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
Anschließend wird der Runner mit dem Projekt registriert:
docker exec -it gitlab-runner gitlab-runner register
Die folgenden Angaben werden dabei interaktiv abgefragt:
GitLab-URL: z. B.
https://gitlab.comoder URL der Self-Hosted-InstanzToken: Wird in GitLab unter Projekt → Einstellungen → CI/CD → Runner → „Eigenen Runner registrieren“ bereitgestellt
Beschreibung: z. B.
docker-runnerTags: z. B.
build,deployExecutor:
dockerDefault Docker Image: z. B.
alpine:latest,python:3.11,node:18
Nach erfolgreicher Registrierung erscheint der Runner in GitLab und ist bereit, Pipelines auszuführen.
2.4.5. Ergebnisse und Nutzen¶
Durch die Einrichtung der GitLab CI/CD Pipeline mit eigenem Runner konnten wir:
eine vollständige Automatisierung des Build- und Deployment-Prozesses umsetzen,
eine konsistente Umgebung für alle Services schaffen,
manuelle Fehlerquellen bei der Auslieferung vermeiden,
und die Wartbarkeit und Erweiterbarkeit unseres Projekts langfristig verbessern.
Die entwickelte Pipeline dient als zentrales Steuerungselement und kann künftig um weitere Tests, Sicherheitsprüfungen und Monitoring-Erweiterungen ergänzt werden.
2.5. CI Teil¶
Continuous Integration (CI) ist ein wesentlicher Bestandteil moderner Softwareentwicklungsprozesse. Es handelt sich um eine Praxis, bei der Entwickler regelmäßig Code in ein gemeinsames Repository integrieren. Jeder Integrationsvorgang wird dann automatisch durch einen Build-Prozess überprüft, der Tests und andere Qualitätskontrollen umfasst. Das Hauptziel von CI ist es, Fehler frühzeitig zu erkennen und die Integrität des Codes sicherzustellen.
Unterschied zwischen CI und CD
Während Continuous Integration sich auf das regelmäßige Zusammenführen von Codeänderungen und deren automatisierte Überprüfung konzentriert, geht Continuous Deployment (CD) einen Schritt weiter. CD automatisiert den gesamten Prozess bis zur Bereitstellung der Anwendung in der Produktionsumgebung. Der Hauptunterschied liegt also im Umfang der Automatisierung: CI endet mit der Verifizierung des Codes, während CD die Bereitstellung umfasst.
Beispiel 1: Code-Formatierung mit Black
Black ist ein beliebtes Tool zur automatischen Formatierung von Python-Code. Es hilft, einen konsistenten Code-Stil im gesamten Projekt zu gewährleisten, was die Lesbarkeit und Wartbarkeit verbessert. In einer CI-Pipeline kann Black als Schritt integriert werden, um sicherzustellen, dass alle Codeänderungen den definierten Stilrichtlinien entsprechen.
Integration in CI: Ein typischer CI-Job für Black könnte wie folgt aussehen:
format: stage: test script: - black --check .
Dieser Job überprüft, ob der Code korrekt formatiert ist. Bei Abweichungen schlägt der Job fehl, und der Entwickler wird aufgefordert, den Code zu formatieren.
Beispiel 2: Linting mit Pylint
Pylint ist ein statisches Code-Analyse-Tool für Python, das den Code auf potenzielle Fehler, Stilprobleme und andere Verbesserungsmöglichkeiten überprüft. Es ist ein wertvolles Werkzeug, um die Codequalität zu verbessern und Best Practices durchzusetzen.
Integration in CI: Ein typischer CI-Job für Pylint könnte wie folgt aussehen:
lint: stage: test script: - pylint my_project/
Dieser Job führt Pylint auf dem Projektverzeichnis aus und gibt eine Bewertung sowie detaillierte Berichte über gefundene Probleme aus. Entwickler können diese Berichte nutzen, um ihren Code zu verbessern, bevor er in den Hauptzweig integriert wird.
Beispiel 3: Geheimnisprüfung mit git-secrets
Ein typisches Tool zur Geheimnisprüfung ist git-secrets, das entwickelt wurde, um zu verhindern, dass Geheimnisse in Git-Repositories gelangen. Es kann in CI/CD-Pipelines integriert werden, um sicherzustellen, dass keine sensiblen Informationen in den Code-Commits enthalten sind.
Beispiel für einen CI-Job zur Geheimnisprüfung:
secret-check: stage: test script: - git secrets --scan
Dieser Job scannt den Code auf bekannte Muster von Geheimnissen und schlägt fehl, wenn solche Muster gefunden werden. Entwickler werden aufgefordert, die Geheimnisse zu entfernen oder zu sichern, bevor der Code in den Hauptzweig integriert wird.
Durch die Integration von Tools wie Black und Pylint in den CI-Prozess wird sichergestellt, dass der Code nicht nur funktional korrekt, sondern auch stilistisch konsistent und von hoher Qualität ist. Dies trägt dazu bei, die Wartbarkeit und Erweiterbarkeit des Projekts langfristig zu sichern. ```
2.6. CD Teil¶
Continuous Deployment (CD) ist der Prozess, bei dem Code automatisch in verschiedene Umgebungen, einschließlich der Produktionsumgebung, bereitgestellt wird. CD baut auf CI auf und erweitert den Automatisierungsprozess bis zur Bereitstellung.
Unterschied Delivery vs. Deployment
Continuous Delivery: Automatisiertes Deployment in Staging/Test, Produktion nach manueller Freigabe.
Continuous Deployment: Jede Änderung, die alle Tests besteht, wird automatisch produktiv deployed.
Typischer CD-Prozess (Web-App, Beispiel aus unserem Projekt)
Unsere GitLab CI/CD-Pipeline besteht aus mehreren Stufen: Build, Test, Delivery, Staging und Deploy. Die Delivery-Stufe überträgt Artefakte auf den Zielserver, die Deploy-Stufe startet die Anwendung oder aktualisiert Container.
# .gitlab-ci.yml (Ausschnitt)
stages:
- build
- test
- delivery
- staging
- deploy
hs-augsburg-delivery:
stage: delivery
script:
- scp -o StrictHostKeyChecking=no -r ${SOURCE_DIR}/* ${USER_ID}@${SERVER}:${TARGET_DIR}/
- ssh -o StrictHostKeyChecking=no ${USER_ID}@${SERVER} "chmod -R 755 ${TARGET_DIR}"
staging-deploy:
stage: staging
script:
- ssh $STAGING_USER@$STAGING_HOST "mkdir -p ~/staging"
- scp dist/app.sh $STAGING_USER@$STAGING_HOST:~/staging/
- ssh $STAGING_USER@$STAGING_HOST "chmod +x ~/staging/app.sh && ~/staging/app.sh"
production-deploy:
stage: deploy
script:
- ssh $PROD_USER@$PROD_HOST "systemctl restart myapp || echo 'Kein systemd-Service vorhanden, starte manuell' && /opt/myapp/production/app.sh"
when: manual
Das Deployment-Skript [deploy.sh](../deploy.sh) aktualisiert z.B. Docker-Container auf dem Server:
ssh user@server "cd /pfad/docker-Container-projekt && docker-compose pull && docker-compose up -d"
Herausforderungen
Sicherheit: SSH-Keys und Secrets werden nicht im Repo gespeichert, sondern als GitLab-Variablen hinterlegt.
Environment Handling: Unterschiedliche .env-Dateien für Staging/Prod, Variablen werden beim Deployment gesetzt.
Deployment-Strategien & Risiken
Rolling Deployment: Update nacheinander, Risiko: kurzzeitige Inkompatibilitäten.
Blue-Green: Zwei Umgebungen, schnelles Rollback, Risiko: doppelter Ressourcenbedarf.
Canary: Teilweises Ausrollen, Risiko: Fehler werden ggf. erst spät erkannt.
Weitere Beispiele und Konfigurationen siehe [docker-Container-projekt/docker-compose.yml](../docker-Container-projekt/docker-compose.yml). Vorteile von CD:
Schnellere Markteinführung: Neue Features können schneller veröffentlicht werden
Reduzierte Fehleranfälligkeit: Manuelle Bereitstellungsfehler werden minimiert
Einfacheres Rollback: Bei Problemen kann schnell auf eine frühere Version zurückgewechselt werden
Kontinuierliches Feedback: Entwickler erhalten schnelles Feedback zur Funktionsfähigkeit ihrer Änderungen
2.7. Fazit¶
Git und CI/CD-Pipelines sind wesentliche Werkzeuge in der modernen Softwareentwicklung. Sie ermöglichen Teams, effizienter zusammenzuarbeiten, die Codequalität zu verbessern und Softwareänderungen schneller und zuverlässiger bereitzustellen. Durch die Automatisierung wiederkehrender Aufgaben können sich Entwickler auf das Wesentliche konzentrieren: das Erstellen von hochwertigem Code.
Die Integration dieser Technologien in den Entwicklungsprozess ist nicht nur ein technischer Vorteil, sondern auch ein kultureller Wandel hin zu einer agileren und reaktionsfähigeren Arbeitsweise.
2.8. Literatur und Quellen¶
GitLab CI/CD Dokumentation: https://docs.gitlab.com/ee/ci/
GitLab CI/CD Pipelines: https://docs.gitlab.com/ee/ci/pipelines/
GitLab Runner Dokumentation: https://docs.gitlab.com/runner/
Einführung in CI/CD (Atlassian): https://www.atlassian.com/continuous-delivery/ci-vs-ci-vs-cd
Continuous Integration (Wikipedia): https://de.wikipedia.org/wiki/Continuous_Integration
Continuous Deployment (Martin Fowler): https://martinfowler.com/bliki/ContinuousDeployment.html
GitLab Tutorials und Guides: https://about.gitlab.com/solutions/ci-cd/