Aufbau einer guten CI/CD-Pipeline mit GitHub Actions Es ist nicht länger eine Option „für den Fall, dass Zeit ist“: In modernen Teams ist es praktisch eine Voraussetzung für eine schnelle und zuverlässige Bereitstellung. Dennoch ist es oft viel komplizierter als gedacht, ein vollständiges, generisches und durchdachtes Beispiel zu finden, das man an das eigene Unternehmen anpassen kann.
Im Folgenden werden wir die klassische Theorie von CI/CD mit der klassischen Theorie von CI/CD vermischen. mit praktischen Anwendungsbeispielen unter Verwendung von GitHub Actions, wiederverwendbaren Pipelines, Tasks und Bash-Skripten, PowerShell-PnP-ModuleBereitstellungen auf Kubernetes, Google Cloud und Kinsta sowie Best Practices für Sicherheit, Monitoring und Skalierbarkeit. Die Idee ist, dass Sie diese Bausteine ​​an Ihre Bedürfnisse anpassen und viele typische Fallstricke vermeiden können.
Warum Sie eine gut strukturierte CI/CD-Pipeline benötigen
In der aktuellen beruflichen Weiterbildung ist CI/CD das Kreislaufsystem des Codes.Es integriert Änderungen, führt Tests durch, erstellt Artefakte und stellt neue Versionen mit minimalem Eingriff bereit. Ohne diesen Workflow wird jede Bereitstellung zu einer langsamen, fehleranfälligen und manuellen Angelegenheit.
Die kontinuierliche Integration (CI) konzentriert sich auf die Validierung von Änderungen Sobald die Dateien im Repository hochgeladen sind, werden Unit-Tests, Linter und statische Analysen ausgeführt, um Fehler schnellstmöglich zu erkennen. Je schneller Sie Feedback erhalten, desto eher können Sie Fehler beheben und desto weniger gravierend sind eventuelle Regressionen.
Kontinuierliche Bereitstellung (CD im Sinne von Continuous Deployment) Oder Continuous Delivery (je nach Automatisierungsgrad) fügt die Automatisierung des Release-Prozesses hinzu: Erstellen von Images, Veröffentlichen von Paketen, Bereitstellen in Test-, Staging- oder Produktionsumgebungen und sogar Ändern des Datenverkehrs mithilfe von Blue-Green- oder Canary-Strategien.
In Unternehmen mit viel AltcodeEine gute Pipeline ist einer der besten Hebel zur Modernisierung des Ökosystems: Sie ermöglicht es, Tests in Legacy-Dienste einzuführen, Aufgaben zu automatisieren, die zuvor manuell erledigt wurden, und die Kosten für die Wartung veralteter Infrastrukturen wie Jenkins oder Nexus zu reduzieren.
Was ist GitHub Actions und warum passt es so gut zu CI/CD?
GitHub Actions ist die in GitHub integrierte Automatisierungsplattform. Es ermöglicht Ihnen, Workflows in YAML-Dateien direkt im Repository zu definieren. Damit können Sie Ihre Software kompilieren, testen, analysieren und bereitstellen, ohne externe CI-Server einrichten zu müssen.
Ein Workflow ist eine Reihe von Aufgaben und Schritten. was durch Ereignisse wie beispielsweise push, pull_request, schedule (CRON), workflow_dispatch (manuell) oder sogar Aktionen zu Problemen. Jeder Job wird in einem Runner ausgeführt (zum Beispiel ubuntu-latest) und besteht aus Schritten, die wiederverwendbare Aktionen oder Befehle verwenden. run.
GitHub bietet einen riesigen Marktplatz für Shares Hier finden Sie vorgefertigte Integrationen für nahezu alles: Docker, Kubernetes, AWS, Azure, Google Cloud, SonarCloud, Slack, Jira, Sicherheitsanalysen, Linter für tausende Sprachen usw. Dadurch wird der Zeitaufwand für die Einrichtung komplexer Pipelines erheblich reduziert.
Im Vergleich zu Lösungen wie Jenkins oder ConcourseGitHub Actions bietet mehrere klare Vorteile: Es handelt sich um einen verwalteten Dienst (Sie müssen keine Server verwalten), er ist eng mit dem Code verknüpft, nutzt ein nutzungsbasiertes Abrechnungsmodell und wird von einer großen Community unterstützt. Darüber hinaus kennen viele Entwickler ihn bereits aus eigenen Projekten, was den Lernaufwand deutlich reduziert.
Grundlegende Komponenten eines GitHub Actions-Workflows
Alles beginnt mit einer YAML-Datei in .github/workflows/zum beispiel ci.yml o build-test-deploy.ymlObwohl die Syntax beträchtlich umfangreich sein kann, ist die Grundstruktur relativ einfach.
Die wichtigsten Abschnitte von YAML sind: name (Workflow-Name), on (Ereignisse, die es auslösen), jobs (eine Reihe logischer Aufgaben), und innerhalb jeder Aufgabe, runs-on (Läufer), steps (Schritte), env (globale Variablen) und if (Bedingungen für die Ausführung von Schritten oder Aufträgen).
Jobs stellen Arbeitsblöcke dar. die parallel oder nacheinander ausgeführt werden können needsInnerhalb jedes Jobs werden in den einzelnen Schritten Aktionen verwendet (uses:) oder Befehle (run:Ein typisches Beispiel umfasst: Code-Checkout, Installation der Abhängigkeiten, Ausführung des Linters, Tests und Build.
Geheimnisse und Umgebungsvariablen Sie werden auf Repository-, Organisations- oder Umgebungsebene verwaltet. In Workflows werden sie referenziert mit ${{ secrets.MI_SECRET }} und die Verwendung von API-Schlüsseln, Bereitstellungstoken oder Cloud-Anmeldeinformationen zu ermöglichen, ohne diese im Repository offenzulegen.
YAML ermöglicht auch das Erstellen von Ausführungsarrays. mit strategy.matrix, sehr nützlich, um Ihren Code auf verschiedenen Versionen von Node, Python oder Java oder sogar auf verschiedenen Betriebssystemen zu testen, ohne denselben Codeblock mehrmals schreiben zu müssen.
Entwerfen Sie eine moderne CI/CD-Pipeline unter Anwendung bewährter Verfahren
Eine gesunde Pipeline ist üblicherweise in klar definierte Phasen unterteilt.: Schnellprüfungen (Lint, Unit-Tests), Artefakt-Build, Release (Version, Labeling, Changelog, Veröffentlichung im Artefakt-Repository) und Deployment in einer oder mehreren Umgebungen.
Die kontinuierliche Integrationsphase sollte so schnell wie möglich erfolgen. Dadurch wird sichergestellt, dass jede Push- oder Pull-Anfrage nahezu sofortiges Feedback erhält. Üblicherweise werden die verschiedenen Prüfungen parallel in separaten Arrays oder Jobs ausgeführt, wobei zwar etwas höhere Kosten in Kauf genommen werden, die Gesamtwartezeit jedoch deutlich reduziert wird.
Um die Pipeline von der konkreten Sprache zu entkoppelnSie können ein Aufgabenverwaltungstool wie Task verwenden (ähnlich wie Make, aber mit einer benutzerfreundlicheren Syntax). Auf diese Weise ruft der GitHub Actions-Workflow nur generische Aufgaben auf (task test, task lintetc.) und jedes Repository definiert, wie sie intern implementiert werden, je nachdem, ob es sich um Node, Java, Python usw. handelt.
Versionierung und Artefakte spielen während der Release-Phase eine Rolle.Hier erstellen Sie ein Docker-Image, eine JAR-/WAR-Datei, ein npm-Paket oder ein anderes Artefakt, laden es in die entsprechende Registry hoch (Docker Registry, Maven, npm in der Artifact Registry usw.), taggen Commits und generieren GitHub-Releases oder Changelogs mit Tools wie git-cliff oder Freigabeaktionen.
Schließlich die Bereitstellungsphase Verschieben Sie dieses Artefakt in die Laufzeitumgebung: Kubernetes (GKE), Google App Engine, Cloud Functions, Dienste auf Kinsta, Ihre eigenen Server via SSH usw. Hier können Sie nachfolgende Schritte verketten, wie z. B. Funktionstests nach der Bereitstellung oder Slack-Benachrichtigungen mit Release-Details.
Beispiel: Vollständige Pipeline mit ESLint, Tests und Deployment auf Kinsta
Ein sehr anschauliches Beispiel ist die Verwendung von GitHub Actions. Eine React-Anwendung wird mit ESLint und Unit-Tests validiert und anschließend über die Kinsta-API bereitgestellt. Der gesamte Prozess ist in einem einzigen CI/CD-Workflow orchestriert.
Der erste Teil der YAML-Datei definiert den Auslöser. und der Name der Pipeline. Zum Beispiel, dass sie auf jedem push y pull_request zum Zweig mainund sogar mit CRON-Jobs geplant werden (zum Beispiel jeden Tag um Mitternacht oder jeden Montag um 8:00 Uhr UTC) unter Verwendung des Ereignisses schedule.
Der erste Job in der Pipeline kann so genannt werden eslint und es ist für die Überprüfung der Codesyntax zuständig. Es läuft in ubuntu-latest und verwendet eine Reihe von Node-Versionen (z. B. 18.x, 20.x) mit Schritten zum Auschecken und Konfigurieren von Node mit actions/setup-node, npm-Abhängigkeiten zwischenspeichern, installieren mit npm ci und werfen npm run lint.
Der zweite Job, testsEs hängt davon ab eslint durch needs: eslintEs wird also nur ausgeführt, wenn die Syntaxprüfung erfolgreich ist. Intern wiederholt sich das Muster: Auschecken, Installation der Abhängigkeiten und Ausführung. npm run test auf einer bestimmten Version von Node.js.
Der dritte Job, deployEs ist nach beiden Jobs verkettet. Verwendung needs: und verwendet einen Schritt mit curl um die Kinsta-API aufzurufen. Dazu werden der API-Schlüssel und die Anwendungs-ID als Geheimnisse in GitHub konfiguriert (KINSTA_API_KEY y APP_IDund werden im Rahmen der Arbeit exponiert durch env um die POST-Anfrage zu erstellen, die die Bereitstellung auslöst.
Es ist wichtig zu verstehen, dass dieser Bereitstellungsauftrag Kinsta betrachtet die bloße Akzeptanz der API als Erfolg. Sollte die Bereitstellung jedoch intern bei Kinsta fehlschlagen, kann der GitHub-Workflow weiterhin einen grünen Status anzeigen. Dies sollte beachtet werden, um Selbstzufriedenheit zu vermeiden und den Prozess durch eine Überwachung nach der Bereitstellung zu ergänzen.
Erweiterte Cron-Verwaltung und Workflow-Planung
Die CRON-Syntax in GitHub Actions Es basiert auf dem UNIX-Fünf-Feld-Format: Minute, Stunde, Tag des Monats, Monat und Wochentag. Jedes Feld kann verwendet werden Sternchen, Bereiche, Listen und Schritte (*, 1-5, 1,15,30, */5), wodurch die Planung von Wartungsaufgaben, Datensicherungen, Reinigungen oder regelmäßigen Überprüfungen ermöglicht wird.
Zum Beispiel 0 0 * * * Führe den Workflow jede Nacht um Mitternacht aus. (UTC), während 0 8 * * 1 Dies geschieht jeden Montag um 8:00 Uhr. Dies fügt sich nahtlos in die üblichen Auslöser ein. push y pull_requestsodass dieselbe YAML-Datei sowohl auf Codeänderungen als auch auf geplante Ausführungen reagieren kann.
Diese Funktion eignet sich ideal für Aufgaben, bei denen es nicht sinnvoll ist, sie in jedem Commit freizugeben.: intensive Sicherheitsüberprüfungen (z. B. mit OWASP Dependency Check in Java), Abhängigkeitsprüfungen, Testabdeckungsprüfungen oder das Bereinigen alter Artefakte in der Registry.
Workflow-Wiederverwendung: Skalierung von CI/CD auf Hunderte von Repositories
Wenn Ihre Organisation Dutzende oder Hunderte von Repositories hatDas ständige Kopieren und Einfügen desselben YAML-Codes führt unweigerlich zu Chaos. Jede Änderung erfordert die Anpassung der Hälfte von GitHub Enterprise, wodurch es nahezu unmöglich wird, Konsistenz und Best Practices beizubehalten.
Die Lösung liegt in der Entwicklung wiederverwendbarer Arbeitsabläufe. Zentralisiert in einem CI/CD-„Template“-Repository. Diese Workflows stellen Eingaben und Ausgaben bereit, und jeder Dienst definiert lediglich ein kleines YAML-Skript, das ihn aufruft und Parameter wie den Artefakttyp (Docker, Java-Bibliothek, npm-Paket), die Bereitstellungslaufzeitumgebung (GKE, GAE, Cloud Function usw.) oder auszuführende Aufgaben übergibt.
Ein gängiges Vorgehen besteht darin, drei große, wiederverwendbare Arbeitsabläufe zu trennen.: einer von build-check-task (kontinuierliche Integration), ein weiteres von build-release-dockerfile oder andere Artefakte und eine dritte Bereitstellung (deploy-gke, deploy-gaeusw.), sodass jedes Repository seine Pipeline durch die Kombination dieser Repositorys erstellt.
Zur Kapselung gemeinsamer Logik können auch benutzerdefinierte Aktionen definiert werden. en .github/actionsBeispielsweise zur Konfiguration von Gradle, Java, Node oder Task, zum Abrufen von Build-Metadaten, zum Veröffentlichen von Docker-Images, zum Taggen von Versionen in Git mit einem Bash-Skript oder zum Senden von Benachrichtigungen an Slack. Die wichtigste Regel lautet: Service-Repositories sollten ausschließlich wiederverwendbare Workflows nutzen und diese Aktionen nicht direkt ausführen, um die Abwärtskompatibilität zu verbessern.
Schnelle kontinuierliche Integration mit Aufgaben-, Matrizen- und statischer Analyse
Während der Build- oder Prüfphase ist es ratsam, viele Dinge parallel auszulösen.Unit-Tests, statische Analyse (PMD, Checkstyle, SpotBugs in Java; ESLint in JS/TS), Scannen mit SonarCloud usw. Dadurch bleibt die Gesamtlaufzeit der Pipeline auch bei großen Codebasen im Rahmen.
Die Task-Datei (Taskfile.yml) fungiert als Abstraktionsschicht. bei bestimmten Befehlen, sodass der CI-Workflow einfach aufrufen kann task check, task test o task lintBei einem Java-Projekt können diese Aufgaben an Gradle mit JUnit, PMD, Checkstyle und SpotBugs delegiert werden; bei einem Node-Projekt an Jest, ESLint und Sicherheitstools wie … npm audit oder ähnliches.
GitHub Actions fügt das Array-Element hinzu. Um dieselben Aufgaben auf verschiedenen Versionen der Laufzeitumgebung auszuführen, beispielsweise eine Node-Bibliothek auf Version 16, 18 und 20 oder ein Python-Projekt auf Version 3.10 und 3.12, genügt es, ein Array von Versionen zu deklarieren und dieses in der Jobkonfiguration zu verwenden.
Dieser Ansatz ist besonders nützlich in Organisationen, die mehrere Technologie-Stacks unterstützen möchten. (Java, Node, TypeScript, Python usw.) ohne die Pipeline-Logik für jedes Repository neu schreiben zu müssen: Task passt sich jeder Sprache an und die wiederverwendbaren Workflows bleiben praktisch gleich.
Veröffentlichungsphase: Versionierung, Tagging und Veröffentlichung von Artefakten
Sobald die Prüfungen bestanden sind, ist es an der Zeit, das Artefakt zu erstellen, das tatsächlich eingesetzt werden soll.Docker-Image, JAR-Datei, npm-Paket – was auch immer geeignet ist. Dies betrifft sowohl die Sprachwerkzeuge als auch die Registries und die Versionsrichtlinien der Organisation.
Einige Java-Projekte verwenden Plugins wie Gradle Axion. Die Versionsverwaltung erfolgt über Git-Tags. In gemischten Umgebungen (Java, Node usw.) kann es einfacher sein, ein benutzerdefiniertes Bash-Skript zu verwenden, das die nächste Version berechnet (z. B. mithilfe von SemVer), den Tag erstellt, ihn an das Remote-Repository überträgt und das entsprechende Release generiert.
Werkzeuge wie git-cliff Sie helfen bei der Erstellung von Änderungsprotokollen. Anhand der Commit-Nachrichten werden Änderungen nach Typ klassifiziert (Funktion, Fehlerbehebung, Inkompatibilität usw.). Durch die Integration in die Pipeline wird sichergestellt, dass jede Version ein übersichtliches Änderungsprotokoll enthält, ohne dass dieses manuell erstellt werden muss.
Zum Veröffentlichen von Artefakten werden geeignete Aktionen und Anmeldeinformationen kombiniert.Docker-Registries (Docker Hub, GitHub Container Registry, Artifact Registry), Maven-Repositories, npm-Registries usw. Auch hier werden die Zugangsdaten als Geheimnisse gespeichert und nur bei Bedarf in die Jobs eingefügt.
Kontinuierliche Bereitstellung in Kubernetes, GCP, Kinsta und anderen Umgebungen
Die Bereitstellungsphase ist der Punkt, an dem CI/CD mit der Infrastruktur interagiert.Hier integriert sich GitHub Actions nahtlos in nahezu jede Plattform: Kubernetes, App Engine, Cloud Functions, traditionelle Server, Plattformen wie Kinsta usw.
Für Kubernetes (zum Beispiel in GKE) ist das übliche Muster Es geht darum: sich bei Google Cloud authentifizieren (mithilfe offizieller Aktionen), konfigurieren kubectl Wenden Sie innerhalb des Clusterkontexts die Helm-Manifeste oder -Charts an und führen Sie gegebenenfalls einen kontrollierten Rollout durch (z. B. mit Canary oder Blue-Green) und überprüfen Sie den Status mit Befehlen von kubectl rollout status.
Im Falle von App Engine oder Cloud FunctionsDie Pipeline erstellt das Image oder Artefakt, veröffentlicht es in der Artefaktregistrierung und ruft dann die Bereitstellungsbefehle auf. gcloud angemessen, wiederum unter Verwendung von verwalteten Anmeldeinformationen wie Secrets und Ephemeral Runners.
Wenn die Bereitstellung gegen externe APIs wie beispielsweise Kinstas erfolgtNormalerweise genügt ein Schritt. curl oder eine spezielle Aktion, die die Anfrage mit dem Authentifizierungstoken und den erforderlichen Parametern (App-ID, Branch usw.) sendet. Der Vorgang gilt als erfolgreich, wenn die API korrekt auf die Anfrage nach der neuen Version antwortet.
Der Einsatz wird fast immer von einer Benachrichtigung begleitet. Die Daten werden an Slack, Teams oder andere Kommunikationstools übermittelt und enthalten Angaben darüber, welcher Dienst in welcher Umgebung und mit welcher Version bereitgestellt wurde, wer ihn ausgelöst hat und Links zu den Workflow-Protokollen. Im Produktivbetrieb dienen diese Daten außerdem der Prüfung und Nachverfolgbarkeit.
Qualitätskontrolle: Sicherheit, Überwachung und Protokollierung
Die Automatisierung von Build- und Deployment-Prozessen ist großartig, aber ohne Transparenz Was genau passiert, kann in der Pipeline zu einer Blackbox werden. GitHub Actions bietet detaillierte Protokolle nach Ausführung, Job und Schritt, sodass Sie Kompilierungs-, Test- oder Bereitstellungsfehler diagnostizieren können.
Für komplexere Anforderungen werden externe Observability-Dienste integriert. wie beispielsweise Datadog, New Relic oder Splunk, die Metriken zu Arbeitsabläufen, Ausführungszeiten, Fehlerraten usw. sammeln und so dabei helfen, Engpässe zu erkennen und Pipeline-Optimierungen zu priorisieren.
Parallel dazu spielt die Sicherheit eine Schlüsselrolle.: Verwaltung verschlüsselter Geheimnisse, Richtlinien für minimal notwendige Zugriffe, Überprüfung von Aktionsberechtigungen, Einbindung von Code-Schwachstellenscannern und Abhängigkeiten (Code-Scanning, Geheimnis-Scanning, OWASP usw.) in die Arbeitsabläufe selbst.
Viele Teams führen zusätzlich Tests nach der Bereitstellung durch. in der neu aktualisierten Umgebung: durchgängige Funktionstests, Leistungsprüfungen, grundlegende Funktionstests und, falls etwas schiefgeht, automatisierte Rollback-Mechanismen, die die vorherige stabile Version wiederherstellen.
Workflow-Governance: Geschützte Branches und Pull Requests
Die Arbeitsweise mit Branches und Pull Requests muss mit CI/CD übereinstimmen. damit alles Sinn ergibt. Am häufigsten wird der Hauptzweig geschützt (main o master) und verlangen, dass jede Änderung über einen Pull Request (PR) erfolgt und die Pipeline-Prüfungen besteht.
GitHub ermöglicht es Ihnen, Regeln zum Schutz von Branches zu definieren. Diese Richtlinien erzwingen die Verwendung von Pull Requests, blockieren direkte Commits und verlangen, dass bestimmte Statusprüfungen (spezielle Aktions-Workflows) erfolgreich abgeschlossen sein müssen, bevor ein Merge zugelassen wird. Sie können außerdem Mindestrevisionen, Genehmigungsregeln usw. vorschreiben.
Dieses Modell stellt sicher, dass der Code, der die Produktion erreicht, Es wurde einer manuellen Überprüfung und allen automatisierten Pipeline-Prüfungen unterzogen, wodurch das Risiko, dass schwerwiegende Fehler oder Schwachstellen unentdeckt bleiben, drastisch reduziert wird.
In Unternehmen mit mehreren Umgebungen Die Bereitstellung in der Produktionsumgebung (Entwicklung, Staging, Produktion) ist in der Regel dem Zusammenführen von Änderungen in den Hauptzweig vorbehalten, während andere Zweige Bereitstellungen in vorherigen Umgebungen für interne Tests oder Demos auslösen können.
Im Großen und Ganzen betrachtet ist eine gut konzipierte CI/CD-Pipeline mit GitHub Actions Es bildet das Rückgrat der Entwicklung: Änderungen integrieren, umfassende Testreihen ausführen, Artefakte erstellen und veröffentlichen, auf verschiedenen Cloud-Plattformen bereitstellen, mit Observability-Tools überwachen und durch klare Branching- und Pull-Request-Regeln steuern. Mit wiederverwendbaren Workflows, benutzerdefinierten Aktionen, Hilfswerkzeugen wie Task, Rease Action und Git Cliff sowie robustem Geheimnis- und Berechtigungsmanagement lassen sich einfache Python-Anwendungen ebenso wie komplexe Kubernetes-Architekturen unterstützen. Dabei bleiben Bereitstellungsgeschwindigkeit, Codequalität und Sicherheit erhalten, ohne das Team mit manuellen Aufgaben zu überlasten.