So bauen Sie mit DevOps eine vollwertige Inhouse-Entwicklung auf – VTB-Erfahrung

DevOps-Praktiken funktionieren. Davon waren wir selbst überzeugt, als wir die Release-Installationszeit um das Zehnfache verkürzten. Im FIS-Profile-System, das wir bei VTB verwenden, dauert die Installation jetzt 10 statt 90 Minuten. Die Release-Build-Zeit hat sich von zwei Wochen auf zwei Tage verkürzt. Die Zahl der anhaltenden Implementierungsfehler ist auf nahezu ein Minimum gesunken. Um der „Handarbeit“ zu entkommen und die Abhängigkeit vom Lieferanten zu beseitigen, mussten wir auf Krücken arbeiten und unerwartete Lösungen finden. Unter dem Schnitt finden Sie eine detaillierte Geschichte darüber, wie wir eine vollwertige interne Entwicklung aufgebaut haben.

So bauen Sie mit DevOps eine vollwertige Inhouse-Entwicklung auf – VTB-Erfahrung
 

Prolog: DevOps ist eine Philosophie

Im vergangenen Jahr haben wir viel Arbeit geleistet, um die interne Entwicklung und Implementierung von DevOps-Praktiken bei VTB zu organisieren:

  • Wir haben interne Entwicklungsprozesse für 12 Systeme erstellt;
  • Wir haben 15 Pipelines in Betrieb genommen, von denen vier in Produktion gingen;
  • Automatisierte 1445 Testszenarien;
  • Wir haben eine Reihe von Releases, die von internen Teams vorbereitet wurden, erfolgreich implementiert.

Eine der am schwierigsten zu organisierenden internen Entwicklungen und Implementierungen von DevSecOps-Praktiken erwies sich als das FIS-Profile-System – ein Einzelhandelsproduktprozessor auf einem nicht relationalen DBMS. Dennoch konnten wir die Entwicklung aufbauen, die Pipeline starten, einzelne Nicht-Release-Pakete auf dem Produkt installieren und lernen, wie man Releases zusammenstellt. Die Aufgabe war nicht einfach, aber interessant und ohne offensichtliche Einschränkungen in der Umsetzung: Hier ist das System – Sie müssen eine Eigenentwicklung aufbauen. Die einzige Bedingung besteht darin, die CD vor einer Produktivumgebung zu verwenden.

Der Implementierungsalgorithmus schien zunächst einfach und klar:

  • Wir entwickeln erste Entwicklungskompetenz und erreichen vom Code-Team ein akzeptables Qualitätsniveau ohne grobe Mängel;
  • Wir integrieren uns so weit wie möglich in bestehende Prozesse;
  • Um Code zwischen offensichtlichen Phasen zu übertragen, schneiden wir eine Pipeline ab und schieben eines ihrer Enden in die Fortsetzung.

Während dieser Zeit muss das Entwicklungsteam der erforderlichen Größe Fähigkeiten entwickeln und den Anteil seines Beitrags zu Releases auf ein akzeptables Niveau erhöhen. Und das ist alles, wir können die Aufgabe als erledigt betrachten.

Es scheint, dass dies ein völlig energieeffizienter Weg zum gewünschten Ergebnis ist: Hier ist DevOps, hier sind die Leistungskennzahlen des Teams, hier ist das gesammelte Fachwissen ... Aber in der Praxis haben wir eine weitere Bestätigung erhalten, dass es bei DevOps immer noch um Philosophie geht und nicht „an den Gitlab-Prozess, Ansible, Nexus und weiter unten auf der Liste gebunden“.

Nachdem wir den Aktionsplan noch einmal analysiert hatten, stellten wir fest, dass wir in uns selbst eine Art Outsourcing-Anbieter aufbauten. Daher wurde der oben beschriebene Algorithmus um ein Prozess-Reengineering sowie den Aufbau von Fachwissen entlang der gesamten Entwicklungsroute erweitert, um eine führende Rolle in diesem Prozess zu erreichen. Nicht die einfachste Option, aber dies ist der Weg einer ideologisch korrekten Entwicklung.
 

Wo beginnt die Eigenentwicklung? 

