Einleitung: Es ist wichtig, das Gesamtbild zu verstehen

Hallo! Im ersten Teil haben wir darüber gesprochen, warum wir ein automatisiertes Bereitstellungssystem mit GitHub Webhook aufbauen möchten und welche Vorbereitungen dafür nötig sind. Im zweiten Teil werden wir vor der eigentlichen Implementierung des Codes eine Zeit damit verbringen, das gesamtliche Architektur- und Prozessdesign unseres automatisierten Bereitstellungssystems zu gestalten.

Die Entwicklungsumgebungen können sich leicht unterscheiden. Einige nutzen vielleicht einen Raspberry Pi, während andere einen Cloud VPS verwenden, und die Struktur des Projekts, das bereitgestellt werden soll, wird ebenfalls variieren. Um ein flexibles System in solch unterschiedlichen Umgebungen aufzubauen und eigenständig Probleme zu lösen, ist es vor allem wichtig, den gesamten Fluss und Kontext zu verstehen, anstatt sich nur auf spezifischen Code zu konzentrieren.

In diesem Teil wollen wir das große Bild des Systems, das wir erstellen werden, im Kopf visualisieren und klar verstehen, welche Rolle jede Komponente spielt.

Automatisiertes Bereitstellungssystem Flussdiagramm

Automatisierter Bereitstellungsworkflow: Überblick über den Gesamtfluss

