Entwicklung mit DevOps durch Microsoft

Microsoft ist bestrebt, One Engineering System zu verwenden, um alle Microsoft-Produkte mit einem soliden DevOps-Prozess zu entwickeln und bereitzustellen, der auf einem Git-Verzweigungs- und Versionsfluss basiert. In diesem Artikel werden die praktische Implementierung, die Skalierbarkeit des Systems von kleinen Diensten bis hin zu umfangreichen Plattformentwicklungsanforderungen sowie die Erfahrungen aus dem Einsatz des Systems in verschiedenen Microsoft-Teams beleuchtet.

Die Einführung eines standardisierten Entwicklungsprozesses ist ein ehrgeiziges Unterfangen. Die Anforderungen der verschiedenen Microsoft-Organisationen sind sehr unterschiedlich, und die Anforderungen der verschiedenen Teams innerhalb der Organisationen variieren je nach Größe und Komplexität. Um diesen unterschiedlichen Anforderungen gerecht zu werden, verwendet Microsoft eine trunk-basierte Verzweigungsstrategie, um Produkte schnell zu entwickeln, sie regelmäßig bereitzustellen und Änderungen sicher in die Produktion zu übertragen.

Microsoft verwendet zudem Plattform-Engineering-Prinzipien als Teil des One Engineering Systems.

Microsoft Freigabefluss

Jedes Unternehmen sollte sich auf einen Standardprozess für die Freigabe von Code einigen, um die Konsistenz zwischen den Teams zu gewährleisten. Der Versionsfluss von Microsoft umfasst DevOps-Prozesse von der Entwicklung bis zur Veröffentlichung. Die grundlegenden Schritte des Versionsflusses bestehen aus Branch, Push, Pull Request und Merge.

Verzweigung

Um einen Fehler zu beheben oder eine Funktion zu implementieren, erstellt ein Entwickler einen neuen Zweig neben dem Hauptintegrationszweig. Das leichtgewichtige Verzweigungsmodell von Git erstellt diese kurzlebigen topic Zweige für jeden Codebeitrag. Durch die Verwendung von feature flagskönnen Entwickler frühzeitig Commits durchführen und langwierige Feature-Zweige vermeiden.

Push

Wenn der Entwickler bereit ist, Änderungen zu integrieren und an den Rest des Teams weiterzugeben, verschiebt er seinen lokalen Zweig in einen Zweig auf dem Server und öffnet eine Pull-Anfrage. Repositories mit mehreren hundert Entwicklern, die in vielen Zweigen arbeiten, verwenden eine Namenskonvention für Serverzweige, um Verwirrung zu vermeiden und Zweigwucherung. Entwickler erstellen in der Regel Zweige mit dem Namen users/<username>/feature, wobei <username> ihr Kontoname ist.

Pull Request

Pull-Requests steuern die Zusammenführung von Themenzweigen in den Hauptzweig und stellen sicher, dass die Zweigrichtlinien eingehalten werden. Der Pull-Request-Prozess erstellt die vorgeschlagenen Änderungen und führt einen schnellen Testdurchlauf durch. Die Testsuiten der ersten und zweiten Ebene führen rund 60.000 Tests in weniger als fünf Minuten durch. Dies ist nicht die vollständige Microsoft-Testmatrix, aber sie reicht aus, um schnell Vertrauen in Pull-Anfragen zu schaffen.

Anschließend überprüfen die anderen Teammitglieder den Code und genehmigen die Änderungen. Die Codeüberprüfung setzt dort an, wo die automatisierten Tests aufgehört haben, und ist besonders nützlich, um architektonische Probleme aufzuspüren. Manuelle Code-Reviews stellen sicher, dass die anderen Ingenieure im Team Einblick in die Änderungen haben und dass die Codequalität hoch bleibt.

Merge

Sobald die Pull-Anfrage alle Build-Richtlinien erfüllt und die Prüfer abgezeichnet haben, wird der Themenzweig mit dem Hauptintegrationszweig zusammengeführt, und die Pull-Anfrage ist abgeschlossen.

Nach der Zusammenführung werden weitere Akzeptanztests durchgeführt, die mehr Zeit in Anspruch nehmen. Diese traditionellen Post-Checkin-Tests führen eine gründlichere Validierung durch. Dieser Testprozess bietet ein gutes Gleichgewicht zwischen schnellen Tests während der Überprüfung von Pull Requests und einer vollständigen Testabdeckung vor der Veröffentlichung.

