Im ersten Teil meines
Artikels habe ich darüber gesprochen, wie wir bei Badoo die erste Version des Patch-Systems erstellt haben. Kurz gesagt, wir mussten einen Weg finden, um schwerwiegende Fehler direkt in der Produktion zu beheben, der allen Entwicklern zugänglich ist. Die erste Version war jedoch nicht ohne Nachteile: Wir verwendeten eine spezielle Layoutmethode, die die Atomizität des Layouts der Patches und die Konsistenz des Codes nicht garantierte.
In diesem Teil des Artikels werde ich über eine neue Methode sprechen, um den Code zu gestalten, den wir uns bei der Lösung unserer Probleme ausgedacht haben, und wie unser Patch-System damit transformiert wurde.
Bild: QuelleUniverselle Lösung - Multiversional Deployment Kit
Nach einer weiteren Überprüfung unseres Jura-Systems erklärte
youROCK Nasretdinov, er habe eine Idee, wie wir alle unsere Probleme lösen können. Er bat nur um viel Zeit, um das Layoutsystem neu zu gestalten. So erschien das Konzept des Multiversional Deployment Kit oder, allgemein bekannt, des MDK (Jura verglich es in seinem
Bericht über HighLoad ++ mit anderen Arten des
Codelayouts ).
Das neue System wurde entwickelt, um unser Layoutverfahren zu ändern. Für diejenigen, die meinen ersten Teil des Artikels nicht gelesen haben, werde ich kurz erläutern, wie der Bereitstellungsprozess aussieht: Zuerst sammeln wir alle erforderlichen Dateien in einem Verzeichnis, speichern dann den Status des Verzeichnisses und übermitteln ihn an die Server.
Vor der MDK-Ära verwendeten wir Blockgeräte (dh Dateisystem-Images), sogenannte Loops, zum Speichern und Bereitstellen. Das Verzeichnis wurde in eine leere Schleife kopiert, archiviert und an die Server gesendet.
Im neuen System versionieren wir nicht das gesamte Verzeichnis, sondern jede Datei einzeln, sodass die Version der Datei eindeutig mit ihrem Inhalt korreliert. Für Verzeichnisse gibt es Maps (Maps) - spezielle Dateien, in denen Versionen aller Dateien im Verzeichnis aufgezeichnet werden. Diese Karten sind ebenfalls versioniert und alles sieht ungefähr so aus:

