Wie wir einen plötzlichen Anstieg der Auslastung um das Zehnfache im Homeoffice bewältigt haben und welche Erkenntnisse wir daraus gezogen haben

Hallo, Habr! In den letzten Monaten haben wir eine sehr interessante Situation durchlebt, und ich möchte unsere Geschichte über die Skalierung der Infrastruktur teilen. In dieser Zeit hat SberMarket seine Bestellungen vervierfacht und den Service in 17 neuen Städten gestartet. Der explosive Anstieg der Nachfrage nach Lebensmittelzustellungen hat von uns verlangt, unsere Infrastruktur zu skalieren. Lesen Sie unten mehr über die interessantesten und nützlichsten Erkenntnisse.

Wie wir einen plötzlichen Anstieg der Auslastung um das Zehnfache im Homeoffice bewältigt haben und welche Erkenntnisse wir daraus gezogen haben

Mein Name ist Dima Bobylev, ich bin der technische Direktor von SberMarket. Da dies der erste Beitrag in unserem Blog ist, möchte ich ein paar Worte über mich und das Unternehmen sagen. Letzten Herbst nahm ich am Wettbewerb junger Führungskräfte des Runet teil. Für den Wettbewerb habe ich eine kleine Geschichte geschrieben darüber, wie wir bei SberMarket die interne Kultur und den Ansatz zur Serviceentwicklung sehen. Und obwohl es nicht gelungen ist, den Wettbewerb zu gewinnen, habe ich dennoch die grundlegenden Prinzipien für die Entwicklung der IT-Ökosysteme formuliert.

Bei der Teamführung ist es wichtig, ein Gleichgewicht zwischen den Anforderungen des Unternehmens und den Bedürfnissen jedes einzelnen Entwicklers zu finden. Derzeit wächst СберМаркет jährlich um das 13-fache, was sich auf das Produkt auswirkt und eine ständige Erhöhung der Entwicklungsvolumina und -geschwindigkeiten erfordert. Trotzdem nehmen wir uns ausreichend Zeit für die Entwickler, um eine gründliche Analyse und qualitativ hochwertige Codeerstellung zu ermöglichen. Dieser etablierte Ansatz hilft nicht nur bei der Erstellung eines funktionierenden Produkts, sondern auch bei dessen weiterer Skalierung und Entwicklung. Infolge dieses Wachstums ist СберМаркет bereits zum Marktführer unter den Lebensmittellieferdiensten geworden: Wir liefern täglich rund 18.000 Bestellungen, während es Anfang Februar noch etwa 3.500 waren.

Wie wir einen plötzlichen Anstieg der Auslastung um das Zehnfache im Homeoffice bewältigt haben und welche Erkenntnisse wir daraus gezogen haben
Einmal bat ein Kunde den Kurier von СберМаркет, die Lebensmittel kontaktlos direkt auf den Balkon zu liefern.

Kommen wir zur Sache. In den letzten Monaten haben wir intensiv an der Skalierung der Infrastruktur unseres Unternehmens gearbeitet. Dieser Bedarf wurde durch externe und interne Faktoren bedingt. Mit der gleichzeitigen Erweiterung unseres Kundenstamms stieg die Anzahl der angeschlossenen Shops von 90 zu Beginn des Jahres auf mehr als 200 bis Mitte Mai. Natürlich haben wir uns vorbereitet, die Hauptinfrastruktur reserviert und auf die Möglichkeit des vertikalen und horizontalen Scalings aller in der Yandex-Cloud betriebenen virtuellen Maschinen gesetzt. Doch die Praxis hat gezeigt: „Alles, was schiefgehen kann, wird schiefgehen“. Heute möchte ich die interessantesten Situationen teilen, die in diesen Wochen aufgetreten sind. Ich hoffe, unsere Erfahrungen sind für Sie von Nutzen.

Slave ist in voller Einsatzbereitschaft