Unterschiede zu GitHub Flow

GitHub Flow ist eine beliebte trunk-basierte Entwicklung Release Flow für Organisationen, um einen skalierbaren Ansatz für Git zu implementieren. Einige Organisationen stellen jedoch fest, dass sie mit wachsenden Anforderungen von Teilen des GitHub-Flusses abweichen müssen.

Ein oft übersehener Teil von GitHub Flow ist zum Beispiel, dass Pull-Requests zum Testen in die Produktion übertragen werden müssen, bevor sie mit dem Hauptzweig zusammengeführt werden können. Dieser Prozess bedeutet, dass alle Pull Requests in der Deployment-Warteschlange auf die Zusammenführung warten.

Manche Teams haben mehrere hundert Entwickler, die ständig an einem einzigen Repository arbeiten und pro Tag über 200 Pull-Requests in den Hauptzweig stellen können. Wenn für jede Pull-Anforderung eine Bereitstellung in mehreren Azure-Rechenzentren auf der ganzen Welt zum Testen erforderlich ist, verbringen die Entwickler ihre Zeit damit, auf die Zusammenführung von Zweigen zu warten, anstatt Software zu schreiben.

Stattdessen setzen die Microsoft-Teams die Entwicklung im Hauptzweig fort und unterteilen die Bereitstellungen in zeitlich festgelegte Releases, die in der Regel auf einen dreiwöchigen sprint abgestimmt sind.

Details zur Implementierung

Im Folgenden finden Sie einige wichtige Details zur Implementierung des Microsoft-Freigabeverfahrens:

Git-Repository-Strategie

Verschiedene Teams haben unterschiedliche Strategien für die Verwaltung ihrer Git-Repositories. Einige Teams behalten den Großteil ihres Codes in einem Git-Repository. Der Code ist in Komponenten aufgeteilt, die sich jeweils in einem eigenen Stammverzeichnis befinden. Große Komponenten, insbesondere ältere Komponenten, können mehrere Unterkomponenten haben, die innerhalb der übergeordneten Komponente separate Unterordner haben.

Screenshot showing a Git repository structure.

Ergänzende Repositorien

Einige Teams verwalten auch zusätzliche Repositories. So werden beispielsweise die Build- und Release- Agenten und Aufgaben, die VS Code-Erweiterung und Open-Source-Projekte auf GitHub entwickelt. Konfigurationsänderungen werden in ein separates Repository eingecheckt. Andere Pakete, auf die das Team angewiesen ist, stammen von anderen Stellen und werden über NuGet bezogen.

Mono-Repo oder Multi-Repo

Während einige Teams sich für ein einziges monolithisches Repository entscheiden, das mono-repo, verwenden andere Microsoft-Produkte einen multi-repo Ansatz. Skype beispielsweise verfügt über Hunderte von kleinen Repositories, die in verschiedenen Kombinationen zusammengefügt werden, um viele verschiedene Clients, Dienste und Tools zu erstellen. Insbesondere für Teams, die Microservices einsetzen, kann Multi-Repo der richtige Ansatz sein. In der Regel finden ältere Produkte, die als Monolithen begonnen haben, einen Mono-Repo-Ansatz als einfachsten Übergang zu Git, und ihre Codeorganisation spiegelt dies wider.

Releasebranches

Der Versionsfluss von Microsoft sorgt dafür, dass der Hauptzweig jederzeit baubar ist. Die Entwickler arbeiten in kurzlebigen Themenzweigen, die auf mainzusammengeführt werden. Wenn ein Team zur Auslieferung bereit ist, sei es am Ende eines Sprints oder bei einer größeren Aktualisierung, wird ein neuer Versionszweig neben dem Hauptzweig angelegt. Release-Zweige werden nie in den Hauptzweig zurückgeführt, so dass sie möglicherweise cherry-picking wichtige Änderungen erfordern.

Das folgende Diagramm zeigt kurzlebige Zweige in blau und Freigabezweige in schwarz. Ein Zweig mit einer Übergabe, die entjungfert werden muss, erscheint in Rot.

Diagram showing Git release branch structure.

