Wie wir die Infrastruktur von Roblox effizienter und widerstandsfähiger machen

So wie Roblox in den letzten mehr als 16 Jahren gewachsen ist, haben auch Umfang und Komplexität der technischen Infrastruktur zugenommen, die Millionen von immersiven 3D-Erlebnissen ermöglicht. Die Anzahl der von uns unterstützten Maschinen hat sich in den letzten zwei Jahren mehr als verdreifacht, von etwa 36.000 am 30. Juni 2021 auf heute fast 145.000. Um diese rund um die Uhr verfügbaren Erlebnisse für Menschen auf der ganzen Welt zu ermöglichen, sind mehr als 1.000 interne Dienste erforderlich. Um Kosten und Netzwerklatenz zu kontrollieren, stellen wir diese Maschinen als Teil einer maßgeschneiderten und hybriden privaten Cloud-Infrastruktur bereit und verwalten sie, die hauptsächlich vor Ort betrieben wird.
Unsere Infrastruktur unterstützt derzeit mehr als 70 Millionen täglich aktive Nutzer weltweit, darunter auch die Entwickler, deren Geschäft auf der Roblox-Ökonomie basiert. All diese Millionen von Menschen erwarten ein sehr hohes Maß an Zuverlässigkeit. Angesichts des immersiven Charakters unserer Erlebnisse ist die Toleranz gegenüber Verzögerungen oder Latenz extrem gering, ganz zu schweigen von Ausfällen. Roblox ist eine Plattform für Kommunikation und Vernetzung, auf der Menschen in immersiven 3D-Erlebnissen zusammenkommen. Wenn Menschen als ihre Avatare in einem immersiven Raum kommunizieren, fallen selbst geringfügige Verzögerungen oder Störungen stärker auf als in einem Text-Chat oder einer Telefonkonferenz.
Im Oktober 2021 kam es bei uns zu einem systemweiten Ausfall. Es begann klein, mit einem Problem bei einer Komponente in einem Rechenzentrum. Doch während wir dem Problem nachgingen, breitete es sich schnell aus und führte schließlich zu einem 73-stündigen Ausfall. Damals teilten wir sowohl Details zu den Geschehnissen als auch einige unserer ersten Erkenntnisse aus dem Vorfall mit. Seitdem haben wir diese Erkenntnisse analysiert und daran gearbeitet, die Ausfallsicherheit unserer Infrastruktur gegenüber den Arten von Ausfällen zu erhöhen, die in allen groß angelegten Systemen aufgrund von Faktoren wie extremen Traffic-Spitzen, Wetter, Hardwareausfällen, Softwarefehlern oder einfach menschlichen Fehlern auftreten. Wie stellen wir sicher, dass sich ein Problem in einer einzelnen Komponente oder einer Gruppe von Komponenten nicht auf das gesamte System ausbreitet, wenn solche Ausfälle auftreten? Diese Frage stand in den letzten zwei Jahren im Mittelpunkt unserer Arbeit, und obwohl die Arbeit noch andauert, zahlen sich unsere bisherigen Maßnahmen bereits aus. So haben wir beispielsweise in der ersten Hälfte des Jahres 2023 im Vergleich zur ersten Hälfte des Jahres 2022 monatlich 125 Millionen Arbeitsstunden eingespart. Heute berichten wir über die Arbeit, die wir bereits geleistet haben, sowie über unsere längerfristige Vision für den Aufbau eines widerstandsfähigeren Infrastruktursystems.

