3 Punkte von xguru 2024-09-02 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Mit MSA betreibt Monzo mehr als 2.800 Services und erzielt dadurch viel Mehrwert
  • Diese Architektur bringt jedoch auch Herausforderungen mit sich. Eine davon ist, Bibliotheksänderungen über alle Services hinweg durchzuführen
  • In der Regel muss man sich zwischen zwei von drei Dingen entscheiden: aktuelle Bibliotheken, konsistente Bibliotheksversionen und geringer Upgrade-Aufwand

Zentralisierte Migrationsstrategie

  • Bei Monzo geht man davon aus, dass sich die drei oben genannten Eigenschaften durch einen zentral gesteuerten Migrationsansatz in hohem Maß erreichen lassen
  • Anstatt die Verantwortung für Migrationen an die Service-Owner zu übergeben, bevorzugt Monzo, dass ein einzelnes Team die Migration vorantreibt
  • So lassen sich hoher Koordinationsaufwand (der zu langsamen Migrationen führt) und das Risiko, dass Projekte zum Stillstand kommen (was zu Inkonsistenzen führt), vermeiden
  • Damit ein einzelnes Team Migrationen in angemessener Zeit abschließen kann, stützt sich Monzo stark auf Folgendes:
    • grundlegende Technologieentscheidungen (z. B. ein hohes Maß an Konsistenz, Nutzung eines monorepo)
    • den großflächigen Einsatz von Automatisierung (z. B. Tools für Massendeployments von Services, automatisierte Rollback-Prüfungen)

Migration vom OpenTracing SDK zum OpenTelemetry SDK

  • Kürzlich hat Monzo seine Strategie bei einem Projekt umgesetzt, das den Wechsel vom OpenTracing SDK zum OpenTelemetry SDK zum Ziel hatte
  • Alle Services exportieren Tracing-Daten nach Jaeger. Bisher geschah dies über das OpenTracing- und das Jaeger-Go-SDK
  • Diese Bibliotheken sind inzwischen veraltet, und die Community hat sich stattdessen auf OpenTelemetry konsolidiert
  • Um die Grundlage für Verbesserungen des Tracing-Systems zu schaffen, wollte Monzo zunächst die veralteten Bibliotheken durch das OpenTelemetry SDK ersetzen

Migrationsprinzipien

  • Zentralisierte Migration, die für Service-Owner transparent ist. Monzo bevorzugt Strategien, die ein einzelnes Team zentral durchführen kann, um Koordinationsaufwand zu minimieren und das Risiko unterbrochener Migrationen zu senken
  • Keine Ausfallzeit. Die meisten Migrationen betreffen Services, die für die Kernfunktionen der Bank entscheidend sind, daher sind Ausfallzeiten nicht akzeptabel
  • Schrittweises Roll-forward, schnelles Rollback. Bei großflächigen Änderungen muss ein schrittweises Roll-forward möglich sein, um den Blast Radius bei Problemen zu begrenzen. Gleichzeitig muss bei Bedarf alles schnell zurückgerollt werden können
  • Die 80/20-Regel für Automatisierung. Bei groß angelegten Migrationen passt ein hoher Anteil der Änderungen meist in ein gemeinsames Template. Solche Änderungen lassen sich leicht automatisieren. Bei spezielleren Use Cases nimmt der Nutzen der Automatisierung ab, und es ist effizienter, sie fallweise zu lösen. Um böse Überraschungen zu vermeiden und den Fortschritt leichter nachverfolgen zu können, ist es sinnvoll, die notwendigen Änderungen im Vorfeld in diese beiden Kategorien einzuordnen

Migrationsstrategie

  • Bei Monzo gibt es eine Reihe von Migrationsschritten, die systematisch verwendet werden

1. Planung und Alignment

  • Migrationen sind mit erheblichen Risiken verbunden. Sie betreffen nicht nur eine große Zahl von Services, sondern das ausführende Team hat unter Umständen wenig oder gar keinen Kontext zu den Services, die migriert werden
  • Auch wenn Monzo auf Konsistenz zwischen Services hinarbeitet, gibt es immer Ausnahmen, und solche Überraschungen sollen möglichst früh erkannt werden
  • Deshalb ist es sehr wichtig, dass der Planungsprozess transparent ist und alle Engineers die Möglichkeit haben, beizutragen
  • Dafür gibt es zwei Prozesse:
    • Proposal: Monzo schreibt sehr viele davon. Praktisch alles Wesentliche wird am Ende in einem einzigen Slack-Kanal geteilt, in dem alle im Unternehmen Feedback geben können
    • Architektur-Review: Bei den größten Änderungen gibt es synchrone Architektur-Review-Meetings, in denen spezifische Bereiche mit mehr Kontroverse oder höherem Risiko tiefer behandelt werden. Das Ziel ist nicht, Freigaben oder Sign-offs einzuholen, sondern den Stand des Designs sinnvoll voranzubringen und das Projekt dadurch zu beschleunigen