Zweigstellenrichtlinien und Berechtigungen

Git-Zweigrichtlinien helfen dabei, die Struktur des Versionszweigs durchzusetzen und den Hauptzweig sauber zu halten. So können beispielsweise Zweigrichtlinien verhindern, dass direkte Pushs in den Hauptzweig erfolgen.

Um die Verzweigungshierarchie übersichtlich zu halten, verwenden Teams Berechtigungen, um die Erstellung von Verzweigungen auf der Stammebene der Hierarchie zu blockieren. Im folgenden Beispiel kann jeder Zweige in Ordnern wie users/, features/, und teams/erstellen. Nur Versionsverwalter haben die Berechtigung, Zweige unter releases/zu erstellen, und einige Automatisierungswerkzeuge haben die Berechtigung für den Ordner integrations/.

Screenshot that shows branches.

Arbeitsablauf im Git-Repository

Innerhalb der Repository- und Branch-Struktur erledigen die Entwickler ihre tägliche Arbeit. Das Arbeitsumfeld ist je nach Team und Person sehr unterschiedlich. Einige Entwickler bevorzugen die Befehlszeile, andere Visual Studio, und wieder andere arbeiten auf verschiedenen Plattformen. Die Strukturen und Richtlinien in den Microsoft-Repositories sorgen für eine solide und konsistente Grundlage.

Ein typischer Arbeitsablauf umfasst die folgenden allgemeinen Aufgaben:

Erstellen Sie eine neue Funktion

Die Entwicklung einer neuen Funktion ist das Kernstück der Arbeit eines Softwareentwicklers. Zu den Teilen des Prozesses, die nicht zu Git gehören, gehören die Auswertung von Telemetriedaten, die Entwicklung eines Entwurfs und einer Spezifikation sowie das Schreiben des eigentlichen Codes. Dann beginnt der Entwickler, mit dem Repository zu arbeiten, indem er sich mit der letzten Übergabe auf mainsynchronisiert. Der Hauptzweig ist immer baubar, also garantiert ein guter Ausgangspunkt. Der Entwickler checkt einen neuen Funktionszweig aus, nimmt Codeänderungen vor, überträgt diese, schiebt sie auf den Server und startet eine neue Pull-Anfrage.

Verwendung von Zweigstellenrichtlinien und -kontrollen

Bei der Erstellung einer Pull-Anforderung überprüfen automatisierte Systeme, ob der neue Code gebaut werden kann, keine Fehler enthält und nicht gegen Sicherheits- oder Compliance-Richtlinien verstößt. Dieser Prozess verhindert nicht, dass andere Arbeiten parallel dazu ausgeführt werden.

Verzweigungsrichtlinien und -prüfungen können einen erfolgreichen Build einschließlich bestandener Tests, Abzeichnung durch die Eigentümer jeglichen berührten Codes und mehrere externe Prüfungen zur Verifizierung der Unternehmensrichtlinien erfordern, bevor ein Pull Request abgeschlossen werden kann.

Screenshot showing the checks on a pull request.

In Microsoft Teams integrieren

Viele Teams konfigurieren die -Integration mit Microsoft Teams, wodurch die neue Pull-Anfrage den Teamkollegen der Entwickler mitgeteilt wird. Die Eigentümer des berührten Codes werden automatisch als Prüfer hinzugefügt. Microsoft-Teams setzen häufig optionale Prüfer für Code ein, der von vielen Personen berührt wird, z. B. für die Erstellung von REST-Clients und gemeinsam genutzten Steuerelementen, um diese Änderungen von Experten prüfen zu lassen.

Screenshot showing Teams integration.

Screenshot showing Teams notification of a pull request.

Einsatz mit Merkmalskennzeichen

Sobald die Prüfer, die Code-Eigentümer und die Automatisierung zufrieden sind, kann der Entwickler die Pull-Anfrage abschließen. Wenn es einen Merge-Konflikt gibt, erhält der Entwickler Anweisungen, wie er den Konflikt synchronisieren, beheben und die Änderungen erneut veröffentlichen kann. Die Automatisierung läuft wieder auf dem festgelegten Code, aber der Mensch muss sich nicht noch einmal abmelden.

