Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Schnellere Veröffentlichungszyklen sind ein wichtiger Vorteil von Microservices-Architekturen. Ohne eine zuverlässige kontinuierliche Integration und kontinuierliche Lieferung (CI/CD) verlieren Sie die Flexibilität, die Microservices bieten. In diesem Artikel werden allgemeine CI/CD-Herausforderungen in Microservices-Architekturen beschrieben und Ansätze zum Erstellen, Überprüfen, Sichern und Unabhängigen Bereitstellen von Diensten empfohlen.
Was ist CI/CD?
CI/CD bezieht sich auf mehrere verwandte Prozesse: kontinuierliche Integration, kontinuierliche Auslieferung und kontinuierliche Bereitstellung.
Kontinuierliche Integration (CI): Codeänderungen werden häufig mit der Hauptzweigung zusammengeführt. Automatisierte Build- und Testprozesse stellen sicher, dass Code in der Hauptzweigung immer produktionsqualität ist.
Kontinuierliche Bereitstellung (CD): Codeänderungen, die den CI-Prozess erfolgreich durchlaufen, werden automatisch in einer produktionsähnlichen Umgebung bereitgestellt. Die Bereitstellung in der Live-Produktionsumgebung erfordert möglicherweise eine manuelle Genehmigung, ist aber andernfalls automatisiert. Das Ziel ist, dass Ihr Code immer für die Bereitstellung in der Produktion bereit ist.
Kontinuierliche Bereitstellung: Codeänderungen, die die beiden vorherigen Schritte erfolgreich durchlaufen, werden automatisch in die Produktion ausgerollt.
Berücksichtigen Sie die folgenden Ziele eines robusten CI/CD-Prozesses für eine Microservices-Architektur:
Jedes Team kann die Dienste, die es besitzt, unabhängig erstellen und bereitstellen, ohne andere Teams zu beeinträchtigen oder zu stören.
Bevor eine neue Version eines Diensts in der Produktion bereitgestellt wird, wird er zur Überprüfung in Entwicklungs-/Test- und QA-Umgebungen bereitgestellt. Qualitätsschranken kommen in jeder Phase zur Anwendung.
Eine neue Version eines Diensts kann parallel zur vorherigen Version bereitgestellt werden.
Es sind ausreichende Richtlinien für die Zugriffssteuerung vorhanden. Pipelines authentifizieren sich bei Azure mit kurzlebigen Verbundanmeldeinformationen anstelle von langlebigen Geheimnissen.
Bei containerisierten Workloads können Sie den Containerimages vertrauen, die in der Produktion eingesetzt werden. Diese Vertrauensstellung wird durch signierte Bilder, SBOM-Nachweise (Software Bill of Materials) und die in der Pipeline erzwungene Sicherheitsrisikoüberprüfung eingerichtet.
Warum eine robuste CI/CD-Pipeline wichtig ist
In einer herkömmlichen monolithischen Anwendung erzeugt eine einzelne Build-Pipeline die ausführbare Datei der Anwendung. Alle Entwicklungsarbeiten fließen in diese Pipeline. Wenn das Team einen Fehler mit hoher Priorität findet, muss der Fix integriert, getestet und veröffentlicht werden, wodurch die Veröffentlichung neuer Features verzögert werden kann. Sie können diese Probleme reduzieren, indem Sie gut formatierte Module und Featureverzweigungen verwenden, um die Auswirkungen von Codeänderungen zu begrenzen. Aber je komplexer die Anwendung wird und je mehr Funktionen hinzugefügt werden, desto komplizierter und fehleranfälliger wird in der Regel der Releaseprozess für einen Monolithen.
Nach der Microservices-Philosophie sollte es niemals einen langen Release-Zyklus geben, in den sich jedes Team einreihen muss. Das Team, das Dienst A erstellt, kann ein Update freigeben, wenn es sich entscheidet und nicht auf Änderungen im Dienst B warten muss, um zusammenzuführen, zu testen und bereitzustellen.
Um eine hohe Veröffentlichungsgeschwindigkeit zu erreichen, muss Ihre Veröffentlichungspipeline automatisiert und sehr zuverlässig sein, um risiken zu minimieren. Wenn Sie eine oder mehrere Male täglich in die Produktion freigeben, müssen Regressionen oder Dienstunterbrechungen selten sein. Wenn Sie ein fehlerhaftes Update bereitstellen, müssen Sie gleichzeitig eine zuverlässige Möglichkeit haben, einen schnellen Rollback oder einen Roll forward auf eine frühere Version eines Diensts zu erstellen.
Challenges
Viele kleine unabhängige Codebasen: Jedes Team ist für die Erstellung eines eigenen Diensts mit eigener Buildpipeline verantwortlich. In einigen Organisationen verwenden Teams möglicherweise separate Coderepositorys. Separate Repositories können das Wissen darüber, wie das System aufgebaut wird, auf verschiedene Teams verteilen. Daher weiß niemand in der Organisation, wie die gesamte Anwendung bereitgestellt wird.
Abhilfe: Verwenden Sie eine einheitliche und automatisierte Pipeline oder zumindest eine gemeinsame Pipeline-Infrastruktur, um Dienste zu erstellen und bereitzustellen, damit dieses Wissen nicht in den einzelnen Teams verborgen bleibt. Wiederverwendbare Pipelinevorlagen, z. B. GitHub Actions wiederverwendbare Workflows oder Azure Pipelines vorlagen, helfen Beim Standardisieren des Builds, Testens, Scannens und Bereitstellens von Schritten für jeden Dienst.
Mehrere Sprachen und Frameworks: Jedes Team verwendet eine eigene Mischung aus Technologien, sodass es schwierig sein kann, einen einzelnen Buildprozess zu erstellen, der über die Arbeitsauslastung hinweg funktioniert. Der Buildprozess muss flexibel genug sein, damit jedes Team ihn für die gewählte Sprache oder das ausgewählte Framework anpassen kann.
Abhilfemaßnahme: Kapseln Sie den Build-Prozess für jeden Dienst in Containern, sodass das Buildsystem nur die Container ausführen muss. Plattformen wie GitHub Actions, Azure Pipelines und Azure Container Registry-Aufgaben können Container-Images unabhängig von der Quellsprache konsistent erstellen und veröffentlichen.
Integrations- und Auslastungstests: Teams veröffentlicht Updates in ihrem eigenen Tempo, sodass es schwierig sein kann, robuste End-to-End-Tests zu entwerfen, insbesondere wenn Dienste Abhängigkeiten von anderen Diensten haben. Das Ausführen eines vollständigen Produktionsclusters kann kostspielig sein, sodass es unwahrscheinlich ist, dass jedes Team seinen eigenen vollständigen Cluster im Produktionsmaßstab nur für Tests ausführt.
Gegenmaßnahme: Verwenden Sie kurzlebige Vorschauumgebungen, z. B. für jeden Pull Request eigene Namespaces in Kubernetes oder Azure-Container-Apps-Umgebungen, die bei Bedarf erstellt werden. Verwenden Sie Vertragstests, damit Sie Integrationsprobleme frühzeitig anzeigen können, ohne dass ein vollständiger Duplikat der Produktion erforderlich ist.
Versionsverwaltung: Jedes Team sollte in der Lage sein, ein Update für die Produktion bereitzustellen. Diese Anforderung bedeutet nicht, dass jedes Teammitglied über berechtigungen zum Bereitstellen verfügt. Eine zentrale Release-Manager-Rolle kann die Bereitstellungsgeschwindigkeit verringern.
Abhilfe: Je stärker Ihr CI/CD-Prozess automatisiert ist und je zuverlässiger er funktioniert, desto weniger benötigen Sie eine zentrale Instanz. Möglicherweise verfügen Sie immer noch über unterschiedliche Richtlinien für die Veröffentlichung wichtiger Featureupdates im Vergleich zu kleineren Fehlerbehebungen. Ein dezentraler Ansatz bedeutet keine Zero Governance. Erzwingen von Genehmigungen mithilfe von Azure Pipelines Umgebungen und Genehmigungen oder GitHub Actions Bereitstellungsumgebungen und erforderlichen Prüfern und codieren Sie clusterseitige Richtlinien mithilfe von Azure Policy für Azure Kubernetes Service (AKS) oder OPA Gatekeeper.
Dienstupdates: Wenn Sie einen Dienst auf eine neue Version aktualisieren, sollte das Update nicht dazu führen, dass andere Dienste fehlschlagen.
Risikominderung: Verwenden Sie Bereitstellungstechniken wie Blue-Green-Deployments oder Canary-Releases für Änderungen ohne Kompatibilitätsbruch. Um API-Änderungen zu unterbrechen, stellen Sie die neue Version nebeneinander mit der vorherigen Version bereit. Bei diesem Ansatz können Dienste, die die vorherige API nutzen, für die neue API aktualisiert und getestet werden. Weitere Informationen finden Sie unter Aktualisieren von Diensten.
Pipelineidentitäten und Verwaltung von Geheimnissen: Langlebige Geheimnisse von Dienstprinzipalen, die in Pipelines gespeichert sind, sind eine häufige Ursache für Sicherheitskompromittierungen und Betriebsaufwand. Serviceprinzipal-Geheimnisse laufen ab, können offengelegt werden und müssen über viele unabhängige Microservice-Pipelines hinweg rotiert werden.
Mitigation: Authentifizieren Sie Pipelines für Azure mit dem Arbeitsauslastungsidentitätsverbund, der OpenID Connect (OIDC) verwendet, sodass kein geheimer Clientschlüssel in der Pipeline gespeichert wird. Weitere Informationen finden Sie unter Workload-Identitäten für Azure Pipelines und Configure OpenID Connect in Azure für GitHub Actions. Speichern Sie alle verbleibenden geheimen Schlüssel in Azure Key Vault und verweisen Sie zur Laufzeit darauf.
Lieferkettensicherheit: Alles, was Sie an die Produktion versenden, muss dem Code und den Abhängigkeiten, aus denen sie erstellt wurde, nachverfolgt werden können. Microservices erhöhen die Anzahl der Bilder, Registrierungen und Pipelines, wodurch die Angriffsfläche der Lieferkette erhöht wird.
Gegenmaßnahme: Signieren Sie Containerimages mithilfe von Notation und Key Vault und überprüfen Sie Signaturen bei der Zulassung mithilfe von AKS image integrity oder Ratify. Generieren Sie ein SBOM als Buildartefakt. Scannen Sie Code, Abhängigkeiten und Pipelines mithilfe von Microsoft Defender for Cloud DevOps-Sicherheit und GitHub Advanced Security. Scannen Sie Laufzeitimages mithilfe von Microsoft Defender für Container. Alle Scans müssen erfolgreich sein, bevor eine Freigabe erfolgen kann.
Monorepo im Vergleich zu Multirepo
Bevor Sie einen CI/CD-Workflow erstellen, müssen Sie wissen, wie die Codebasis strukturiert und verwaltet wird, einschließlich:
- Unabhängig davon, ob Teams in separaten Repositorys oder einem Monorepo arbeiten.
- Ihre Verzweigungsstrategie.
- Wer Code in die Produktion übertragen kann und ob ein Release-Manager vorhanden ist.
Teams verwenden häufig beide Ansätze in der Produktion. Ihre Wahl hängt von der Teamtopologie, dem Reifegrad der Tools und davon ab, wie viel Code gemeinsam von mehreren Diensten genutzt wird.
| Monorepo | Mehrere Repositorien | |
|---|---|---|
| Vorteile | - Code teilen - Einfachere Standardisierung von Code und Tools - Code leichter refaktorisieren - Auffindbarkeit (eine einzelne Ansicht des Codes) |
- Klare Zuständigkeit pro Team - Potenziell weniger Zusammenführungskonflikte - Hilft beim Erzwingen der Microservice-Entkopplung |
| Herausforderungen | - Änderungen am freigegebenen Code können sich auf mehrere Microservices auswirken - Größeres Potenzial für Zusammenführungskonflikte - Werkzeuge müssen für eine große Codebasis skalierbar sein - Zugriffssteuerung – Komplexerer Bereitstellungsprozess |
- Code ist schwieriger zu teilen - Schwieriger, Codierungsstandards zu erzwingen - Abhängigkeitsverwaltung - Diffuse Codebasis, schlechte Auffindbarkeit - Fehlende gemeinsam genutzte Infrastruktur |
Verwenden Sie, unabhängig davon, für welches Modell Sie sich entscheiden, in Ihren Pipelines pfadbezogene Auslöser wie Pfadfilter in GitHub Actions oder Triggerpfade in Azure Pipelines. Pfadbezogene Trigger tragen dazu bei, sicherzustellen, dass bei jedem Commit nur betroffene Microservices neu erstellt und erneut bereitgestellt werden.
Dienste aktualisieren
Es gibt verschiedene Strategien zum Aktualisieren eines Dienstes, der bereits produktiv ist, darunter Rolling Update, Blue-Green-Deployment und Canary-Release. Diese Muster werden häufig über einen GitOps-Workflow koordiniert. Weitere Informationen finden Sie unter GitOps und progressive Zustellung.
Einsatz von fortlaufenden Updates
In einem rollierenden Update stellen Sie neue Instanzen eines Diensts bereit, und die neuen Instanzen beginnen sofort mit dem Empfangen von Anforderungen. Sobald die neuen Instanzen bereit sind, werden die vorherigen Instanzen entfernt.
Beispiel in Kubernetes: In Kubernetes sind rollierende Updates das Standardverhalten, wenn Sie die Pod-Spezifikation für eine Bereitstellung aktualisieren. Der Deployment-Controller erstellt ein neues ReplicaSet für die aktualisierten Pods. Anschließend wird das neue ReplicaSet skaliert, während das vorherige ReplicaSet herunterskaliert wird, um die gewünschte Replikatanzahl beizubehalten. Es werden keine vorherigen Pods gelöscht, bis die neuen Pods bereit sind. Kubernetes behält einen Verlauf des Updates bei, sodass Sie bei Bedarf ein Rollback für ein Update ausführen können.
Beispiel in Container Apps: Container Apps verwenden Revisionen zur Verwaltung von fortlaufenden Updates. Wenn Sie eine neue Revision bereitstellen, können Container-Apps den Datenverkehr mithilfe von Regeln zur Datenverkehrsaufteilung schrittweise von der vorherigen auf die neue Revision verlagern. Wenn bei der neuen Revision Probleme auftreten, können Sie einen Rollback ausführen, indem Sie den Datenverkehr zur vorherigen Überarbeitung umleiten. Sie können mehrere aktive Überarbeitungen gleichzeitig konfigurieren und den Prozentsatz des Datenverkehrs steuern, den jede Überarbeitung empfängt.
Eine Herausforderung bei fortlaufenden Updates besteht darin, dass während des Aktualisierungsprozesses ältere und neue Versionen gleichzeitig ausgeführt werden und Traffic erhalten. Während dieses Zeitraums kann das System jede Anforderung an eine der beiden Versionen weiterleiten.
Zum Unterbrechen von API-Änderungen empfiehlt es sich, beide Versionen nebeneinander zu unterstützen, bis alle Clients der vorherigen Version aktualisiert werden. Weitere Informationen finden Sie unter API-Versionsverwaltung.
Blau-Grün-Bereitstellung
In einer blaugrünen Bereitstellung stellen Sie die neue Version zusammen mit der vorherigen Version bereit. Nachdem Sie die neue Version überprüft haben, wechseln Sie den gesamten Datenverkehr auf einmal von der vorherigen Version zur neuen Version. Nach dem Wechsel überwachen Sie die Anwendung auf Probleme. Wenn ein Problem vorliegt, können Sie den Datenverkehr zurück zur vorherigen Version wechseln. Wenn keine Probleme auftreten, können Sie die vorherige Version löschen.
Mit einer herkömmlicheren monolithischen oder N-stufigen Anwendung bedeutet die blaugrüne Bereitstellung im Allgemeinen, dass Sie zwei identische Umgebungen erstellen. Sie stellen die neue Version in einer Stagingumgebung bereit und leiten dann den Clientdatenverkehr an diese Umgebung um, z. B. durch Austauschen einer virtuellen IP-Adresse. In einer Microservices-Architektur treten Updates auf Microservice-Ebene auf, sodass Sie das Update in der Regel in derselben Umgebung bereitstellen und einen Dienstermittlungsmechanismus verwenden, um den Datenverkehr zu wechseln.
Beispiel in Kubernetes: In Kubernetes müssen Sie keinen separaten Cluster erstellen, um blaugrüne Bereitstellungen durchzuführen. Stattdessen können Sie Selektoren nutzen. Erstellen Sie eine neue Bereitstellungsressource mit einer neuen Pod-Spezifikation und einer anderen Gruppe von Bezeichnungen. Erstellen Sie diese Bereitstellung, löschen Sie jedoch nicht die vorherige Bereitstellung, oder ändern Sie den Dienst, der darauf verweist. Nachdem die neuen Pods laufen, können Sie den Selector des Dienstes so aktualisieren, dass er mit der neuen Bereitstellung übereinstimmt.
Ein Nachteil des Blue-Green-Deployments ist, dass Sie während des Updates doppelt so viele Pods für den Dienst (aktuelle und nächste) ausführen. Wenn die Pods erhebliche CPU- oder Arbeitsspeicherressourcen verwenden, müssen Sie den Cluster möglicherweise vorübergehend skalieren, um den höheren Ressourcenbedarf zu erfüllen.
Canary-Version
In einer Canaryversion stellen Sie eine aktualisierte Version für eine kleine Teilmenge von Clients bereit und überwachen dann das Verhalten des neuen Diensts, bevor Sie sie für alle Clients bereitstellen. Mit diesem Ansatz können Sie die Einführung schrittweise und kontrolliert vornehmen, reale Daten überwachen und Probleme identifizieren, bevor sie alle Kunden betreffen.
Ein Canary-Release ist komplexer zu verwalten als ein Blue-Green-Deployment oder ein Rolling Update, da Sie Anfragen dynamisch an unterschiedliche Versionen des Dienstes weiterleiten müssen.
Beispiel in Kubernetes: In Kubernetes können Sie einen Dienst so konfigurieren, dass er zwei Replikatgruppen (eine für jede Version) umfasst und die Replikatanzahl manuell anpasst. Dieser Ansatz ist jedoch grobgranular, da Kubernetes die Last über Pods hinweg verteilt. Wenn Sie beispielsweise über insgesamt 10 Replikate verfügen, können Sie den Datenverkehr nur in 10 %-Schritten verschieben. Wenn Sie ein Service Mesh verwenden, können Sie die Routing-Regeln des Service Mesh verwenden, um eine ausgereiftere Canary-Release-Strategie zu implementieren.
Beispiel in Container Apps: In Container Apps können Sie die Datenverkehrsaufteilung verwenden, um einen festgelegten Prozentsatz des Datenverkehrs an eine neue Revision zu senden (z. B. 10 % an v2, während 90 % auf v1 verbleiben) und die Gewichtung schrittweise verlagern, wenn das Vertrauen steigt, ohne dass ein externes Service Mesh erforderlich ist.
Progressive Zustellung und GitOps
Für Teams, die viele Microservices auf Kubernetes betreiben, ergänzt ein GitOps Pull-basiertes Modell die früheren Push-basierten Beispiele. Der gewünschte Clusterstatus befindet sich in Git, und ein In-Cluster-Operator versöhnt den Cluster mit diesem Zustand. CI erstellt, testet, scannt, signiert und pusht das Image. CD gleicht den Cluster mit dem Manifest ab. Diese Trennung bietet Ihnen Überwachungspfade und einfachere Notfallwiederherstellung (DR). Außerdem macht es die Notwendigkeit überflüssig, dass der CI-Runner über direkte Anmeldeinformationen für den Cluster verfügt.
Nächste Schritte
- Lernpfad: Definieren und Implementieren von CI
- Schulung: Entwicklung für Enterprise DevOps
- Microservices-Architektur