Aufbau eines Sicherheitsnetzes
In groß angelegten Infrastruktursystemen treten täglich zahlreiche kleinere Ausfälle auf. Wenn eine Maschine ein Problem hat und außer Betrieb genommen werden muss, ist das beherrschbar, da die meisten Unternehmen mehrere Instanzen ihrer Backend-Dienste unterhalten. Wenn also eine einzelne Instanz ausfällt, übernehmen andere die Arbeitslast. Um diesen häufigen Ausfällen zu begegnen, sind Anfragen in der Regel so eingestellt, dass sie bei einem Fehler automatisch erneut versucht werden.
Dies wird zu einer Herausforderung, wenn ein System oder eine Person zu aggressiv nachversucht, was dazu führen kann, dass sich diese kleinen Ausfälle über die gesamte Infrastruktur auf andere Dienste und Systeme ausbreiten. Wenn das Netzwerk oder ein Benutzer hartnäckig genug nachversucht, wird dies letztendlich jede Instanz dieses Dienstes und möglicherweise auch andere Systeme global überlasten. Unser Ausfall im Jahr 2021 war das Ergebnis eines Phänomens, das in groß angelegten Systemen recht häufig vorkommt: Ein Ausfall beginnt klein, breitet sich dann durch das System aus und wird so schnell groß, dass es schwer ist, ihn zu beheben, bevor alles zusammenbricht.
Zum Zeitpunkt unseres Ausfalls verfügten wir über ein aktives Rechenzentrum (mit Komponenten, die als Backup fungierten). Wir benötigten die Möglichkeit, manuell auf ein neues Rechenzentrum umzuschalten, wenn ein Problem das bestehende lahmlegte. Unsere oberste Priorität war es, sicherzustellen, dass wir über eine Backup-Bereitstellung von Roblox verfügten, also richteten wir dieses Backup in einem neuen Rechenzentrum ein, das sich in einer anderen geografischen Region befindet. Das bot zusätzlichen Schutz für den schlimmsten Fall: einen Ausfall, der sich auf so viele Komponenten innerhalb eines Rechenzentrums ausbreitet, dass dieses vollständig ausfällt. Wir haben nun ein Rechenzentrum, das die Arbeitslasten verarbeitet (aktiv), und eines im Standby-Modus, das als Backup dient (passiv). Unser langfristiges Ziel ist es, von dieser Aktiv-Passiv-Konfiguration zu einer Aktiv-Aktiv-Konfiguration überzugehen, bei der beide Rechenzentren Workloads verarbeiten, wobei ein Load Balancer die Anfragen basierend auf Latenz, Kapazität und Zustand zwischen ihnen verteilt. Sobald dies umgesetzt ist, erwarten wir eine noch höhere Zuverlässigkeit für das gesamte Roblox-System und die Möglichkeit, Failovers nahezu sofort statt über mehrere Stunden hinweg durchzuführen.

