Verbesserung der Server-Ressourcennutzung durch die Erfassung von Speicherlecks


Die erste Hypothese, die wir untersuchten, war, dass sich das Speicherverbrauchsverhalten von Spielen veränderte. Wir ermutigen Entwickler, die Grenzen der Plattform auszuloten und die ihnen zur Verfügung stehenden Ressourcen zu nutzen, um großartige und bahnbrechende Spiele zu entwickeln. Dies war leicht zu überprüfen: Wir konnten den Speicherverbrauch aller Spiele aggregieren und feststellen, ob dieser gestiegen war. Aber weit gefehlt – der durchschnittliche Speicherverbrauch und die prozentualen Aggregate pro Spiel blieben relativ stabil, während unsere Kapazität stetig abnahm.
Wir speichern monatlich Terabytes an Leistungs- und Ressourcenverbrauchsdaten, die aggregiert und gefiltert werden können, um die Ursache für Probleme wie dieses zu finden. Wir versuchten, das Problem auf eine bestimmte Region, einen bestimmten Hardwaretyp oder eine bestimmte Softwareversion einzugrenzen, doch leider trat das Problem überall auf. Wir beschlossen daraufhin, einige Spielserver stichprobenartig zu überprüfen und eine detaillierte Untersuchung durchzuführen. Ich wurde anfangs durch das Konzept des „freien“ Speichers auf Linux-Systemen in die Irre geführt. Das ist ein so häufiges Problem, dass es jemanden dazu veranlasste, eine Domain zu registrieren und eine Website einzurichten, um die Speicherkategorien zu erklären: https://www.linuxatemyram.com.

TL;DR: Wenn der Großteil Ihres Speichers belegt ist, ist das gut; freier Speicher ist verschwendeter Speicher. Wir müssen uns nur Sorgen machen, wenn der verfügbare Speicher nahe bei 0 liegt.
Nachdem wir festgestellt hatten, dass wir den Speicher korrekt überwachten, begannen wir mit einer Reihe von Experimenten, um herauszufinden, wo der Speicher genutzt wurde. Unser Ansatz bestand darin, bestimmte Speicher-Unterkategorien zu überwachen, um eine gezielte Lösung zu finden.