Das automatisierte Bereitstellungssystem, das wir implementieren, funktioniert in einer Reihe von Schritten.

  1. Änderungen im Code und Git Push auf der lokalen Arbeitsmaschine: Der Entwickler ändert den Code in seiner lokalen Umgebung und pusht die Änderungen auf einen bestimmten Branch des GitHub-Repositories (z.B. main oder develop).

  2. Webhook-Ereignis in GitHub-Repository wird ausgelöst: Sobald das push Ereignis im GitHub Repository erkannt wird, sendet GitHub über den zuvor eingestellten Webhook eine HTTP POST-Anfrage an die angegebene URL.

  3. Anfrage wird an den Webhook-Endpunkt des Staging-Servers weitergeleitet: Die Webhook-Anfrage von GitHub verweist auf eine spezifische URL unseres Staging-Servers (z.B. https://deployer.example.com/webhook). Diese Anfrage enthält verschiedene Payload-Daten, wie die Informationen zu den gepushten Commits und eine Liste der geänderten Dateien.

  4. Webhook-Anfrage wird von der FastAPI-Anwendung auf dem Staging-Server empfangen: Unsere FastAPI-Anwendung, die auf dem Staging-Server läuft, empfängt diese Webhook-Anfrage. Hier beginnt die Rolle des FastAPI-Dienstes.

Kernrollen des FastAPI Webhook-Dienstes

Der Webhook-Dienst, den wir mit FastAPI implementieren, wird über das bloße Empfangen von Anfragen hinaus wichtige Aufgaben übernehmen.

Webhook empfangen und initial verarbeiten

Die FastAPI-Anwendung bietet einen Endpunkt (z.B. /webhook), der in der Lage ist, HTTP POST Anfragen von GitHub zu empfangen. Dieser Endpunkt parst die HTTP-Header und den Body (Payload) der Anfrage, um die erforderlichen Informationen zu extrahieren.

Überprüfung des Secrets

Sicherheit ist das Wichtigste in einem automatisierten Bereitstellungssystem. GitHub Webhook bietet einen Secret-Wert über den Header X-Hub-Signature-256, um die Integrität der Anfrage zu überprüfen. Unser FastAPI-Dienst muss Logik enthalten, um zu überprüfen, ob die Anfrage von GitHub stammt und ob sie nicht manipuliert wurde. Bei einem Fehler in der Überprüfung wird die Anfrage sofort abgelehnt, um unbefugten Zugriff zu verhindern.

Sofortige Antwort und Hintergrundaufgaben

Wenn GitHub nach dem Senden einer Anfrage innerhalb einer bestimmten Zeit (standardmäßig 10 Sekunden) keine Antwort erhält, wird dies als Timeout angesehen, und es wird ein Retry oder ein Fehler verzeichnet. Der tatsächliche Bereitstellungsprozess (Git Pull, Docker-Build/Neustart usw.) kann jedoch lange dauern.

Daher wird unser FastAPI-Dienst nach dem Empfang der Webhook-Anfrage sofort die initiale Verarbeitung, wie die Überprüfung des Secret, abschließen und sofort eine 200 OK-Antwort an GitHub senden. Der tatsächliche Bereitstellungs-Logik wird so gestaltet, dass sie mithilfe der BackgroundTasks-Funktion von FastAPI asynchron im Hintergrund ausgeführt wird. So können wir das Timeout-Problem von GitHub umgehen und dennoch zuverlässig die Bereitstellung durchführen.

Detaillierte Logik des Bereitstellungshandlers

Der im Hintergrund ausgeführte Bereitstellungshandler wird die folgenden zentralen Aufgaben ausführen.

Verwaltung mehrerer Projekte: Auslesen des Repository-Pfads

Wir werden so gestalten, dass ein einziges FastAPI Webhook-Dienst mehrere GitHub-Repositories (Projekte) automatisiert bereitstellen kann. Dazu mappen wir die Repository-Pfade der einzelnen Projekte und die entsprechenden lokalen Pfade, zu denen das Projekt auf dem Server bereitgestellt wird, über Umgebungsvariablen oder Konfigurationsdateien. Nachdem wir festgestellt haben, aus welchem Repository das Ereignis stammt, wechseln wir zum Pfad des entsprechenden Projekts und führen die Bereitstellung durch.

Projekt-spezifische Anpassung: Parsen der .env-Datei (optional)

Jedes Projekt kann über spezifische Umgebungsvariablen oder Konfigurationswerte verfügen, die für den Build oder die Bereitstellung erforderlich sind. Dies könnten bestimmte Docker-Image-Tags, Build-Optionen oder Befehle zum Neustarten von Services sein. Um dies effizient zu verwalten, werden wir die erforderlichen Werte aus der .env-Datei im lokalen Repository jedes Projekts parsen und für die Bereitstellungslogik verwenden. Dies wird uns helfen, eine flexible und maßgeschneiderte Bereitstellungslogik zu implementieren.

Code aktualisieren: git pull ausführen

Das ist der grundlegendste Schritt. Mit dem subprocess-Modul führen wir im lokalen Repository des Projekts den Befehl git pull origin <branch_name> aus, um den neuesten Code vom GitHub-Repository abzurufen.

Entscheidung zur Neubau des Docker-Images: Verwendung von git diff

Für Docker-basierte Projekte ist der Befehl docker compose up -d ausreichend, wenn nur der Code geändert wurde. Aber wenn sich Dateien wie Dockerfile oder requirements.txt (im Falle von Python-Projekten) ändern, die den Build des Docker-Images beeinflussen, muss das Image neu gebaut werden.

Wir werden den Befehl git diff verwenden, um festzustellen, ob sich zwischen dem zuletzt gepushten Commit und dem vorherigen Commit Dateien wie Dockerfile oder andere build-relevante Dateien verändert haben. Wenn Änderungen festgestellt werden, führen wir docker compose up -d --build aus, andernfalls nur docker compose up -d, um unnötige Neubauten des Images zu vermeiden und die Bereitstellungszeit zu verkürzen.

Docker Compose ausführen: Verwendung des subprocess-Moduls

Nach dem Abrufen des neuesten Codes und der Entscheidung über die Neubau des Images, verwenden wir das subprocess-Modul, um die Befehle docker compose up -d oder docker compose up -d --build auszuführen und die Docker-Container auf den neuesten Stand zu bringen und die Services neu zu starten.

Protokollierung: Aufzeichnung aller Prozesse

Alle Prozesse der Bereitstellung (Empfang des Webhooks, Validierung, Ergebnisse des Git Pulls, Docker-Bau-/Neustart-Logs usw.) müssen detailliert protokolliert werden. Dies ist unerlässlich, um die Ursachen bei Problemen zu identifizieren und zu debuggen. Wir werden das logging-Modul von Python verwenden, um Protokolle in Dateien zu führen.

Bereitstellungs- und Betriebsstrategien für den FastAPI-Dienst

Der FastAPI Webhook-Dienst, den wir implementieren, muss stabil kontinuierlich auf dem Staging-Server laufen.

Wichtigkeit der Nutzung von Systemd-Service

Anstatt die FastAPI-Anwendung direkt als Docker-Container auszuführen, empfehlen wir dringend, sie als Systemd-Service zu betreiben. Der Grund dafür sind folgende:

  • Ressourcenschonung: Wahrscheinlich sind auf dem Staging-Server bereits Tools wie git, docker, docker compose installiert, die für die Bereitstellung benötigt werden. Das Erstellen des FastAPI Webhook-Dienstes als Docker-Container und das anschließende Installieren von git oder docker innerhalb dieses Containers kann die Größe des Containers unnötig erhöhen und die Notwendigkeit komplizierter Konfigurationen und Sicherheitsprobleme wie docker-in-docker oder docker-out-of-docker verursachen.

  • Einfaches Management: Systemd ist der Standard für das Management von Diensten in Linux-Systemen. Wenn die FastAPI-App als Systemd-Service registriert wird, können sie bei Serverstart automatisch gestartet, der Status des Dienstes überprüft und einfach neu gestartet oder gestoppt werden – alles in einem integrierten und einfachen Management auf OS-Ebene.

  • Nutzung der Systemressourcen: Wenn die FastAPI-App über Systemd betrieben wird, kann sie direkt die auf dem System installierten git- und docker-Befehle aufrufen, um die Bereitstellung durchzuführen, wodurch die bestehenden Ressourcen des Systems optimal genutzt werden können.

Im nächsten Teil werden wir ausführlich erläutern, wie die FastAPI-Anwendung als Systemd-Service registriert und verwaltet werden kann.

Reverse Proxy und HTTPS mit Nginx/Apache2

Wie im ersten Teil betont, ist es aus Sicherheitsgründen sehr riskant, die FastAPI-Anwendung direkt im Internet exponiert zu haben. Daher werden wir Nginx oder Apache2 als Reverse Proxy einsetzen, um die Webhook-Anfragen sicher an die FastAPI-Anwendung weiterzuleiten.

Zusätzlich empfiehlt GitHub Webhook nachdrücklich die Verwendung von HTTPS, daher müssen wir eine dedizierte Subdomain wie deployer.example.com einrichten und ein HTTPS-Zertifikat über Let’s Encrypt erhalten und auf dem Webserver implementieren. Dadurch wird sichergestellt, dass sämtliche Kommunikation von außen verschlüsselt wird, um die Sicherheit zu erhöhen.

Überwachung und Debugging

Um sicherzustellen, dass das automatisierte Bereitstellungssystem ordnungsgemäß funktioniert und bei Problemen die Ursachen zu identifizieren, werden wir die folgenden zwei Methoden nutzen.

  • Protokolldatei des FastAPI-Dienstes: Der FastAPI-Dienst, den wir implementieren, wird alle Schritte des Bereitstellungsprozesses in einer eigenen Protokolldatei festhalten. Wir können diese Datei überprüfen, um den Erfolg der Bereitstellung und aufgetretene Fehlermeldungen zu erfassen.

  • Systemd journalctl: Da wir den FastAPI-Dienst mit Systemd verwalten, können wir die Standardausgaben und Fehlerprotokolle des Dienstes in Echtzeit über den Befehl journalctl -u your-fastapi-service.service überprüfen und analysieren.

Abschluss: Vorschau auf den nächsten Teil

Im zweiten Teil haben wir die gesamte Architektur des automatisierten Bereitstellungssystems mit GitHub Webhook, die Kernrollen der FastAPI-Dienste und die Strategien für Bereitstellung und Betrieb detailliert betrachtet. Ich hoffe, dass Sie jetzt das große Bild vor Augen haben.

Im nächsten, dritten Teil werden wir basierend auf den heute entworfenen Inhalten tatsächlich den Code für den FastAPI Webhook-Dienst schreiben, den GitHub Webhook konfigurieren und die Implementierung des Registrierens als Systemd-Service behandeln. Seien Sie gespannt!


Serie über den Aufbau eines automatisierten Bereitstellungssystems mit GitHub Webhook

Teil 1 - Warum selbst implementieren?