Der Zweig wird in mainzusammengeführt, und der neue Code wird im nächsten Sprint oder in der nächsten Hauptversion bereitgestellt. Das bedeutet aber nicht, dass die neue Funktion sofort verfügbar sein wird. Microsoft entkoppelt die Bereitstellung und Freigabe neuer Funktionen durch die Verwendung von feature flags.

Selbst wenn die Funktion noch ein wenig mehr Arbeit benötigt, bevor sie vorzeigbar ist, kann sie sicher auf main veröffentlicht werden, wenn das Produkt gebaut und bereitgestellt werden kann. Unter mainwird der Code Teil eines offiziellen Builds, wo er erneut getestet, auf die Einhaltung der Richtlinien geprüft und digital signiert wird.

Linksverschiebung zur Früherkennung von Problemen

Dieser Git-Arbeitsablauf bietet mehrere Vorteile. Erstens wird durch die Arbeit von einer einzigen Hauptniederlassung aus die fusionierten Schulden praktisch eliminiert. Zweitens bietet der Pull-Request-Fluss einen gemeinsamen Punkt, um Tests, Code-Reviews und Fehlererkennung in einem frühen Stadium der Pipeline zu erzwingen. Diese Strategie shift left trägt dazu bei, den Feedback-Zyklus für Entwickler zu verkürzen, da Fehler innerhalb von Minuten und nicht erst nach Stunden oder Tagen erkannt werden können. Diese Strategie gibt auch Sicherheit beim Refactoring, da alle Änderungen ständig getestet werden.

Derzeit kann ein Produkt mit mehr als 200 Pull Requests mehr als 300 Continuous Integration Builds pro Tag erzeugen, was mehr als 500 Testläufen alle 24 Stunden entspricht. Ohne die Stamm-basierte Verzweigung und den Freigabe-Workflow wäre ein solches Maß an Tests unmöglich.

Freigabe zu Sprint-Meilensteinen

Am Ende eines jeden Sprints erstellt das Team aus dem Hauptzweig einen Release-Zweig. Zum Beispiel erstellt das Team am Ende von Sprint 129 einen neuen Versionszweig releases/M129. Das Team bringt dann den Sprint 129-Zweig in Produktion.

Nach der Verzweigung des Veröffentlichungszweigs bleibt der Hauptzweig für Entwickler offen, um Änderungen zusammenzuführen. Diese Änderungen werden drei Wochen später bei der nächsten Sprint-Einführung umgesetzt.

Illustration of the release branch at sprint 129.

Hotfixes veröffentlichen

Manchmal müssen Änderungen schnell in die Produktion gehen. Microsoft fügt in der Regel keine neuen Funktionen in der Mitte eines Sprints hinzu, sondern möchte manchmal schnell einen Fehler beheben, um die Benutzer zu entlasten. Probleme können geringfügig sein, wie z. B. Tippfehler, oder groß genug, um ein Problem mit der Verfügbarkeit oder live site incidentzu verursachen.

Die Behebung dieser Probleme beginnt mit dem normalen Arbeitsablauf. Ein Entwickler erstellt einen Zweig von main, lässt ihn auf seinen Code überprüfen und vervollständigt den Pull Request, um ihn zusammenzuführen. Der Prozess beginnt immer damit, dass zuerst die Änderung auf main vorgenommen wird. Auf diese Weise kann die Korrektur schnell erstellt und lokal validiert werden, ohne dass zum Versionszweig gewechselt werden muss.

Die Einhaltung dieses Prozesses garantiert auch, dass die Änderung in mainaufgenommen wird, was entscheidend ist. Die Behebung eines Fehlers im Freigabezweig, ohne die Änderung zurück auf main zu bringen, würde bedeuten, dass der Fehler beim nächsten Einsatz wieder auftritt, wenn der Sprint 130 Freigabezweige von main freigibt. In der Verwirrung und dem Stress, die während eines Stromausfalls entstehen können, vergisst man leicht, main zu aktualisieren. Änderungen zuerst auf main zu bringen bedeutet, dass die Änderungen immer sowohl im Hauptzweig als auch im Versionszweig vorhanden sind.

Die Git-Funktionalität ermöglicht diesen Arbeitsablauf. Um Änderungen sofort in die Produktion zu bringen, kann ein Entwickler, sobald er eine Pull-Anfrage in mainzusammenführt, die Pull-Anfrage-Seite nutzen, um die Änderungen in den Release-Zweig zu übernehmen. Dieser Prozess erstellt eine neue Pull-Anfrage, die auf den Release-Zweig abzielt und den Inhalt, der gerade in mainzusammengeführt wurde, zurückportiert.