Der verfügbare Speicher wird grob als Summe aus MemFree + Active(file) + Inactive(file) + SReclaimable berechnet.
MemFree erfasst ungenutzten Speicher.
Active(file) und Inactive(file) erfassen den Seiten-Cache-Speicher. Der Seiten-Cache speichert aufgerufene Daten im Speicher, um die Anzahl der Festplatten-E/A-Vorgänge zu reduzieren.
SReclaimable erfasst den wiederverwendbaren Slab-Speicher. Slab-Speicher wird verwendet, um Caches von initialisierten Objekten zu halten, die häufig vom Kernel genutzt werden.
Das erste Experiment bestand darin, die Cache-Einstellungen anzupassen, insbesondere: vm.vfs_cache_pressure und vm.dirty_background_ratio.
Eine Erhöhung von vm.vfs_cache_pressure führt dazu, dass Objekte mit höherer Wahrscheinlichkeit aus dem wiederverwendbaren Cache zurückgewonnen werden. Dies hat Auswirkungen auf die Leistung (sowohl bei Cache-Fehlern als auch bei der Suchzeit zum Auffinden freigebbarer Objekte).
dirty_background_ratio ist der Prozentsatz (des verschmutzten Seiten-Cache-Speichers), bei dem wir beginnen, auf nicht blockierende Weise auf die Festplatte zu schreiben.
Wir haben die folgenden Änderungen vorgenommen und die Auswirkungen in memory, slabinfo und cgroups beobachtet.
vm.vfs_cache_pressure= 100 ==> 10000<br>vm.dirty_background_ratio= 10 ==> 5Eine nicht-invasive Methode zur Verfolgung der Ergebnisse bestand darin, regelmäßig einen Cron-Job auszuführen, der einen Snapshot des Speicherstatus erstellte. Etwa so:
#!/bin/bash<br>now=`date +%Y-%m-%d.%H:%M`
# create test dir<br>mkdir -p ~/memtest # log meminfo<br>sudo cat /proc/meminfo
~/memtest/meminfo_$now# log slabinfo<br>sudo cat /proc/slabinfo
~/memtest/slabinfo_$now # log cgroups<br>sudo cat /proc/cgroups
~/memtest/cgroups_$now Nachdem wir die Änderungen am Cache-Druck vorgenommen und einige Stunden lang beobachtet hatten, begannen wir mit der Analyse. Wir kamen zu dem Schluss, dass wir etwa 8 GB freien Speicher gewonnen hatten, dieser Speicher jedoch direkt aus dem Seiten- und Festplatten-Cache stammte, aus den Kategorien „Active(file)“ und „Inactive(file)“. Dies war ein enttäuschendes Ergebnis: Es gab keinen signifikanten Nettozuwachs an verfügbarem Speicher, und zudem nutzten wir diesen Speicher nicht mehr sinnvoll. Wir mussten Speicher von woanders zurückgewinnen.
Nachdem es uns nicht gelungen war, den verfügbaren Speicher direkt zu erhöhen, versuchten wir, konkurrierende Kategorien zu reduzieren. Wir stellten fest, dass die Speicherkategorie „SUnreclaim“ sehr groß war und in einigen Fällen innerhalb weniger Monate auf bis zu 60 GB angewachsen war. Die Kategorie „SUnreclaim“ erfasst den Speicher, der vom Betriebssystem für Objektpools verwendet wird und unter Speicherbelastung nicht zurückgewonnen werden kann. Das erste Anzeichen für ein Problem war eine ständig wachsende Anzahl von cgroups. Wir hatten höchstens ein paar hundert cgroups durch die Ausführung unserer Docker-Prozesse erwartet, sahen jedoch cgroups in der Größenordnung von HUNDERTTAUSENDEN. Zu unserem Glück scheint ein anderer Ingenieur, Roman Gushchin von Facebook, genau dieses Problem kürzlich auf Kernel-Ebene entdeckt und behoben zu haben: https://patchwork.kernel.org/cover/10943797/. Er erklärt:
Das zugrunde liegende Problem ist recht einfach: Jede Seite, die einer Cgroup zugeordnet ist, enthält einen Verweis auf diese, sodass die Cgroup erst freigegeben werden kann, wenn alle zugeordneten Seiten entfernt wurden. Wenn ein Slab-Objekt aktiv von anderen Cgroups genutzt wird, wird es nicht freigegeben und verhindert, dass die ursprüngliche Cgroup freigegeben wird.
Das schien genau unser Problem zu sein, also warteten wir gespannt auf Kernel 5.3, um die Korrektur zu überprüfen.
Wir verwendeten das Skript zur Speicherüberwachung aus dem Cache-Belastungsexperiment erneut, wollten für das Kernel-Experiment jedoch Kontroll- und Versuchsgruppen einrichten. Wir entlasteten den Produktionsdatenverkehr von zwei Server-Racks, aktualisierten dann den Kernel auf einem Rack auf 5.3 und behielten auf dem anderen Kernel 5.0 bei. Anschließend starteten wir beide Racks neu und öffneten sie wieder für den Produktionsdatenverkehr. Nach etwa einer Woche verfolgten wir, wie sich cgroups und nicht wiederverwertbarer Slab-Speicher im Laufe der Zeit veränderten. Hier sind die Ergebnisse:


Kernel 5.0.0 verzeichnet ein ununterbrochenes Wachstum der cgroups und gewinnt innerhalb einer Woche 4 GB nicht wiederverwertbaren Slab-Speicher hinzu, was insgesamt 6 GB ergibt. Im Gegensatz dazu weist Kernel 5.3.7 täglich erhebliche Rückgänge bei den cgroups auf, und das Wachstum des nicht wiederverwertbaren Slab-Speichers verläuft sehr langsam. Nach einer Woche beträgt der nicht wiederverwertbare Slab-Speicher ~2 GB. Mit dem neuen Kernel stabilisiert sich der nicht wiederverwertbare Slab-Speicher bei etwa 4 GB, selbst nach mehreren Monaten Betriebszeit.
Das eigentliche Problem, das wir lösen wollten, bestand darin, dass unsere Spielserver im Laufe der Zeit an Kapazität verloren. Dies lag an der geringeren verfügbaren Speichermenge, die wiederum auf den ständig wachsenden nicht wiederverwendbaren Slab-Speicher zurückzuführen war. Als wir dies dank der Kernel-Korrektur relativ gut unter Kontrolle hatten, wie wirkte sich das auf die Serverkapazität aus?

Auf der linken Seite sehen Sie unser Problem: einen stetigen Rückgang der Spiele pro Server, der unsere Infrastruktur stark belastet hat. Wir haben im März 2020 die Kernel-Version 5.3 auf unserer gesamten globalen Serverflotte bereitgestellt und konnten bisher eine hohe Serverkapazität aufrechterhalten. Vielen Dank an Roman Gushchin für den Kernel-Fix und an Andre Tran für die Unterstützung bei der Untersuchung des Problems und der Bereitstellung des Fixes.
Weder die Roblox Corporation noch dieser Blog befürworten oder unterstützen bestimmte Unternehmen oder Dienste. Außerdem werden keine Garantien oder Zusagen hinsichtlich der Genauigkeit, Zuverlässigkeit oder Vollständigkeit der in diesem Blog enthaltenen Informationen gegeben.
Dieser Blogbeitrag wurde ursprünglich auf dem Roblox Tech Blog veröffentlicht.