Es war nicht das benutzerfreundlichste System, mit dem man arbeiten konnte. Architektonisch war es ein großes, nicht relationales DBMS, das aus vielen separaten ausführbaren Objekten (Skripten, Prozeduren, Batches usw.) bestand, die nach Bedarf aufgerufen wurden, und nach dem Prinzip einer Black Box funktionierte: Es empfängt eine Anfrage und gibt sie aus eine Antwort. Weitere erwähnenswerte Schwierigkeiten sind:

  • Exotische Sprache (MUMPS);
  • Konsolenschnittstelle;
  • Mangelnde Integration mit gängigen Automatisierungstools und Frameworks;
  • Datenvolumen in Dutzenden Terabyte;
  • Belastung von über 2 Millionen Betätigungen pro Stunde;
  • Bedeutung – geschäftskritisch.

Gleichzeitig gab es auf unserer Seite kein Quellcode-Repository. Überhaupt. Es gab eine Dokumentation, aber alle wichtigen Kenntnisse und Kompetenzen lagen bei einer externen Organisation.
Wir haben die Entwicklung des Systems unter Berücksichtigung seiner Funktionen und der geringen Verbreitung fast von Grund auf begonnen. Begonnen im Oktober 2018:

  • Studium der Dokumentation und Grundlagen der Codegenerierung;
  • Wir haben den kurzen Entwicklungskurs des Anbieters studiert;
  • Beherrschte anfängliche Entwicklungskompetenzen;
  • Wir haben ein Schulungshandbuch für neue Teammitglieder zusammengestellt.
  • Wir einigten uns darauf, das Team in den „Kampf“-Modus einzubeziehen;
  • Das Problem mit der Codequalitätskontrolle wurde behoben.
  • Wir haben einen Entwicklungsstand organisiert.

Wir verbrachten drei Monate damit, Fachwissen zu entwickeln und in das System einzutauchen, und ab Anfang 2019 begann die interne Entwicklung ihren Weg in eine glänzende Zukunft, manchmal mit Schwierigkeiten, aber zuversichtlich und zielstrebig.

Repository-Migration und Autotests

Die erste DevOps-Aufgabe ist das Repository. Wir einigten uns schnell auf die Bereitstellung des Zugriffs, aber es war notwendig, vom aktuellen SVN mit einem Trunk-Zweig auf unser Ziel-Git zu migrieren, mit dem Übergang zu einem Modell mit mehreren Zweigen und der Entwicklung von Git Flow. Wir verfügen außerdem über zwei Teams mit eigener Infrastruktur sowie einen Teil des Lieferantenteams im Ausland. Ich musste mit zwei Gits leben und für die Synchronisation sorgen. In einer solchen Situation war es das kleinere von zwei Übeln.

Die Migration des Repositorys wurde immer wieder verschoben und erst im April mit Hilfe von Kollegen an vorderster Front abgeschlossen. Bei Git Flow haben wir uns entschieden, die Dinge zunächst einfach zu halten und uns für das klassische Schema mit Hotfix, Entwicklung und Veröffentlichung entschieden. Sie beschlossen, den Master (auch bekannt als Prod-like) aufzugeben. Im Folgenden erklären wir, warum sich diese Option für uns als optimal herausgestellt hat. Als Worker wurde ein externes Repository des Anbieters verwendet, das für zwei Teams gemeinsam ist. Die Synchronisierung mit dem internen Repository erfolgte nach einem Zeitplan. Mit Git und Gitlab war es nun möglich, Prozesse zu automatisieren.

Das Problem der Autotests ließ sich überraschend einfach lösen – uns wurde ein fertiges Framework zur Verfügung gestellt. Unter Berücksichtigung der Besonderheiten des Systems war der Aufruf einer separaten Operation ein verständlicher Teil des Geschäftsprozesses und diente gleichzeitig als Unit-Test. Es blieb nur noch, die Testdaten aufzubereiten und die gewünschte Reihenfolge für den Aufruf der Skripte und die Auswertung der Ergebnisse festzulegen. Als die Liste der Szenarien, die auf der Grundlage von Betriebsstatistiken, der Kritikalität von Prozessen und der bestehenden Regressionsmethodik erstellt wurde, ausgefüllt wurde, begannen automatische Tests zu erscheinen. Jetzt könnten wir mit dem Bau der Pipeline beginnen.