2. Die alte Bibliothek wrappen

  • Anstatt sofort die neue Bibliothek zu installieren und den Service-Code so zu aktualisieren, dass er sie aufruft, entschied sich Monzo zunächst dafür, die alte Bibliothek zu wrappen

    1. Dadurch lassen sich Aufrufe an die zugrunde liegende Bibliothek abfangen und anhand dynamischer Konfiguration entscheiden, welche Implementierung verwendet wird. So sind Roll-forward und Rollback einfach möglich, ohne alle Services neu deployen zu müssen
    2. In der neuen Bibliothek gab es deutlich andere Typen/Funktionen. Alle Call Sites zu aktualisieren hätte viel Aufwand bedeutet, und in manchen Fällen war der Nutzen der neuen API gering. Durch das Wrappen der alten Bibliothek konnte in solchen Fällen eine ähnliche Schnittstelle wie zuvor beibehalten werden, was die Aktualisierung der Call Sites erleichterte
  • Weitere Vorteile des Wrappens von Bibliotheken:

    • Sie lassen sich mit der eigenen Telemetrie-Bibliothek instrumentieren
    • Es kann eine stärker opinionated Schnittstelle bereitgestellt werden

3. Call Sites aktualisieren

  • Die Nutzung dieser Bibliothek folgte einem gemeinsamen Muster:

    • Es gab eine kleine Zahl von Funktionen/Typen, auf die im gesamten Codebestand vielfach verwiesen wurde
    • Daneben gab es einen langen Rattenschwanz von Funktionen/Typen, die nur an wenigen Stellen referenziert wurden
  • Diese beiden Fälle behandelte Monzo unterschiedlich:

    • Bei den wenigen Funktionen/Typen mit vielen Referenzen wurde so viel wie möglich automatisiert. Für diese Bibliothek stützte sich Monzo hauptsächlich auf gopls und gorename, um automatisierte Refactorings durchzuführen
    • Um den langen Rattenschwanz an Funktionen/Typen mit nur wenigen Referenzen zu bewältigen, wurde ein manueller, fallweiser Ansatz gewählt. In manchen Fällen wurden sie manuell migriert. In anderen Fällen stellte sich heraus, dass sich dieselbe Aufgabe auch mit einer etablierteren API erledigen ließ, und entsprechend wurde darauf umgestellt. Das bedeutete, dass diese Fälle nicht mehr als Sonderfälle behandelt werden mussten, und hatte den zusätzlichen Vorteil, dass die API der Wrapper-Bibliothek klein und opinionated bleiben konnte
  • Zusätzlich zum Wrappen der alten Bibliothek blockierte Monzo neue Abhängigkeiten von ihr. Das geschah durch das Hinzufügen einer CI-Prüfung mit semgrep

4. Die neue Bibliothek wrappen

  • Sobald die alte Bibliothek gewrappt war, konnte hinter der Wrapper-Bibliothek mit dem Hinzufügen der neuen Bibliothek begonnen werden
  • Anfangs war die neue Implementierung per Konfiguration deaktiviert. Das bedeutete, dass keine Verhaltensänderung zu erwarten war und Änderungen weiterhin schrittweise in den Master-Branch gemergt werden konnten

5. Massendeployment von Services

  • Bevor die neue Implementierung aktiviert werden konnte, musste sichergestellt werden, dass alle laufenden Services sie unterstützen
  • Bei anderen Arten von Bibliotheksänderungen kann jeweils nur eine Teilmenge der Services mit neuer Funktionalität gleichzeitig deployt werden. Bei einer Tracing-Bibliothek gilt jedoch: Wenn ein Service so migriert wurde, dass er die neue Bibliothek verwendet, dann müssen auch alle Services, die er (übergangsweise) aufrufen kann, die neue Funktionalität unterstützen
  • Um das Deployment einer großen Zahl von Services zu verwalten, entwickelte Monzo ein Tool für Massendeployments, das Bibliotheksänderungen per asynchronem Batch-Job an alle Services ausrollen kann
  • Um die Auswirkungen potenziell fehlerhafter Deployments abzumildern:
    • werden automatisierte Rollback-Prüfungen verwendet
    • werden zuerst die am wenigsten kritischen Services deployt. Alle Services sind mit einem „Tier“-Tag versehen, und das Massendeployment-Tool nutzt dieses, um die risikoärmsten Deployments zu priorisieren