Umstellung auf eine zellulare Infrastruktur
Unsere nächste Priorität war es, innerhalb jedes Rechenzentrums starke Schutzwände zu errichten, um das Risiko eines Ausfalls des gesamten Rechenzentrums zu verringern. Zellen (manche Unternehmen nennen sie Cluster) sind im Wesentlichen eine Gruppe von Maschinen und bilden die Grundlage für diese Schutzwände. Wir replizieren Dienste sowohl innerhalb als auch zwischen den Zellen, um zusätzliche Redundanz zu gewährleisten. Letztendlich möchten wir, dass alle Dienste bei Roblox in Zellen laufen, damit sie sowohl von den starken Schutzwänden als auch von der Redundanz profitieren können. Wenn eine Zelle nicht mehr funktionsfähig ist, kann sie sicher deaktiviert werden. Die Replikation über Zellen hinweg ermöglicht es dem Dienst, weiterzulaufen, während die Zelle repariert wird. In einigen Fällen kann die Reparatur einer Zelle eine vollständige Neubereitstellung der Zelle bedeuten. In der gesamten Branche ist das Löschen und die Neubereitstellung eines einzelnen Rechners oder einer kleinen Gruppe von Rechnern recht üblich, dies jedoch für eine gesamte Zelle, die etwa 1.400 Rechner umfasst, ist es nicht.
Damit dies funktioniert, müssen diese Zellen weitgehend einheitlich sein, damit wir Workloads schnell und effizient von einer Zelle in eine andere verschieben können. Wir haben bestimmte Anforderungen festgelegt, die Dienste erfüllen müssen, bevor sie in einer Zelle ausgeführt werden. Beispielsweise müssen Dienste containerisiert sein, was sie wesentlich portabler macht und verhindert, dass jemand Konfigurationsänderungen auf Betriebssystemebene vornimmt. Wir haben für Zellen eine „Infrastructure-as-Code“-Philosophie eingeführt: In unserem Quellcode-Repository enthalten wir die Definition aller Elemente, die sich in einer Zelle befinden, sodass wir sie mithilfe automatisierter Tools schnell von Grund auf neu aufbauen können.
Derzeit erfüllen noch nicht alle Dienste diese Anforderungen, daher haben wir daran gearbeitet, den Dienstverantwortlichen zu helfen, diese nach Möglichkeit zu erfüllen, und wir haben neue Tools entwickelt, um die Migration von Diensten in Zellen zu vereinfachen, sobald sie bereit sind. Unser neues Bereitstellungstool verteilt beispielsweise die Bereitstellung eines Dienstes automatisch auf mehrere Zellen, sodass sich die Dienstverantwortlichen keine Gedanken über die Replikationsstrategie machen müssen. Diese Strenge macht den Migrationsprozess zwar wesentlich anspruchsvoller und zeitaufwändiger, aber langfristig wird sich dies in einem System auszahlen, in dem:
- es viel einfacher ist, einen Ausfall einzudämmen und zu verhindern, dass er sich auf andere Zellen ausbreitet;
- Unsere Infrastrukturingenieure effizienter arbeiten und schneller handeln können; und
- die Ingenieure, die die Dienste auf Produktebene entwickeln, die letztendlich in Zellen bereitgestellt werden, nicht wissen müssen oder sich Gedanken darüber machen müssen, in welchen Zellen ihre Dienste laufen.
Größere Herausforderungen meistern
Ähnlich wie Brandschutztüren dazu dienen, Flammen einzudämmen, fungieren Zellen als starke Schutzwände innerhalb unserer Infrastruktur, um Probleme einzudämmen, die einen Ausfall in einer einzelnen Zelle auslösen. Letztendlich werden alle Dienste, aus denen Roblox besteht, redundant innerhalb und über Zellen hinweg bereitgestellt. Sobald diese Arbeit abgeschlossen ist, könnten sich Probleme zwar immer noch so weit ausbreiten, dass eine gesamte Zelle außer Betrieb gesetzt wird, aber es wäre extrem schwierig für ein Problem, sich über diese Zelle hinaus auszubreiten. Und wenn es uns gelingt, Zellen austauschbar zu machen, wird die Wiederherstellung deutlich schneller erfolgen, da wir auf eine andere Zelle umschalten und verhindern können, dass das Problem Auswirkungen auf Endnutzer hat.
Die Schwierigkeit besteht darin, diese Zellen so weit voneinander zu trennen, dass die Wahrscheinlichkeit einer Fehlerausbreitung sinkt, während gleichzeitig die Leistung und Funktionalität erhalten bleiben. In einem komplexen Infrastruktursystem müssen Dienste miteinander kommunizieren, um Abfragen, Informationen, Workloads usw. auszutauschen. Wenn wir diese Dienste in Zellen replizieren, müssen wir sorgfältig überlegen, wie wir die Kommunikation zwischen den Zellen verwalten. Im Idealfall leiten wir den Datenverkehr von einer fehlerhaften Zelle auf andere, fehlerfreie Zellen um. Aber wie gehen wir mit einer „tödlichen Abfrage“ um – einer, die dazu führt, dass eine Zelle ausfällt? Wenn wir diese Abfrage an eine andere Zelle umleiten, kann dies dazu führen, dass diese Zelle genau auf die Weise ausfällt, die wir zu vermeiden versuchen. Wir müssen Mechanismen finden, um „guten“ Datenverkehr aus ausgefallenen Zellen umzuleiten und gleichzeitig den Datenverkehr zu erkennen und zu unterbinden, der dazu führt, dass Zellen ausfallen.
Kurzfristig haben wir Kopien von Rechendiensten in jeder Rechenzelle bereitgestellt, sodass die meisten Anfragen an das Rechenzentrum von einer einzigen Zelle bedient werden können. Außerdem verteilen wir den Datenverkehr lastenausgleichend auf die Zellen. Mit Blick auf die Zukunft haben wir begonnen, einen Service-Discovery-Prozess der nächsten Generation zu entwickeln, der von einem Service Mesh genutzt werden soll und den wir hoffentlich 2024 fertigstellen können. Dies wird es uns ermöglichen, ausgefeilte Richtlinien zu implementieren, die eine zellübergreifende Kommunikation nur dann zulassen, wenn sie keine negativen Auswirkungen auf die Failover-Zellen hat. Ebenfalls im Jahr 2024 wird eine Methode eingeführt, mit der abhängige Anfragen an eine Serviceversion in derselben Zelle weitergeleitet werden, was den zellübergreifenden Datenverkehr minimiert und damit das Risiko einer zellübergreifenden Ausbreitung von Ausfällen verringert.
In Spitzenzeiten werden mehr als 70 Prozent unseres Backend-Service-Traffics außerhalb der Zellen bedient, und wir haben viel darüber gelernt, wie man Zellen erstellt. Wir rechnen jedoch mit weiteren Forschungen und Tests, während wir die Migration unserer Dienste bis 2024 und darüber hinaus fortsetzen. Im Laufe der Zeit werden diese Schutzwände immer stärker werden.