Wie es war: das Modell vor der Automatisierung

Das bestehende Modell des Implementierungsprozesses ist eine eigene Geschichte. Jede Änderung wurde manuell als separates inkrementelles Installationspaket übertragen. Als nächstes folgte die manuelle Registrierung in Jira und die manuelle Installation in Umgebungen. Bei einzelnen Paketen sah alles klar aus, aber mit der Vorbereitung der Veröffentlichung wurde es komplizierter.

Die Montage erfolgte auf der Ebene der Einzellieferungen, die eigenständige Gegenstände waren. Bei jeder Änderung handelt es sich um eine Neulieferung. Unter anderem wurden 60–70 technische Versionen zu den 10–15 Paketen der Hauptveröffentlichungszusammensetzung hinzugefügt – Versionen, die man erhält, wenn man etwas zur Veröffentlichung hinzufügt oder davon ausschließt und Änderungen in den Verkäufen außerhalb der Veröffentlichungen widerspiegelt.

Objekte innerhalb der Lieferungen überschnitten sich, insbesondere im ausführbaren Code, der weniger als die Hälfte eindeutig war. Es gab viele Abhängigkeiten sowohl zum bereits installierten Code als auch zu dem, dessen Installation gerade geplant war. 

Um die erforderliche Version des Codes zu erhalten, musste die Installationsreihenfolge strikt eingehalten werden, bei der Objekte viele Male, etwa 10–12 Mal, physisch neu geschrieben wurden.

Nachdem ich eine Reihe von Paketen installiert hatte, musste ich den Anweisungen manuell folgen, um die Einstellungen zu initialisieren. Das Release wurde vom Anbieter zusammengestellt und installiert. Die Zusammensetzung der Veröffentlichung wurde fast vor der Implementierung geklärt, was die Erstellung von „Entkopplungs“-Paketen zur Folge hatte. Infolgedessen wanderte ein erheblicher Teil der Lieferungen von Release zu Release mit einem eigenen Schwanz von „Entkopplungen“.

Nun ist klar, dass bei diesem Ansatz – dem Zusammensetzen des Release-Puzzles auf Paketebene – ein einzelner Master-Zweig keine praktische Bedeutung hatte. Die Installation in der Produktion erforderte eineinhalb bis zwei Stunden Handarbeit. Gut ist, dass zumindest auf Installer-Ebene die Reihenfolge der Objektverarbeitung festgelegt wurde: Felder und Strukturen wurden vor den Daten dafür und Prozeduren eingegeben. Dies funktionierte jedoch nur innerhalb eines separaten Pakets.

Das logische Ergebnis dieses Ansatzes waren die obligatorischen Installationsfehler in Form von schiefen Versionen von Objekten, unnötigem Code, fehlenden Anweisungen und unerklärlichen gegenseitigen Einflüssen von Objekten, die nach der Veröffentlichung fieberhaft beseitigt wurden. 

Erste Updates: Montage und Lieferung festlegen

Die Automatisierung begann mit der Übertragung von Code über eine Pipe entlang dieser Route:

  • Abholung der fertigen Lieferung vom Lager;
  • Installieren Sie es in einer dedizierten Umgebung.
  • Führen Sie Autotests durch.
  • Bewerten Sie das Installationsergebnis;
  • Rufen Sie die folgende Pipeline auf der Seite des Testbefehls auf.

Die nächste Pipeline sollte die Aufgabe in Jira registrieren und darauf warten, dass Befehle an ausgewählte Testschleifen verteilt werden, die vom Timing der Aufgabenimplementierung abhängen. Auslöser – ein Brief über die Lieferbereitschaft an eine bestimmte Adresse. Das war natürlich eine offensichtliche Krücke, aber ich musste irgendwo anfangen. Im Mai 2019 begann die Übertragung des Codes mit Überprüfungen unserer Umgebungen. Der Prozess hat begonnen, es bleibt nur noch, ihn in einen ordentlichen Zustand zu bringen:

  • Jede Änderung wird in einem separaten Zweig durchgeführt, der dem Installationspaket entspricht und in den Ziel-Master-Zweig übergeht;
  • Der Auslöser für den Pipeline-Start ist das Erscheinen eines neuen Commits im Master-Zweig durch eine Zusammenführungsanforderung, die von Betreuern des internen Teams geschlossen wird;
  • Repositorys werden alle fünf Minuten synchronisiert.
  • Die Assemblierung des Installationspakets wird gestartet – mit dem vom Hersteller erhaltenen Assembler.