Bereits vor Beginn der Pandemie hatten wir mit einem Anstieg der Anfragen an unsere Backend-Server zu kämpfen. Der Trend, Produkte nach Hause liefern zu lassen, nahm zu, und mit der Einführung der ersten Maßnahmen zur Selbstisolierung aufgrund von COVID-19 stieg die Last dramatisch während des gesamten Tages. Es entstand die Notwendigkeit, die Master-Server der Hauptdatenbank schnell zu entlasten und einen Teil der Leseanfragen auf Replikaserver (Slave) zu verschieben.

Wir hatten uns im Vorfeld auf diesen Schritt vorbereitet und bereits 2 Slave-Server für manuelle Anpassungen in Betrieb genommen. Diese führten hauptsächlich Batch-Aufgaben zur Generierung informativer Feeds zum Datenaustausch mit Partnern durch. Diese Prozesse erzeugten zusätzliche Last und wurden vollkommen zu Recht bereits vor ein paar Monaten "aus dem Rahmen genommen". 

Da auf dem Slave eine Replikation stattfand, hielten wir an dem Konzept fest, dass Anwendungen nur im Nur-Lese-Modus mit ihnen arbeiten konnten. Der Disaster Recovery Plan sah vor, dass wir im Falle einer Katastrophe einfach den Slave anstelle des Masters mounten und alle Lese- und Schreibanfragen auf den Slave umschalten konnten. Allerdings wollten wir auch Replikate für die Bedürfnisse der Analyseabteilung nutzen, weshalb die Server nicht vollständig in den Nur-Lese-Status versetzt wurden. Auf jedem Host gab es zudem einen eigenen Satz von Benutzern, von denen einige Schreibrechte hatten, um Zwischenergebnisse der Berechnungen zu speichern.

Bis zu einem bestimmten Lastlevel reichten uns der Master sowohl für Schreib- als auch für Leseanfragen bei der Bearbeitung von HTTP-Anfragen. Mitte März, als Sbermarkt beschloss, vollständig auf Remote-Arbeit umzustellen, erlebten wir einen exponentiellen Anstieg der RPS. Immer mehr unserer Kunden gingen in Selbstquarantäne oder arbeiteten von zu Hause aus, was sich auf die Lastindikatoren auswirkte.

Die Leistung des Master-Servers war nicht mehr ausreichend, daher haben wir begonnen, einige der schwersten Leseabfragen auf die Replik auszulagern. Für die transparente Weiterleitung von Schreibanfragen an den Master und Leseanfragen an den Slave haben wir das Ruby Gem "Octopus" verwendet. Wir haben einen speziellen Benutzer mit dem Suffix _readonly ohne Schreibrechte erstellt. Aufgrund eines Fehlers in der Konfiguration eines der Hosts wurden jedoch einige Schreibanfragen im Namen eines Benutzers an den Slave-Server gesendet, der über die entsprechenden Rechte verfügte.

Das Problem trat nicht sofort auf, da die erhöhte Last das Rückstandsverhalten der Slaves verstärkte. Die Inkonsistenz der Daten wurde am Morgen festgestellt, als die Slaves nach nächtlichen Importen den Master nicht "eingeholt" hatten. Wir schrieben dies auf die hohe Last des Dienstes selbst und den Import im Zusammenhang mit der Eröffnung neuer Geschäfte. Allerdings war es inakzeptabel, Daten mit stundenlangen Verzögerungen zu liefern, und wir wechselten die Prozesse auf den zweiten analytischen Slave, da dieser über größere Ressourcen verfügte und nicht mit Leseanfragen belastet war (was wir uns als Grund für das Fehlen von Replikationsverzögerungen erklärten).größtendie größten Ressourcen und keine Anfragen zum Weiterlesen und (wie wir uns selbst erklärt haben) keine Lags bei der Replikation.

Nachdem wir die Ursachen des "Auseinanderlaufens" des Haupt-Slave geklärt hatten, war der analytische bereits aus demselben Grund ausgefallen. Trotz der zwei zusätzlichen Server, auf die wir die Last im Falle eines Master-Ausfalls umleiten wollten, stellte sich aufgrund eines ärgerlichen Fehlers heraus, dass im entscheidenden Moment keiner verfügbar war.