Illustration of cherry-picking a hotfix commit into branch 129.

Mit der Cherry-Pick-Funktion wird eine Pull-Anfrage schnell geöffnet und bietet die Nachvollziehbarkeit und Zuverlässigkeit von Verzweigungsrichtlinien. Das Cherry-Picking kann auf dem Server erfolgen, ohne dass der Versionszweig auf einen lokalen Computer heruntergeladen werden muss. Änderungen, das Beheben von Konflikten bei der Zusammenführung oder kleinere Änderungen aufgrund von Unterschieden zwischen den beiden Zweigen können alle auf dem Server vorgenommen werden. Teams können Änderungen direkt über den browserbasierten Texteditor oder über die Pull Request Merge Conflict Extension bearbeiten, was eine fortgeschrittenere Erfahrung darstellt.

Sobald eine Pull-Anfrage auf den Veröffentlichungszweig abzielt, überprüft das Team den Code erneut, bewertet die Richtlinien des Zweigs, testet die Pull-Anfrage und führt sie zusammen. Nach der Zusammenführung wird die Korrektur innerhalb weniger Minuten auf dem ersten ring von Servern bereitgestellt. Von dort aus verteilt das Team nach und nach die Korrektur auf weitere Konten, indem es Verteilungsringeverwendet. Während die Änderungen an mehr Benutzer verteilt werden, überwacht das Team den Erfolg und prüft, ob die Änderung den Fehler behebt und keine Mängel oder Verlangsamungen mit sich bringt. Der Fix wird schließlich auf alle Azure-Rechenzentren ausgeweitet.

Weiter zum nächsten Sprint

In den nächsten drei Wochen schließt das Team die Hinzufügung von Funktionen zu Sprint 130 ab und bereitet sich auf die Bereitstellung dieser Änderungen vor. Sie erstellen den neuen Versionszweig, releases/M130 von main und stellen diesen Zweig bereit.

Zu diesem Zeitpunkt gibt es in der Produktion zwei Zweige. Mit einem ringbasierten Deployment werden Änderungen sicher in die Produktion überführt. Der schnelle Ring erhält die Sprint-130-Änderungen, und die langsamen Ring-Server bleiben auf Sprint-129, während die neuen Änderungen in der Produktion validiert werden.

Das Hotfixing einer Änderung in der Mitte eines Einsatzes könnte das Hotfixing von zwei verschiedenen Releases erfordern, dem Sprint 129 Release und dem Sprint 130 Release. Das Team portiert den Hotfix auf beide Versionszweige und stellt ihn bereit. Der Zweig 130 wird mit dem Hotfix auf die Ringe, die bereits aktualisiert wurden, neu verteilt. Der Zweig 129 wird mit dem Hotfix an die äußeren Ringe verteilt, die noch nicht auf die Version des nächsten Sprints aktualisiert haben.

Sobald alle Ringe bereitgestellt sind, wird der alte Zweig von Sprint 129 aufgegeben, da alle Änderungen, die als Hotfix in den Zweig von Sprint 129 eingebracht wurden, auch in mainvorgenommen wurden. Diese Änderungen werden also auch im Zweig releases/M130 zu finden sein.

Illustration of a release branch at sprint 130.

Zusammenfassung

Das Release-Flow-Modell ist das Herzstück der DevOps-Entwicklung von Microsoft zur Bereitstellung von Online-Diensten. Bei diesem Modell wird eine einfache, stammbasierte Verzweigungsstrategie verwendet. Doch anstatt die Entwickler in einer Warteschlange für die Bereitstellung feststecken zu lassen, während sie darauf warten, dass ihre Änderungen zusammengeführt werden, können sie mit dem Microsoft Release Flow weiterarbeiten.

Dieses Versionsmodell ermöglicht auch die regelmäßige Bereitstellung neuer Funktionen in den Azure-Rechenzentren, trotz der Größe der Microsoft-Codebases und der Anzahl der daran arbeitenden Entwickler. Das Modell ermöglicht auch die schnelle und effiziente Einführung von Hotfixes in die Produktion.