6. Rollout-Steuerung über Konfiguration

  • Das Problem mit dem Massendeployment-Tool ist, dass es relativ langsam ist. Was Monzo unbedingt vermeiden will: alle Services zu deployen und dann festzustellen, dass die neue Bibliothek ein Problem hat und sich nicht schnell zurückrollen lässt
  • Deshalb wurde nicht die Aktivierung der neuen Implementierung selbst deployt, sondern die Fähigkeit, sie über das Konfigurationssystem zu aktivieren
  • Gegenüber einem normalen Deployment hat die Nutzung des Konfigurationssystems hier den Vorteil, dass sie schnell ist. Alle Services aktualisieren ihre Konfiguration alle 60 Sekunden, sodass bei Bedarf schnell ein Rollback möglich ist
  • Außerdem bietet das deutlich mehr Kontrolle darüber, wann die neue Implementierung verwendet wird. Zum Beispiel kann sie nur für bestimmte Nutzergruppen oder für einen zufälligen Prozentsatz von Requests aktiviert werden
  • In diesem Fall entschied sich Monzo dafür, den Rollout nur für API-Endpunkte zu aktivieren, die dem Team gehören, und dies mit schrittweise steigender Wahrscheinlichkeit

7. Aufräumen

  • Sobald vollständig auf die neue Implementierung umgestellt wurde, folgt die befriedigende Arbeit, die alte Implementierung aus der Wrapper-Bibliothek zu entfernen

Migrations-Superkräfte

  • Solche zentralisierten Migrationen sind bei Monzo dank grundlegender Technologieentscheidungen und Tools möglich, in die kontinuierlich investiert wird
  • Konsistente Technologie: Alle Services sind in Go geschrieben und verwenden dieselbe Version der alten Bibliothek. Das macht die Automatisierung von Änderungen deutlich einfacher. So wird zum Beispiel nur ein einziges Refactoring-Tool benötigt, statt jeweils eines pro Sprache
  • Monorepo: Der gesamte Service-Code liegt in einem einzigen monorepo, wodurch groß angelegte Refactorings viel einfacher in einem einzigen Commit durchgeführt werden können. Außerdem lässt sich die Nutzung bestimmter Bibliotheken über CI-Prüfungen global durchsetzen, was hilft, Konsistenz zu bewahren
  • Massendeployment: Wenn es viele deploybare Komponenten gibt, braucht es einen automatisierten Deployment-Prozess, um Bibliotheksänderungen auszurollen
  • Leichter und flexibler Konfigurationsdienst: Der Deployment-Prozess ist sicher, aber langsam (mehrere Minuten pro Deployment). Für das schnelle, sofortige Aktivieren/Deaktivieren neuer Funktionalität über viele Services hinweg ist ein leichterer, flexibler Prozess erforderlich

Fazit

  • Früher versuchte Monzo, Migrationen zu verteilen, doch das führte unweigerlich zu unvollständig abgeschlossenen Migrationen und hohem Koordinationsaufwand
  • Deshalb bevorzugt Monzo zentralisierte Migrationen sehr deutlich. Ein Team muss dabei zwar relativ hohe Kosten tragen, insgesamt ist der Aufwand jedoch geringer, und die Wahrscheinlichkeit, Konsistenz zu wahren, steigt erheblich
  • Dieser Ansatz erzeugt einen positiven Kreislauf:
    • Das Team, das Migrationen durchführt, hat einen starken Anreiz, in Tools zur Automatisierung dieser Migrationen zu investieren
    • Es sorgt außerdem dafür, technische Konsistenz aufrechtzuerhalten, was den Bau solcher Tools erleichtert
    • Gleichzeitig bleibt Monzo beim Grad der Automatisierung pragmatisch und wendet die 80/20-Regel an
  • Zusätzlich zu den Tools, in die Monzo kontinuierlich investiert, ist dieser Ansatz nur dank einiger zentraler Technologieentscheidungen zu Beginn möglich
    • vor allem dank der Tatsache, dass hauptsächlich ein opinionated und begrenzter Technologie-Stack verwendet wird

Noch keine Kommentare.

Noch keine Kommentare.