Kommt Ihnen das bekannt vor? So sind Objekte in Git angeordnet (Sie können hier darüber lesen, dies ist jedoch zum Verständnis des Artikels nicht erforderlich).
Für die Versionierung verwenden wir die ersten acht Zeichen aus dem MD5-Hash (genauer gesagt aus seiner hexadezimalen Darstellung), die aus dem Inhalt der Datei stammen. Diese Version wird am Ende des Dateinamens oder am Anfang des Kartennamens geschrieben (damit Sie die Kartendatei von der generierten Versionskarte unterscheiden können):
Die Codeversion ist die Kartenversion des WWW-Stammverzeichnisses. Um die aktuelle Karte zu finden, haben wir eine symbolische Verknüpfung (symlink) current.map.
Warum nicht Git verwenden?Trotz der Tatsache, dass MDK teilweise Ideen von Git entlehnt, gibt es einige Unterschiede. Das Wichtigste ist, wie die Dateien im Arbeitsverzeichnis (d. H. Auf Computern) gespeichert werden. Wenn Git dort nur eine aktuelle Version speichert, enthält MDK dort alle verfügbaren Versionen von Dateien. Gleichzeitig verweist nur eine symlink current.map auf die aktuelle Version des Codes, die in ihrer Arbeit Autoload verwendet und atomar geändert werden kann. Zum Vergleich verwendet Git
git-checkout , um die Version zu ändern, wodurch die Dateien der Reihe nach geändert werden und nicht atomar sind.
Mit MDK erstellen
MDK wird benötigt, um den Status des Verzeichnisses am Ende der Assembly zu speichern. Zu diesem Zweck haben wir einen speziellen Ort, den wir als Repository bezeichnet haben - ein Repository aller Versionen von Dateien, die für uns von Wert sind (dh die wir möglicherweise zerlegen möchten). Wenn der neue Inhalt des Verzeichnisses fertig ist, berechnen wir die Versionen aller darin enthaltenen Dateien und melden die fehlenden dem Repository.
Layout mit MDK
Während des Layouts auf jedem der empfangenden Server führen wir ein Skript aus, das prüft, ob sich alle erforderlichen Dateien auf dem Server befinden, und diejenigen anfordert, die im Repository fehlen. Wir können die Version nur durch Ändern des Symlinks current.map auf eine neue Version umstellen.
Wie es unsere Probleme lösen soll
Es wurde angenommen, dass, wenn nur wenige Dateien in der neuen Version geändert wurden, deren Zusammenstellung und Layout unter Verwendung des neuen Systems zeitlich mindestens mit dem Layout des Patches als separate Dateien vergleichbar sein sollten. Wenn ja, dann generieren wir für jeden Patch einfach eine neue Version.
MDK-Implementierung
MDK hatte einen Nachteil: Auf den endgültigen Computern sollte der Name jeder Datei seine Version haben. Auf diese Weise können Sie viele Versionen einer Datei gleichzeitig in einem Verzeichnis speichern. Sie können jedoch nicht include user.php aus dem Code einschließen. Sie müssen eine bestimmte Version angeben. Fügen Sie noch die verschiedenen Fehler hinzu, die im Code des Layoutsystems verbleiben könnten, den neuen Layoutalgorithmus, der komplizierter als der alte war, und es wird klar, warum wir uns entschieden haben, das neue System in kleinen Schritten zu implementieren. Wir haben buchstäblich mit einem oder zwei Servern begonnen und ihre Liste schrittweise erweitert, um gleichzeitig die auftretenden Probleme zu beheben.
Da die Umstellung auf ein neues System viel Zeit in Anspruch nehmen sollte, mussten wir uns überlegen, wie unsere Patches während der Übergangszeit funktionieren würden. Zu dieser Zeit verwendeten wir für das Layout von Patches das selbstgeschriebene Dienstprogramm mscp, mit dem die Dateien einzeln angeordnet wurden. Wir haben ihr beigebracht, aktuelle Dateien auf Servern im Voraus durch MDK zu ersetzen, aber wir konnten solchen Servern keine neue Datei hinzufügen (da ich die Dateizuordnung ändern musste). Ich wollte keine sehr komplizierte Zwischenlösung einführen - weil wir in eine glänzende Zukunft gehen würden, in der mscp nicht benötigt wird. Infolgedessen musste ich mich mit diesem Problem abfinden. Während der Übergangsphase haben die Entwickler im Allgemeinen gelitten, aber jetzt scheint es uns, dass es sich gelohnt hat.
Vertraue niemandem
Bild: QuelleWahrscheinlich wird die Frage logisch sein, aber wird es eine Kollision von Versionen in MDK geben (d. H. Eine Situation, in der zwei Dateien mit unterschiedlichem Inhalt dieselbe Version zugewiesen bekommen)?
Tatsächlich sind wir vor solchen Fehlern ziemlich gut geschützt. Wir
{ }.{}
Dateien wie folgt:
{ }.{}
, was bedeutet, dass mehr als acht Zeichen übereinstimmen müssen, damit ein Fehler auftritt.
Aber einmal ging trotzdem etwas schief. Nach der nächsten Berechnung haben wir eine wachsende Anzahl von Fehlern mit dem HTTP-Code 404 festgestellt (Datei nicht gefunden). Eine kleine Untersuchung ergab, dass einige der statischen Dateien fehlten. Es stellte sich heraus, dass wir eine sehr alte statische Karte erstellt und Links zu Dateien angegeben haben, die sich nicht bereits auf den Servern befinden sollten. Aber woher kommt diese Karte? Im ersten Teil des Artikels habe ich festgestellt, dass die Statik durch einen separaten Prozess zerlegt wird und nur die Versionszuordnung den PHP-Code enthält. Wenn wir eine neue Version von MDK generieren, melden wir die fehlenden Versionen der Dateien an das Repository, aus dem nichts gelöscht wird (es gibt viel Speicherplatz, das macht uns nichts aus). Und wir geben oft das Beste für die Bereitstellung, und daher ist die statische Versionszuordnung eine dieser Dateien, die sich häufiger ändern als andere. All dies führte dazu, dass wir vor einer Kollision standen. Nach Überprüfung der Version entschied MDK, dass alles in Ordnung war, da bereits eine Datei einer solchen Version vorhanden ist, und legte sie auf Servern an. Es ist gut, dass wir den Fehler schnell entdeckt haben.
Jetzt überprüfen wir zusätzlich zur Version die Dateigröße: Wenn sie im Repository identisch ist, handelt es sich höchstwahrscheinlich um dieselbe Datei. Im schlimmsten Fall haben wir eine Geschichte für einen neuen Artikel.
MDK - Weihnachtsdieb
Bild: QuelleUnd ich möchte Ihnen von einem weiteren Fehler erzählen, weil er zumindest lustig ist. Es ist leicht zu erraten, dass wir alte Versionen von Dateien auf Zielservern bereinigt haben. Um eines der Probleme schnell zu lösen, haben wir eine schicksalhafte Entscheidung getroffen: Stellen Sie die Reinigungszeit auf einen Tag ein (anstatt wie zuvor auf sieben). Es hat funktioniert - und das Problem ist weg. Wir haben sogar eine Weile gelebt.
Am Sonntag gegen fünf Uhr morgens klingelte ein Telefon in meinem Schlafzimmer, und der Monitor auf Abruf klingelte: „Skripte funktionieren bei uns nicht. Sie sagen, dass Sie wissen, was los ist. " Für mich klang es wie: „Im Büro ist der Entsafter ausgebrannt. Sie sagen, Sie wissen, was los ist. " Ich kannte die Prinzipien unseres Scripting-Frameworks nur aus Artikeln und Geschichten, hatte keine „persönlichen Beziehungen“ zu ihm und noch mehr, ich habe ihn nie repariert. Aber ich stieg auf die Server, um herauszufinden, was los war, und stellte fest, dass das Problem wirklich „auf unserer Seite“ war: Es gab einfach keinen Code auf den Servern.
Ich habe den Code erneut hochgeladen - und es hat funktioniert. Der Fehler stellte sich übrigens als primitiv heraus: Am Samstag wurde keine einzige neue Version von MDK erstellt, und das Reinigungsskript hat, wie sich herausstellte, keine Überprüfungen durchgeführt, um die aktuelle Version nicht zu löschen. Infolgedessen löschte er um fünf Uhr morgens (nach einem Zeitplan) den Code von allen Servern. Nach dieser Geschichte wurde uns klar, dass es mit den alten Einstellungen an Feiertagen von 7 Tagen auftreten würde, zum Beispiel an Neujahrsfeiertagen, nur an Heiligabend. "Christus wurde geboren - der Code ist weg" - lange konnten wir diesen Witz hören.
Neues Patch-System
Am Ende haben wir ein neues Layoutsystem eingeführt - und es ist Zeit, das Patch-System zu wiederholen. Mscp wurde nicht mehr benötigt und es musste nicht vermieden werden, neue Versionen zu generieren. Zuerst haben wir den Lebenszyklus des Patches geändert. Nachdem er die Änderungen bestätigt hat, kehrt er zum Entwickler zurück, der eine Entscheidung trifft, wann der Patch zur Berechnung bereit ist. Er klickt auf die Schaltfläche Bereitstellen. Anschließend fügen wir den Patch hinzu, um eine neue Version von MDK zu erstellen, zu generieren und bereitzustellen. Eine Beteiligung der Entwickler in dieser Phase ist nicht mehr erforderlich.
Wir haben eine sehr gute Layoutgeschwindigkeit erreicht: Die Änderungen werden in buchstäblich einer Minute auf die Server übertragen. Dazu mussten wir jedoch auf einige Tricks zurückgreifen: Zum Beispiel generieren wir immer noch keine Statiken oder Übersetzungen - stattdessen nehmen wir die Version aus dem letzten zerlegten Build. Aus diesem Grund behalten wir eine Einschränkung für Patches für JS- und CSS-Dateien bei.
Die Experimente
Wir haben es wirklich geschafft, alle Probleme zu lösen, die wir vorher hatten. Sie müssen nicht mehr darüber nachdenken, wie Sie die Änderungen korrekt gestalten können, was keine Probleme mit dem dateiweisen Layout verursacht. Berühren Sie einfach nicht die Statik, und alles wird funktionieren.
Aber eine neue Schwierigkeit trat auf. Zuvor erlaubten wir Entwicklern, ihre Änderungen auf einem oder mehreren Servern zu veröffentlichen, um sicherzustellen, dass alles mit ihnen funktioniert. Mit dem neuen System ist diese Funktion verschwunden, da Master nun ausnahmslos die aktuelle Version für alle Server ist.
Bild: QuelleAus diesem Grund ist eine neue Anforderung für das Patch-System aufgetreten: Sie müssen in der Lage sein, Ihre Änderungen auf einer kleinen Anzahl von Servern zu überprüfen, ohne Änderungen am Master hinzuzufügen. Wir haben die neuen Funktionalitätsexperimente genannt.
Für den Entwickler sieht der Prozess ungefähr so aus: Nach Erhalt des Upgrades wird eine neue Seite in der Patch-Systemschnittstelle verfügbar, auf der Sie die Server auswählen können, auf denen Sie experimentieren möchten. Dies kann eine Gruppe von Servern, ein Server oder eine beliebige Kombination sein, die unser System versteht. Das System stellt den Patch bereit und benachrichtigt den Entwickler. Gleichzeitig wird auf der Seite ein Protokoll der neuesten Fehler der betroffenen Server angezeigt.
Wir beschränken Entwickler in keiner Weise, sie können Experimente auf denselben Servern erstellen. Einer kann mit einem 10% -Cluster experimentieren, der andere mit dem gesamten Cluster.
Dies wurde möglich, weil wir genau die „Version von Patches“ hatten, die uns so sehr fehlte. Dies ist eine Version, die theoretisch für jeden Server eindeutig sein kann. Es sieht aus wie eine durch Kommas getrennte Zeichenfolge, z. B. "32,45,79". Dies bedeutet, dass der Server alle Änderungen des Assistenten und der Patches mit den Nummern 32, 45 und 79 haben muss. Für jede dieser Versionen generieren wir unsere eigene Version von MDK. Wir übernehmen die neuesten Änderungen aus dem Hauptzweig und wenden dann nacheinander die einzelnen Patches an. Wenn während der Generierung einer der Versionen ein Konflikt auftritt, brechen wir das Experiment für den neuesten Patch einfach ab und benachrichtigen den Entwickler.
Generierte Dateien
Vom ersten Tag der Existenz des Patch-Systems an gingen wir zum Trick: Wir weigerten uns, statische Daten zu generieren, damit die Änderungen die Server so schnell wie möglich erreichen. Natürlich wollten wir unbedingt die Möglichkeit haben, den JS-Code genauso zu ändern wie den PHP-Code, aber alle Versuche, diesen Prozess zu erstellen, waren erfolglos.
Vor ungefähr sechs Monaten sind wir wieder auf dieses Problem zurückgekommen. Zweck: Sie müssen die Statik ändern, aber Sie können die Geschwindigkeit des Layouts von PHP-Code nicht opfern. Das Hauptproblem: Eine komplette Montage dauert acht Minuten. Was zu tun ist?
Sie müssen Kompromisse eingehen. Wir haben damit begonnen, dass der JS-Code im Rahmen der Experimente nicht angelegt werden kann. Dies sollte viel Zeit sparen: Halten Sie einfach eine Version der Statik auf dem neuesten Stand, anstatt Dutzende verschiedener Versionen für verschiedene Gruppen von Maschinen zu generieren. Aber es ist noch eine lange Zeit. Was können Sie noch sparen? Wir haben nicht herausgefunden, wie die Zeit verkürzt werden kann, aber entschieden, dass es kein Problem geben würde, wenn die Assembly das Layout des PHP-Codes nicht blockieren würde.
Wir haben begonnen, Statik asynchron zu generieren. Mit Änderungen an den JS- oder CSS-Dateien starten wir einen separaten Prozess, der eine neue Zuordnung der statischen Versionen erstellt. Beim Zusammenstellen von PHP-Code zu Beginn der Arbeit wird geprüft, ob eine neue statische Zuordnung vorhanden ist, und, falls vorhanden, wird diese auf allen Servern erfasst und angeordnet. Hast du das Problem gelöst? In der Praxis. Mit diesem Ansatz haben wir eine neue Einschränkung gewählt: Sie können den JS- und PHP-Code nicht in einem Patch ändern, da wir diese Änderungen asynchron zerlegen und nicht garantieren können, dass sie gleichzeitig auf den Computern vorhanden sind.
Zusammenfassung
Wir sind sehr zufrieden mit dem Update. Es war nicht einfach für uns, aber es hat unser System viel zuverlässiger gemacht. Die Entwickler haben eine alternative Anwendung für Experimente gefunden: Mit ihnen können Sie problemlos bestimmte Protokolle von zwei Servern sammeln, ohne Ihre Änderungen dem Master hinzuzufügen.
Wir haben noch Ideen zur Verbesserung des Systems, für deren Umsetzung noch nicht genügend Zeit zur Verfügung steht. Zum Beispiel möchten wir den Prozess des Erstellens eines Patches wiederholen und die Möglichkeit hinzufügen, JS-Dateien gleichzeitig mit dem Hauptcode zu ändern, um die neuesten Einschränkungen zu beseitigen.
Jeden Tag veröffentlichen wir ungefähr 60 Patches, manchmal gibt es mehrere Male mehr, zum Beispiel während der Entwicklung einiger Funktionen, die bisher nur Testern zur Verfügung stehen. Etwa ein Drittel der Patches durchläuft Experimente, bevor sie angelegt werden. Insgesamt hatten wir während der Existenz des Systems ungefähr 46.000 Patches für den Master.