Danach gab es bereits Schritte zur Überprüfung und Übertragung des Codes, zum Starten der Pipe und zum Zusammenbau auf unserer Seite.

Diese Option wurde im Juli eingeführt. Die Schwierigkeiten des Übergangs führten zu einer gewissen Unzufriedenheit beim Anbieter und an der Front, aber im Laufe des nächsten Monats gelang es uns, alle Ecken und Kanten zu beseitigen und einen Prozess zwischen den Teams zu etablieren. Wir haben jetzt die Montage per Commit und Lieferung.
Im August gelang es uns, die erste Installation eines separaten Pakets in der Produktion mithilfe unserer Pipeline abzuschließen, und seit September wurden ausnahmslos alle Installationen einzelner Nicht-Release-Pakete über unser CD-Tool durchgeführt. Darüber hinaus ist es uns gelungen, mit einem kleineren Team als beim Anbieter einen Anteil von Inhouse-Aufgaben an 40 % der Release-Zusammensetzung zu erreichen – das ist ein klarer Erfolg. Die schwerwiegendste Aufgabe blieb bestehen – die Veröffentlichung zusammenzubauen und zu installieren.

Die endgültige Lösung: kumulative Installationspakete 

Uns war völlig klar, dass die Skripterstellung für die Anweisungen des Anbieters eine mittelmäßige Automatisierung darstellte; wir mussten den Prozess selbst überdenken. Die Lösung lag auf der Hand: einen kumulativen Vorrat aus dem Release-Zweig mit allen Objekten der erforderlichen Versionen zu sammeln.

Wir begannen mit dem Proof of Concept: Wir haben das Release-Paket entsprechend den Inhalten der vergangenen Implementierung manuell zusammengestellt und in unseren Umgebungen installiert. Es hat alles geklappt, das Konzept hat sich als realisierbar erwiesen. Als Nächstes haben wir das Problem der Skripterstellung der Initialisierungseinstellungen und deren Einbindung in den Commit gelöst. Im Rahmen des Kontur-Updates haben wir ein neues Paket vorbereitet und in Testumgebungen getestet. Die Installation war erfolgreich, wenn auch mit einer Vielzahl von Kommentaren seitens des Implementierungsteams. Aber die Hauptsache ist, dass wir mit unserer Baugruppe die Freigabe erhalten haben, im November-Release in Produktion zu gehen.

Da noch etwas mehr als ein Monat übrig war, deuteten die handverlesenen Vorräte deutlich darauf hin, dass die Zeit knapp wurde. Sie beschlossen, den Build aus dem Release-Zweig zu erstellen, aber warum sollte er getrennt werden? Wir haben kein Prod-ähnliches Produkt und die vorhandenen Zweige nützen nichts – es gibt viel unnötigen Code. Wir müssen dringend Prod-Likes reduzieren, und das sind über dreitausend Commits. Der Zusammenbau per Hand ist überhaupt keine Option. Wir haben ein Skript entworfen, das das Produktinstallationsprotokoll durchläuft und Commits für die Verzweigung sammelt. Beim dritten Mal funktionierte es korrekt und nach dem „Fertigstellen mit einer Datei“ war der Zweig bereit. 

Wir haben unseren eigenen Builder für das Installationspaket geschrieben und ihn in einer Woche fertiggestellt. Dann mussten wir das Installationsprogramm in Bezug auf die Kernfunktionalität des Systems modifizieren, da es Open Source ist. Nach einer Reihe von Prüfungen und Änderungen wurde das Ergebnis als erfolgreich angesehen. In der Zwischenzeit nahm die Zusammenstellung der Veröffentlichung Gestalt an, für deren korrekte Installation es notwendig war, die Testschaltung mit der Produktionsschaltung abzugleichen, und dafür wurde ein separates Skript geschrieben.