Da wir jedoch nicht nur ein Dump der Datenbank gemacht hatten (die Wiederherstellung dauerte zu diesem Zeitpunkt etwa 5 Stunden), sondern auch einen Snapshot des Master-Servers angefertigt hatten, gelang es uns, die Replikation innerhalb von 2 Stunden zu starten. Allerdings wartete danach noch die längere Anwendung des Replikationsprotokolls auf uns (da der Prozess im Einzelthread-Modus abläuft, aber das ist eine ganz andere Geschichte).

Fazit: Nach einem solchen Vorfall wurde klar, dass wir von der Praxis der eingeschränkten Schreibrechte für Benutzer absehen und den gesamten Server auf read-only setzen sollten. Mit einem solchen Ansatz kann man sicher sein, dass die Replikate in kritischen Momenten verfügbar sind.

Die Optimierung auch nur einer schweren Abfrage kann die Datenbank "wieder zum Leben erwecken".

Obwohl wir das Katalogangebot auf der Website ständig aktualisieren, gab es bei den Anfragen, die wir an die Slave-Server gerichtet haben, eine leichte Verzögerung im Vergleich zum Master. Die Zeit, die wir benötigten, um das Problem der "plötzlich abgeschalteten" Slaves zu identifizieren und zu beheben, überschritt die "psychologische Grenze" (in dieser Zeit hätte sich eine Preisanpassung stattfinden können, und die Kunden hätten veraltete Daten gesehen), und wir mussten alle Anfragen auf den Haupt-Datenbankserver umleiten. Dadurch arbeitete die Website langsam ... aber sie funktionierte wenigstens. Und während sich die Slaves erholten, blieben uns nur Optimierungen. 

Während die Slave-Server sich erholten, zogen sich die Minuten in die Länge, der Master blieb überlastet, und wir konzentrierten alle Kräfte auf die Optimierung der aktiven Aufgaben gemäß dem "Pareto-Prinzip": Wir wählten die TOP-Anfragen aus, die den größten Teil der Last verursachten, und begannen mit der Feinabstimmung. Dies geschah direkt "im laufenden Betrieb".

Ein interessantes Phänomen war, dass eine bis zur Grenze belastete MySQL-Datenbank bereits auf geringfügige Prozessverbesserungen reagierte. Die Optimierung einiger Abfragen, die nur 5 % der Gesamtlaste ausmachten, führte bereits zu einer merklichen Entlastung der CPU. Dadurch konnten wir ausreichende Ressourcen für den Betrieb von Master mit der Datenbank sicherstellen und die nötige Zeit für die Wiederherstellung der Replikate erhalten. 

Fazit: Bereits eine kleine Optimierung ermöglicht es, mehrere Stunden bei Überlastung zu "überleben". Genau das hat uns zur Wiederherstellung der Replikaserver gereicht. Übrigens werden wir die technische Seite der Abfrageoptimierung in einem der nächsten Beiträge besprechen. Abonnieren Sie also unseren Blog, wenn das für Sie von Nutzen sein könnte.

Organisieren Sie das Monitoring der Verfügbarkeit von Partnerdiensten.

Wir kümmern uns um die Bearbeitung von Kundenaufträgen, und deshalb interagieren unsere Dienste ständig mit externen APIs – das sind Schnittstellen zum Versenden von SMS, Zahlungsplattformen, Routing-Systeme, Geocoder, Finanzauskunftsdienste und viele andere Systeme. Als die Last rapide zunahm, stießen wir auf die Einschränkungen der APIs unserer Partnerdienste, über die wir zuvor nie nachgedacht hatten.

Unerwartete Überschreitungen der Kontingente von Partnerdiensten können zu Ausfallzeiten bei Ihrem eigenen Dienst führen. Viele APIs blockieren Kunden, die ihre Limits überschreiten, und in einigen Fällen kann eine Überlastung der Anfragen die Produktion bei dem Partner überlasten. 