Migration einer ständig aktiven Infrastruktur
Roblox ist eine globale Plattform, die Nutzer auf der ganzen Welt unterstützt. Daher können wir Dienste nicht während der Nebenzeiten oder „Ausfallzeiten“ verlagern, was den Prozess der Migration all unserer Maschinen in Zellen und der Umstellung unserer Dienste auf den Betrieb in diesen Zellen zusätzlich erschwert. Wir haben Millionen von ständig aktiven Anwendungen, die weiterhin unterstützt werden müssen, selbst während wir die Maschinen, auf denen sie laufen, und die Dienste, die sie unterstützen, verlagern. Als wir diesen Prozess begannen, hatten wir keine Zehntausende von Maschinen, die ungenutzt herumstanden und für die Migration dieser Workloads zur Verfügung standen.
Wir verfügten jedoch über eine kleine Anzahl zusätzlicher Maschinen, die in Erwartung zukünftigen Wachstums angeschafft worden waren. Zunächst bauten wir mit diesen Maschinen neue Zellen auf und migrierten dann Workloads dorthin. Wir legen sowohl Wert auf Effizienz als auch auf Zuverlässigkeit. Anstatt also neue Maschinen zu kaufen, sobald uns die „Ersatzmaschinen“ ausgingen, bauten wir weitere Zellen auf, indem wir die Maschinen, von denen wir die Workloads migriert hatten, löschten und neu bereitstellten. Anschließend migrierten wir die Workloads auf diese neu bereitgestellten Maschinen und begannen den Prozess von vorne. Dieser Prozess ist komplex – wenn Maschinen ersetzt werden und für den Einbau in Zellen frei werden, geschieht dies nicht auf ideale, geordnete Weise. Sie sind physisch über die Rechenzentren verteilt, sodass wir sie nur stückweise bereitstellen können, was einen Defragmentierungsprozess auf Hardware-Ebene erfordert, um die Hardware-Standorte mit den großen physischen Ausfallbereichen abzustimmen.
Ein Teil unseres Infrastruktur-Engineering-Teams konzentriert sich darauf, bestehende Workloads aus unserer Legacy- oder „Pre-Cell“-Umgebung in Zellen zu migrieren. Diese Arbeit wird so lange fortgesetzt, bis wir Tausende verschiedener Infrastrukturdienste und Tausende von Backend-Diensten in neu errichtete Zellen migriert haben. Wir gehen davon aus, dass dies das gesamte nächste Jahr und möglicherweise bis ins Jahr 2025 dauern wird, da einige komplizierende Faktoren eine Rolle spielen. Erstens erfordert diese Arbeit die Entwicklung robuster Tools. Beispielsweise benötigen wir Tools, um bei der Bereitstellung einer neuen Zelle eine große Anzahl von Diensten automatisch neu zu verteilen – ohne dass unsere Nutzer davon beeinträchtigt werden. Wir haben auch Dienste gesehen, die unter bestimmten Annahmen über unsere Infrastruktur entwickelt wurden. Wir müssen diese Dienste überarbeiten, damit sie nicht von Faktoren abhängen, die sich in Zukunft ändern könnten, wenn wir auf Zellen umsteigen. Wir haben zudem sowohl eine Methode zur Suche nach bekannten Entwurfsmustern, die mit der Zellarchitektur nicht gut funktionieren, als auch einen methodischen Testprozess für jeden migrierten Dienst implementiert. Diese Prozesse helfen uns, Probleme für die Nutzer zu vermeiden, die dadurch entstehen könnten, dass ein Dienst nicht mit Zellen kompatibel ist.
Heute werden fast 30.000 Maschinen von Cells verwaltet. Das ist nur ein Bruchteil unserer gesamten Flotte, aber der Übergang verlief bisher sehr reibungslos, ohne negative Auswirkungen auf die Spieler. Unser oberstes Ziel ist es, dass unsere Systeme jeden Monat eine Verfügbarkeit von 99,99 Prozent für die Nutzer erreichen, was bedeutet, dass wir nicht mehr als 0,01 Prozent der Nutzungsstunden unterbrechen würden. Branchenweit lassen sich Ausfallzeiten nicht vollständig vermeiden, aber unser Ziel ist es, jegliche Ausfallzeiten bei Roblox so weit zu reduzieren, dass sie nahezu unbemerkt bleiben.
Zukunftssicherheit bei der Skalierung
Auch wenn sich unsere ersten Bemühungen als erfolgreich erweisen, ist unsere Arbeit an den Cells noch lange nicht abgeschlossen. Während Roblox weiter wächst, werden wir daran arbeiten, die Effizienz und Ausfallsicherheit unserer Systeme durch diese und andere Technologien zu verbessern. Im Laufe der Zeit wird die Plattform zunehmend widerstandsfähiger gegenüber Problemen werden, und alle auftretenden Probleme sollten für die Nutzer unserer Plattform zunehmend weniger sichtbar und störend sein.
Zusammenfassend haben wir bis heute:
- ein zweites Rechenzentrum errichtet und den Aktiv-Passiv-Status erfolgreich erreicht.
- Zellen in unseren aktiven und passiven Rechenzentren erstellt und mehr als 70 Prozent unseres Backend-Service-Traffics erfolgreich auf diese Zellen migriert.
- die Anforderungen und Best Practices festgelegt, die wir befolgen müssen, um alle Zellen einheitlich zu halten, während wir die Migration der restlichen Infrastruktur fortsetzen.
- einen kontinuierlichen Prozess zur Errichtung stärkerer „Blast Walls“ zwischen den Zellen eingeleitet.
Da diese Zellen immer austauschbarer werden, wird es weniger Übersprechen zwischen den Zellen geben. Dies eröffnet uns einige sehr interessante Möglichkeiten hinsichtlich einer verstärkten Automatisierung bei der Überwachung, Fehlerbehebung und sogar der automatischen Verlagerung von Workloads.
Im September haben wir außerdem damit begonnen, Active/Active-Experimente in unseren Rechenzentren durchzuführen. Dies ist ein weiterer Mechanismus, den wir testen, um die Zuverlässigkeit zu verbessern und Failover-Zeiten zu minimieren. Diese Experimente halfen dabei, eine Reihe von Systemdesignmustern zu identifizieren – vor allem im Bereich des Datenzugriffs –, die wir überarbeiten müssen, während wir darauf hinarbeiten, vollständig Active/Active zu werden. Insgesamt war das Experiment so erfolgreich, dass wir es für den Datenverkehr einer begrenzten Anzahl unserer Nutzer weiterlaufen lassen.