Natürlich gab es viele Kommentare zur ersten Installation, aber im Großen und Ganzen funktionierte der Code. Und nach etwa der dritten Installation begann alles gut auszusehen. Die Kompositionskontrolle und Versionskontrolle von Objekten wurden separat im manuellen Modus überwacht, was zu diesem Zeitpunkt durchaus gerechtfertigt war.

Eine zusätzliche Herausforderung war die große Anzahl an Nichtfreigaben, die berücksichtigt werden mussten. Aber mit dem Prod-ähnlichen Zweig und Rebase wurde die Aufgabe transparent.

Beim ersten Mal schnell und fehlerfrei

Wir gingen mit einer optimistischen Einstellung und mehr als einem Dutzend erfolgreicher Installationen auf verschiedenen Rennstrecken an die Veröffentlichung heran. Aber buchstäblich einen Tag vor Ablauf der Frist stellte sich heraus, dass der Anbieter die Arbeiten zur Vorbereitung der Version für die Installation nicht in der akzeptierten Weise abgeschlossen hatte. Wenn unser Build aus irgendeinem Grund nicht funktioniert, wird die Veröffentlichung unterbrochen. Darüber hinaus durch unsere Bemühungen, was besonders unangenehm ist. Wir hatten keine Möglichkeit, uns zurückzuziehen. Deshalb haben wir über alternative Optionen nachgedacht, Aktionspläne erstellt und mit der Installation begonnen.

Überraschenderweise startete das gesamte Release, bestehend aus mehr als 800 Objekten, beim ersten Mal korrekt und in nur 10 Minuten. Wir haben eine Stunde damit verbracht, die Protokolle auf Fehler zu prüfen, konnten aber keine finden.

Den gesamten nächsten Tag herrschte Stille im Release-Chat: Keine Implementierungsprobleme, krumme Versionen oder „unangemessener“ Code. Es war sogar irgendwie umständlich. Später tauchten einige Kommentare auf, deren Anzahl und Priorität im Vergleich zu anderen Systemen und bisherigen Erfahrungen jedoch deutlich geringer waren.

Ein zusätzlicher Effekt aus dem kumulativen Effekt war eine Steigerung der Montage- und Testqualität. Aufgrund mehrfacher Installationen der Vollversion wurden Build-Mängel und Bereitstellungsfehler rechtzeitig erkannt. Durch Tests in Vollversionskonfigurationen konnten zusätzlich Fehler in der gegenseitigen Beeinflussung von Objekten identifiziert werden, die bei inkrementellen Installationen nicht auftraten. Es war definitiv ein Erfolg, insbesondere angesichts unseres 57-prozentigen Beitrags zur Veröffentlichung.

Ergebnisse und Schlussfolgerungen

In weniger als einem Jahr haben wir es geschafft:

  • Erstellen Sie eine vollwertige interne Entwicklung mit einem exotischen System.
  • Eliminieren Sie die Abhängigkeit von kritischen Anbietern.
  • Starten Sie CI/CD für ein sehr unfreundliches Erbe.
  • Implementierungsprozesse auf ein neues technisches Niveau heben;
  • Deutliche Verkürzung der Bereitstellungszeit;
  • Reduzieren Sie die Anzahl der Implementierungsfehler erheblich.
  • Erklären Sie sich selbstbewusst als führender Entwicklungsexperte.

Natürlich sieht vieles von dem, was beschrieben wird, völliger Mist aus, aber das sind die Merkmale des Systems und die darin vorhandenen Prozessbeschränkungen. Derzeit betreffen die Änderungen die Produkte und Dienstleistungen des IS-Profils (Hauptkonten, Plastikkarten, Sparkonten, Treuhandkonten, Barkredite), aber potenziell kann der Ansatz auf jedes IS angewendet werden, für das die Aufgabe der Implementierung von DevOps-Praktiken gestellt wird. Das kumulative Modell kann für nachfolgende Implementierungen (einschließlich Nicht-Release-Implementierungen) aus vielen Lieferungen sicher repliziert werden.

Source: habr.com

Kommentar hinzufügen