Zum Beispiel, während der Anstieg der Lieferungen hatten die begleitenden Dienste Schwierigkeiten bei der Verteilung und der Routenbestimmung. Infolgedessen kam es vor, dass Bestellungen aufgegeben wurden, aber der Dienst zur Routenplanung nicht funktionierte. Es ist zu sagen, dass unsere Logistiker in diesen Bedingungen praktisch das Unmögliche geleistet haben, und die klare Zusammenarbeit des Teams half, temporäre Ausfälle der Dienste zu kompensieren. Aber ein solches Aufkommen an Anfragen kann nicht dauerhaft manuell verarbeitet werden, und irgendwann wären wir mit einer unzulässigen Diskrepanz zwischen Bestellungen und deren Erfüllung konfrontiert. 

Es wurden eine Reihe von organisatorischen Maßnahmen ergriffen, und die reibungslose Zusammenarbeit des Teams half uns, Zeit zu gewinnen, während wir neue Bedingungen vereinbarten und auf die Modernisierung der Dienste von einigen Partnern warteten. Es gibt auch andere APIs, die sich durch hohe Zuverlässigkeit und wettbewerbsfähige Tarife bei hohem Traffic auszeichnen. Zu Beginn nutzten wir beispielsweise eine bekannte Karten-API zur Bestimmung der Lieferadresse. Doch am Monatsende erhielten wir eine saftige Rechnung von fast 2 Millionen Rubel. Daraufhin entschieden wir uns schnell, sie zu ersetzen. Ich werde keine Werbung machen, aber ich kann sagen, dass unsere Ausgaben erheblich gesenkt wurden.
Wie wir einen plötzlichen Anstieg der Auslastung um das Zehnfache im Homeoffice bewältigt haben und welche Erkenntnisse wir daraus gezogen haben

Fazit: Es ist unbedingt notwendig, die Bedingungen aller Partnerdienste zu überwachen und dabei zu berücksichtigen. Selbst wenn es heute scheint, dass sie Ihnen „mit einem großen Puffer“ zur Verfügung stehen, bedeutet das nicht, dass sie morgen nicht zum Hindernis für Ihr Wachstum werden. Und natürlich ist es besser, sich im Voraus über die finanziellen Bedingungen gestiegener Anforderungen an den Dienst zu einigen. 

Manchmal stellt sich heraus, dass „man mehr Gold braucht“ (c) nicht hilft

Wir sind von «Engpässen» in der Hauptdatenbank oder auf Anwendungsservern gewohnt, aber beim Skalieren können Probleme an unerwarteten Stellen auftreten. Für die volltextliche Suche auf der Website nutzen wir die Engine Apache Solr. Mit steigendem Traffic haben wir eine Verzögerung der Antwortzeiten festgestellt, und die CPU-Auslastung des Servers erreichte bereits 100 %. Was könnte einfacher sein — wir geben dem Solr-Container mehr Ressourcen.

Statt des erwarteten Leistungszuwachses hat der Server einfach »gestoppt«. Er war sofort auf 100 % ausgelastet und antwortete noch langsamer. Ursprünglich hatten wir 2 Kerne und 2 GB RAM. Wir haben beschlossen, das zu tun, was normalerweise hilft — wir gaben dem Server 8 Kerne und 32 GB. Es wurde alles viel schlimmer (wie genau und warum — werden wir in einem separaten Beitrag erklären). 

Nach einigen Tagen haben wir die Feinheiten dieser Angelegenheit verstanden und eine optimale Leistung bei 8 Kernen und 32 GB RAM erreicht. Diese Konfiguration ermöglicht es uns auch heute, die Last weiter zu steigern, was sehr wichtig ist, da das Wachstum nicht nur bei den Kunden, sondern auch in der Zahl der angeschlossenen Geschäfte stattfindet — innerhalb von 2 Monaten hat sich ihre Anzahl verdoppelt. 

Fazit: Standardmethoden wie "mehr Hardware hinzufügen" funktionieren nicht immer. Daher ist es wichtig, beim Skalieren eines Dienstes gut zu verstehen, wie er Ressourcen nutzt, und seine Leistung unter neuen Bedingungen im Voraus zu testen. 

Stateless — der Schlüssel zu einfachem horizontalem Scaling

Insgesamt folgt unser Team einem bekannten Ansatz: Dienste sollten keinen internen Zustand (stateless) aufweisen und unabhängig von der Ausführungsumgebung sein. Dies ermöglichte es uns, einen Anstieg der Last durch einfaches horizontales Scaling zu bewältigen. Allerdings hatten wir einen Ausnahme-Dienst – einen Handler für langwierige Hintergrundaufgaben. Dieser war zuständig für das Versenden von E-Mails und SMS, die Verarbeitung von Events, die Generierung von Feeds, das Importieren von Preisen und Beständen sowie die Bildverarbeitung. Es stellte sich heraus, dass dieser Dienst von einem lokalen Dateispeicher abhängig war und nur in einer Instanz existierte. 

Als die Anzahl der Aufgaben in der Warteschlange des Prozessors wuchs (was natürlich mit der steigenden Anzahl von Bestellungen geschah), wurde die Leistung des Hosts, auf dem der Prozessor und der Speicherplatz gehostet wurden, zum begrenzenden Faktor. Infolgedessen wurde die Aktualisierung des Sortiments und der Preise, das Versenden von Benachrichtigungen an die Benutzer und viele andere kritische Funktionen, die in der Warteschlange steckten, gestoppt. Das Ops-Team migrierte schnell den Speicherplatz in eine S3-ähnliche Netzwerkablage, wodurch wir mehrere leistungsstarke Maschinen hochfahren konnten, um den Background-Job-Prozessor zu skalieren.

Fazit: Die Stateless-Regel muss für alle Komponenten ohne Ausnahme eingehalten werden, auch wenn es scheint, dass "wir hier sicherlich nicht auflaufen werden". Es ist besser, ein wenig Zeit in die richtige Organisation der Arbeitsweise aller Systeme zu investieren, als später in Eile den Code umschreiben und den überlasteten Dienst reparieren zu müssen.

7 Prinzipien für intensives Wachstum

Trotz der Verfügbarkeit zusätzlicher Kapazitäten sind wir während unseres Wachstums auf einige Stolpersteine gestoßen. In dieser Zeit hat sich die Anzahl der Bestellungen um mehr als das Vierfache erhöht. Aktuell liefern wir bereits über 17.000 Bestellungen täglich in 62 Städten und planen, unsere geografische Reichweite weiter auszubauen – im ersten Halbjahr 2020 wird ein Servicestart in ganz Russland erwartet. Um mit der steigenden Last zurechtzukommen, und basierend auf unseren bisherigen Erfahrungen, haben wir für uns sieben grundlegende Arbeitsprinzipien in Zeiten des kontinuierlichen Wachstums festgelegt:

  1. Incident-Management. Wir haben ein Board in Jira erstellt, auf dem jeder Vorfall als Ticket festgehalten wird. Dies hilft, die damit verbundenen Aufgaben effektiv zu priorisieren und zu bearbeiten. Denn im Grunde genommen ist es nicht schlimm, Fehler zu machen – schlimm ist es, zwei Mal aus demselben Grund Fehler zu machen. Für die Fälle, in denen Vorfälle schneller wiederholt auftreten, als wir die Ursache beheben können, sollte eine Handlungsanweisung bereitstehen, da es während hoher Last wichtig ist, blitzschnell zu reagieren.
  2. Überwachung Dies ist für alle Infrastrukturkomponenten ohne Ausnahme erforderlich. Dank ihm konnten wir das Wachstum der Last prognostizieren und die „Engpässe“ richtig auswählen, um Prioritäten für deren Beseitigung zu setzen. Bei hoher Last wird wahrscheinlich alles, woran Sie nicht einmal gedacht haben, kaputt gehen oder anfangen zu stocken. Daher ist es am besten, neue Alarme direkt nach den ersten Vorfällen zu erstellen, um sie zu überwachen und vorauszusehen.
  3. Richtige Alarme sind bei plötzlichem Anstieg der Last einfach unerlässlich. Erstens sollten sie genau berichten, was kaputt gegangen ist. Zweitens sollte es nicht zu viele Alarme geben, denn eine Vielzahl von nicht kritischen Alarmen führt dazu, dass alle Benachrichtigungen ignoriert werden.
  4. Anwendungen sollten stateless sein. Wir haben bestätigt, dass es für diese Regel keine Ausnahmen geben sollte. Es ist eine vollständige Unabhängigkeit von der Laufzeitumgebung erforderlich. Dazu können Sie gemeinsame Daten in einer Datenbank oder beispielsweise direkt in S3 speichern. Am besten folgen Sie den Regeln. https://12factor.net. Während eines plötzlichen Anstiegs der Nutzung bleibt keine Zeit, um den Code zu optimieren, und man muss die Belastung durch direktes Erhöhen der Rechenressourcen und horizontale Skalierung bewältigen.
  5. Quoten und Leistung externer Dienste. Bei schnellem Wachstum kann das Problem nicht nur in Ihrer Infrastruktur auftreten, sondern auch bei externen Diensten. Am frustrierendsten ist es, wenn dies nicht durch einen Ausfall geschieht, sondern aufgrund von Erreichung der Quoten oder Limits. Externe Dienste müssen daher ebenso gut skalieren wie Sie selbst. 
  6. Prozesse und Warteschlangen trennen. Das hilft enorm, wenn es an einem der Gateways zu Engpässen kommt. Wir würden keine Verzögerungen bei der Datenübertragung haben, wenn überfüllte SMS-Versandwarteschlangen den Austausch von Benachrichtigungen zwischen den Informationssystemen nicht behindern würden. Außerdem wäre es einfacher, die Anzahl der Worker zu erhöhen, wenn sie separat arbeiten würden.
  7. Finanzielle Realitäten. Wenn die Datenströme explosionsartig ansteigen, bleibt keine Zeit, über Tarife und Abonnements nachzudenken. Aber man sollte sie im Hinterkopf behalten, insbesondere wenn man ein kleines Unternehmen ist. Eine hohe Rechnung kann von jedem API-Betreiber sowie von Ihrem Hosting-Anbieter ausgestellt werden. Daher ist es wichtig, die Verträge sorgfältig zu lesen.

Fazit

Ohne Verluste ging es nicht, aber wir haben diese Phase überstanden und versuchen heute, alle erkannten Prinzipien einzuhalten. Jede Maschine hat die Möglichkeit, die Leistung um das Vierfache zu steigern, um mit unerwarteten Situationen umzugehen. 

In den kommenden Beiträgen werden wir unsere Erfahrungen bei der Untersuchung von Leistungsabfällen in Apache Solr teilen. Außerdem berichten wir über die Optimierung von Anfragen und wie die Interaktion mit der FNS dem Unternehmen hilft, Geld zu sparen. Abonnieren Sie unseren Blog, um nichts zu verpassen, und teilen Sie uns in den Kommentaren mit, ob Sie ähnliche Probleme während eines Traffic-Wachstums erlebt haben.

Wie wir einen plötzlichen Anstieg der Auslastung um das Zehnfache im Homeoffice bewältigt haben und welche Erkenntnisse wir daraus gezogen haben

Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte melden Sie sich an.Sind Sie an Contour interessiert?

Hatten Sie eine Verlangsamung oder einen Ausfall des Dienstes bei plötzlich wachsendem Traffic aufgrund von:

  • 55,6%Unfähigkeit, schnell zusätzliche Rechenressourcen hinzuzufügen10

  • 16,7%Limits der Infrastruktur des Hosting-Anbieters3

  • 33,3%Limits von Drittanbieter-APIs6

  • 27,8%Verstöße gegen die Prinzipien der Stateless-Architektur in eigenen Anwendungen

  • 88,9%Suboptimale Codequalität eigener Dienste

18 Benutzer haben abgestimmt. 6 Benutzer haben sich enthalten.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster