Unser Team ist verantwortlich für den Betrieb und die Entwicklung eines großen Unternehmensprodukts.
Anfang 2017 haben wir uns entschlossen, die Entwicklung und Bereitstellung unserer Anwendung zu überprüfen, nachdem wir eine Pause von einer wichtigen Implementierung eingelegt und die „Lessons Learned“ erneut gelesen hatten. Wir waren besorgt über die geringe Geschwindigkeit und Qualität der Lieferung und konnten nicht den Service bieten, den Kunden von uns erwarten.
Es war Zeit, von Worten zu Taten überzugehen - Prozesse zu ändern.
In diesem Artikel wird kurz darauf eingegangen, wo wir angefangen haben, was wir getan haben, wie die Situation jetzt ist, auf welche Schwierigkeiten wir gestoßen sind, was wir zurücklassen müssen, was wir sonst noch vorhaben.
Starten Sie
Ein wenig über das System
Die Anwendung ist ein klassisches Beispiel für eine monolithische Unternehmensanwendung der "Architektur der 2000er Jahre":
- Betrieben und entwickelt über 15 Jahre.
- Es handelt sich um eine Reihe von anderthalb Dutzend WinForms, Windows-Diensten und ASP .Net-Anwendungen, die an eine einzelne MS SQL-Datenbank gebunden sind.
- Codebasisgröße: ~ 1MLOC für C #, ~ 9000 Datenbankobjekte. Ein Großteil der Geschäftslogik läuft auf der Datenbankseite.
- Die Anwendung besteht aus über 250 Lösungen zum Erstellen eines Win / Web-Clients (eine Lösung pro Gruppe verwandter Formulare). Dies ist ein Erbe des vorherigen Entwicklungsprozesses und der Client-Architektur.
- Die Anwendung unterstützt verschiedene Arten von Prozessen (Clients) durch Ändern der internen Konfiguration: Festlegen von Prozessen, Berechtigungen, flexiblen Feldern usw. in den Konfigurationstabellen der Systemdatenbank. Gleichzeitig ist die Anwendungscodebasis für alle Clients gleich.
- Die Anwendung wird auf mehr als 25 Standorten bereitgestellt und unterstützt (jeder Standort ist eine unabhängige Instanz des Systems) und bedient insgesamt mehrere tausend Endbenutzer in verschiedenen Zeitzonen.
- Die Entwicklung und Montage der fertigen Anwendung und ihrer Komponenten erfolgt durch den Auftragnehmer.
- Der Code wurde auf der Seite des Auftragnehmers gespeichert (lokale Version von MS TFS). Der Code wird monatlich in Form eines Archivs der aktuellen Version des Haupt-Repository-Zweigs an den Kunden übermittelt.
- Die Lieferung erfolgte durch Lieferung von "Delta-Updates": für die Anwendung (Satz von DLL, Exe usw.) und Datenbankkomponenten (Satz von SQL-Skripten erstellen / ändern). Die Anwendung wurde erstellt und die Delta-Pakete vom Auftragnehmer vorbereitet.
- Der Bereitstellungsprozess wurde vom Transportsystem unterstützt, Änderungen wurden automatisch übernommen.
Die Lieferung erfolgt im Rahmen monatlicher Veröffentlichungen (wie ich es arrangiert habe, habe ich Ihnen hier früher gesagt).
Bestehende Probleme
Mangel an Kontrolle
- Trotz des formellen Eigentums an dem Code war die tatsächliche Zusammenstellung des Antrags durch den Kunden nicht möglich.
- Infolgedessen ist es unmöglich, die Funktionsfähigkeit des an den Kunden übermittelten Codes zu überprüfen.
- Änderungen im Code - für den Kunden nicht transparent. Es ist nicht möglich, die angeforderten und tatsächlichen Änderungen im Produkt abzugleichen.
- Die Codeanalyse ist für SQL schwierig und für C # -Komponenten unmöglich
Arbeitseinsatz und Fehler
- Die Erstellung von "Delta-Paketen" ist ein zeitaufwändiges Entwicklungsverfahren, eine Fehlerquelle und bestimmte Projektkosten.
- Für die Bereitstellung einer Delta Packet-Anwendung muss die Reihenfolge der Pakete verfolgt werden. Ein Paketfehler außerhalb der Reihenfolge ist ein großes Bereitstellungsproblem und eine erhebliche Ursache für Vorfälle.
- Regelmäßig treten Regressionen auf: Fehler, die anscheinend repariert und Korrekturen am Produkt vorgenommen wurden, traten erneut auf.
Einschränkungen
- Die Möglichkeit, den Status des Systems zu einem früheren Zeitpunkt wiederherzustellen (Rollback-Änderungen), fehlt praktisch.
- Die Fähigkeit, Entwicklungs- und frühzeitige Testressourcen effektiv zu skalieren, indem Kundenmitarbeiter angezogen werden, fehlt praktisch.
Erwartete Ergebnisse
Zu Beginn des Projekts haben wir uns offensichtliche Ziele gesetzt, um die oben genannten Probleme zu lösen.
- Übertragen Sie das Code-Repository an die Kundenkontrolle
- Verschieben Sie den Anwendungserstellungsprozess auf die Kundenseite
- Ändern Sie den Prozess der Verteilung von Änderungen, indem Sie das „Delta der Änderungen“ zugunsten einer vollständigen Aktualisierung aufgeben
Zusätzlich haben wir unter Verwendung der Lösungen, die erhalten wurden, als die ersten beiden Ziele erreicht wurden, berechnet:
- Verbessern Sie die technische Qualität der resultierenden Lösungen durch Code-Kontrolle
- Steigern Sie das Testengagement und die Benutzerfreundlichkeit, indem Sie eine Self-Service-Bereitstellung bereitstellen.
Etappen eines langen Weges
Analyse des aktuellen Standes der Entwicklungsprozesse
Erster Schritt: Analyse des bestehenden Entwicklungsprozesses für Auftragnehmer. Dies half, die Änderungen so zu planen, dass die Arbeit nach Möglichkeit nicht unterbrochen wurde.
Leider hat die Kenntnis des Entwicklungsprozesses gezeigt, dass der Prozess nach heutigem Verständnis der IT-Branche nicht vorhanden war.
- Der Datenbankcode und die Geschäftslogik dafür wurden nicht aktuell im Repository verwaltet. Der Hauptgrund: das Fehlen von Tools, die die Assembly aus dem Code im Repository implementieren, und die Bereitstellung des Ergebnisses. Der Code im Repository ist also nur eine Dokumentation.
- Die "echte" Version des Datenbankcodes befindet sich in der gemeinsamen "Entwicklungsdatenbank", an der Dutzende von Entwicklern arbeiten.
- Der Clientanwendungscode (C #, ASP.NET) wurde im Repository verwaltet, die Qualität und Aktualität der Festschreibungen wurde jedoch nicht garantiert.
- Die Montage der Komponenten (nicht der gesamten Anwendung) erfolgte an den Entwicklerstationen. Es ist nicht ganz klar, wie der Code vor dem Zusammenbau aktualisiert wurde. Die zusammengesetzte Komponente wurde in einem freigegebenen freigegebenen Ordner angeordnet. Von dort wurde ein "Delta-Paket" für den Kunden gebildet.
- Der völlige Mangel an Praxis bei der Pflege von Entwicklungszweigen. Durch indirekte Anzeichen haben wir dies lange vermutet - aber nachdem wir in den Prozess eingetaucht waren, wurde alles offensichtlich.
Wechsel zu einem neuen Repository und Versionskontrollsystem
Die Abhängigkeit von MS-Plattformen und Unternehmensstandards bestimmte die Wahl der Entwicklungsumgebung - Team Foundation Server.
Als wir das Projekt jedoch direkt starteten (April 2017), wurde gerade die Version von Visual Studio Team Services veröffentlicht. Das Produkt schien sehr interessant zu sein, es wurde als strategische Richtung für MS festgelegt, es bot Git-Repositories, Montage und Bereitstellung für On-Prem und Cloud.
Das On-Prem-TFS für Unternehmen blieb hinter der Version und Funktionalität von VSTS zurück. Die Migration auf die neue Version wurde erst diskutiert. Wir wollten nicht warten. Wir haben uns entschlossen, sofort auf VSTS umzusteigen, da dies unsere Gemeinkosten für die Unterstützung der Plattform senkte und uns die volle Kontrolle darüber gab, wie und was wir tun.
Zum Zeitpunkt des Beginns der Änderungen hatte das Entwicklungsteam Erfahrung mit TFSVC, der Anwendungscode wurde in einem solchen Repository gespeichert. Auf der anderen Seite ist GIT längst zum Standard für die IT-Community geworden - der Kunde und Berater von Drittanbietern haben empfohlen, auf dieses System umzusteigen.
Wir wollten, dass das Entwicklungsteam an der Entscheidung für ein neues Versionskontrollsystem beteiligt ist und eine fundierte Entscheidung trifft.
Wir haben zwei Projekte in VSTS mit unterschiedlichen Repositorys bereitgestellt - TFSVC und GIT. Es wurde eine Reihe von Szenarien definiert, die vorgeschlagen wurden, um die Verwendbarkeit in jedem der Systeme zu testen und zu bewerten.
Unter den bewerteten Szenarien waren:
- Zweige erstellen und zusammenführen
- Organisation gemeinsamer Arbeit (in einem oder mehreren Zweigen)
- Kettenoperationen ändern (Festschreiben, Rückgängig machen)
- Integration von Drittanbietern
- Die Möglichkeit, weiter zu arbeiten, wenn der Server nicht verfügbar ist.
Infolgedessen wurde erwartungsgemäß GIT ausgewählt, und bisher hat es niemand bereut.
Als Prozess haben wir begonnen, GitFlow zu verwenden. Dieser Prozess bot genügend Kontrolle über die Änderungen und ermöglichte die Lieferung von Releases, wie wir es gewohnt sind.
- Wir haben den Entwicklungszweig mit einer Richtlinie verteidigt, die verlangt, dass alle Änderungen Pull-Anforderungen durchlaufen.
- Wir versuchen, die Praxis von "einem Ticket - eine Pull-Anforderung" einzuhalten. Änderungen von verschiedenen Tickets werden niemals in einer Änderung kombiniert. Wir versuchen unser Bestes, um den Feature-Zweig zu testen, um die Situation mit Korrekturen bei nachfolgenden Pull-Anforderungen zu vermeiden.
- Beim Zusammenführen mit Develop werden alle Änderungen zu einem einzigen Commit (Squash) zusammengeführt.
- Release-Zweige werden aus Develop erstellt.
- Bei Bedarf können Sie im Release-Zweig die neuesten Änderungen selektiv (Cherry-Pick) oder alle (Rebase) hinzufügen. Wir führen die Korrektur nicht direkt im Release-Zweig durch.
- Nach der Bereitstellung der neuesten Version für das Produkt wird es per Push Force an den Master gesendet (nur wenige Personen haben dieses Recht).
Automatisierung der Produktmontage
Die Anwendung bestand aus einer großen Anzahl von Baugruppen, Hunderten von Lösungen. Wie sich während des Prozessaudits herausstellte, wurde dies alles separat und „manuell“ erfasst.
In der ersten Phase haben wir beschlossen, nicht alles von Grund auf neu zu erstellen (um die vorhandene Lieferung nicht zu stoppen), sondern die Assembly in eine Reihe von msbuild-Skripten zu "verpacken" - ein Skript pro Komponente.
So erhielten wir schnell Skripte, die alle notwendigen Zwischenartefakte ausführten, und am Ende das fertige Produkt.
Eine separate Geschichte ist ein Datenbankdesign. Leider enthält das System mehrere CLR-Komponenten, die nicht gut strukturiert waren. Abhängigkeiten erlauben keine einfache Bereitstellungsbasis mit Inhalten. Derzeit wird dies durch ein Skript vor der Bereitstellung gelöst.
Aufgrund der ungleichmäßigen Systemlandschaft (SQL Server-Versionen 2008 und 2014 wurden an verschiedenen Stellen installiert) war es außerdem erforderlich, die Zusammenstellung des Basisprojekts für die .NET-Versionen 2.0 und 4.0 zu organisieren.
Nachdem alle Skripte fertig und getestet waren, wurden sie im Build-Skript VSTS verwendet.
Unmittelbar vor Beginn der Montage wurden die Versionen aller Produkte auf eine gemeinsame Standardnummer aktualisiert, einschließlich der Build-Through-Nummer des Builds. Die gleiche Nummer wurde im Post-Deployment-Skript gespeichert. Somit waren alle Komponenten - die Datenbank und alle Clientanwendungen - konsistent und gleich nummeriert.
Bereitstellung auf dem Prüfstand
Nachdem die erste Version des Erstellungsprozesses abgeschlossen war, haben wir mit der Vorbereitung des Bereitstellungsskripts fortgefahren.
Es wird erwartet, dass die Datenbank am problematischsten war.
Die Bereitstellung einer „Top“ -Kopie einer realen Datenbank ergab viele Konflikte zwischen der Assembly und dem Status realer Systeme:
- Inkonsistente Versionen in GIT und im realen System
- Datenbankschemata von Benutzern, deren Löschung geplant war.
Stabilisierung des Entwicklungsprozesses
Natürlich ist es seltsam, darüber zu sprechen und noch mehr hier zu schreiben, aber die schwerwiegendste Änderung für Entwickler war die Einführung des Prinzips "Wenn dies nicht in git ist, existiert es nicht". Zuvor wurde der Code "zur Berichterstattung an den Kunden" festgeschrieben. Jetzt - ohne das ist es unmöglich, etwas zu liefern.
Das Schwierigste war mit dem Datenbankcode. Nach dem Wechsel zur Bereitstellung der Datenbank aus dem Repository durch Zusammenstellung und Bereitstellung mithilfe von SQL Package wurde der "Delta" -Ansatz durch den "gewünschten Status" -Ansatz ersetzt. Pakete gehörten der Vergangenheit an, alles musste automatisch bereitgestellt werden.
Aber! Bis zum vollständigen Übergang zum neuen Bereitstellungsprozess mussten noch Änderungen bereitgestellt werden. Und das musste auf die altmodische Art und Weise geschehen - „Delta-Updates“.
Wir standen vor der Aufgabe, eine vollständige und konstante Konsistenz des Systemzustands bei der Zustellung von Delta-Paketen und des Inhalts des Repositorys sicherzustellen.
Zu diesem Zweck haben wir den folgenden Prozess organisiert:
- Regelmäßig wurde Code aus dem Repository gesammelt und in einer leeren "Modell" -Datenbank bereitgestellt.
- Basierend auf der "Modell" -Basis wurde ein spezieller Autotest vorbereitet. Für jedes Objekt der Modelldatenbank wurden Prüfsummen berechnet. Der Autotest enthält alle diese Prüfsummen und berechnet beim Start die Prüfsummen der entsprechenden Objekte der "geprüften" Datenbank. Jede Diskrepanz in der Zusammensetzung von Objekten oder deren Prüfsummen führt zu einem Abfall des Tests.
- Der "fallende" Test verbot automatisch die Übertragung von Paketen aus der Testumgebung weiter unten in der Landschaft. Eine solche Integration wurde bereits im vorherigen Verkehrssystem implementiert.
Mit der automatischen Steuerung war es somit möglich, den Produktdatenbankcode relativ schnell auf den neuesten Stand zu bringen und ohne zusätzlichen Aufwand seitens des Projektteams zu pflegen. Gleichzeitig gewöhnten sich die Entwickler an die Notwendigkeit, Code korrekt und rechtzeitig in das Repository zu übertragen.
Produktbereitstellung in Integrationstestumgebungen
Nach Abschluss der vorherigen Phase haben wir die Anwendung direkt in einer Testumgebung bereitgestellt. Wir haben die Anwendung von Delta-Paketen auf Testsysteme vollständig eingestellt und auf die automatische Bereitstellung mit VSTS umgestellt.
Von diesem Moment an erhielt das gesamte Team die ersten Früchte der früher aufgewendeten Bemühungen: Der Einsatz erfolgte ohne zusätzliche Anstrengungen. Benutzerdefinierter Code wurde automatisch erfasst, bereitgestellt und getestet.
Wie wir später verstanden haben, führte die „Ausrichtung des Repositorys“ leider dazu, dass wir eine Version der stabil unterstützten Version von „Entwickeln“ hatten, aber die Version von „Produktion“ war immer noch nicht verfügbar. Daher gab es mit QAS und PRD nichts, was über die Testumgebung hinausging.
Der Anwendungscode auf der Datenbankseite könnte mit dem produktiven Code verglichen werden und die Unterschiede verstehen. Es gab nichts, mit dem man Clientanwendungen vergleichen konnte - es gab nur eine aktuelle produktive Version in Form einer Reihe ausführbarer Dateien, aus denen sie kompiliert wurden, was man nicht mit Sicherheit sagen konnte.
Testen des Produkts als Ergebnis einer automatischen Montage
Nach einer Änderung des Montageansatzes musste das Produkt umfangreichen Regressionstests unterzogen werden. Es musste sichergestellt werden, dass die Anwendung funktionierte und nichts verloren ging.
Beim Testen stellte sich heraus, dass die Funktionalität auf der Seite der Datenbank einfacher war. Glücklicherweise haben wir eine Reihe von Autotests entwickelt, die kritische Bereiche abdecken.
Es gab jedoch keine Tests für C # - daher wurde alles von Hand überprüft. Dies war ein erheblicher Arbeitsaufwand, und die Überprüfung dauerte einige Zeit.
Glaubenssprung - produktiver Piloteinsatz
Trotz der Tests war die erstmalige Bereitstellung auf einem Produkt beängstigend.
Wir hatten Glück - wir hatten gerade die nächste Bereitstellung des Systems an einem neuen Standort geplant. Und wir haben uns entschlossen, diese Chance für einen Piloteinsatz zu nutzen.
Benutzer haben nicht gesehen, mögliche Fehler der neuen Baugruppe waren einfach zu beheben, echte produktive Arbeit hat noch nicht begonnen.
Wir haben das System bereitgestellt und es befand sich mehrere Wochen lang im vorproduktiven Modus (geringe Last, ein bestimmtes Nutzungsmuster, das im Produkt übersprungen werden kann). Während dieser Zeit wurden mehrere Fehler entdeckt, die während des Tests übersehen wurden. Sie wurden korrigiert, als sie gefunden wurden, und die neue Version wurde sofort zur Überprüfung bereitgestellt.
Nach dem offiziellen Start und einer Woche Support nach dem Start haben wir angekündigt, dass dies das erste Exemplar ist, das "auf neue Weise" zusammengestellt und geliefert wird.
Diese Version der Assembly wurde zur ersten stabilen Version des Hauptzweigs. Sie wurde mit den Feiertags-Tags "fisrt_deployment" aufgehängt (wir haben keine Symbole mit einem Hash des Commits bestellt).
Skalieren Sie die Bereitstellung in einer gesamten produktiven Landschaft
Wie James Bond sagte: "Das zweite Mal ist viel einfacher." Nach dem Erfolg der Pilotbereitstellung haben wir die verbleibenden Instanzen von Systemen eines ähnlichen Typs schnell verbunden.
Das System hat jedoch mehrere Verwendungsarten - eine Funktionalität kann für einen Typ verwendet werden und wird in anderen Fällen nicht verwendet. Dementsprechend garantierte die bei der Implementierung des ersten Typs getestete Funktionalität nicht unbedingt den Erfolg für andere Fälle.
Um die Funktionalität der verbleibenden Nutzungsarten zu testen, haben wir begonnen, aktive Projekte zu verwenden, die sich in der Entwicklung befinden. Die Idee war ähnlich und die erste Bereitstellung - wir haben angefangen, automatische Baugruppen zu verwenden, um sie zusammen mit der Entwurfsfunktionalität an die Benutzer weiterzugeben. So überprüften Benutzer, die mit der "Projekt" -Version des Produkts arbeiteten, gleichzeitig die alte Funktionalität.
Die Skalierung selbst ergab unerwartete technische Probleme:
Inhomogene Systemlandschaft
Neben der direkten Bereitstellung der Anwendung mussten wir zunächst darauf achten, dass überall alles gleich war - .NET-Versionen, Powershell und Module. Es hat ziemlich viel Zeit gekostet.
Netzwerkverbindung
An einigen Standorten erlaubte die Netzwerkverbindung einfach nicht das Pumpen aller Komponenten der Baugruppe. Es gab Timeouts, Schäden während der Übertragung. Wir haben viele Dinge überprüft und ausprobiert - nicht sehr erfolgreich.
Ich musste mich mit der folgenden Lösung befassen: Das Assemblerskript wurde fertiggestellt, sodass alle Ergebnisse in ein großes Archiv gepackt wurden, das dann in kleine Fragmente (jeweils 2 MB) geschnitten wurde. Wir haben das Bereitstellungsszenario abgeschlossen, um die Parallelität beim Herunterladen von Artefakten zu beseitigen, alle 2-Megabyte-Fragmente akzeptiert und daraus wiederhergestellt, was bereits erweitert werden kann.
Konflikt mit Antivirus
Ein weiteres seltsames Problem ist ein Konflikt zwischen Antivirensoftware und einem der Bereitstellungsschritte: Wenn alle Arten von „verdächtigen“ Dateien wie .js, .dll aus Artefaktarchiven extrahiert werden, beginnt das Antivirus, sie genau zu untersuchen. Und das Seltsamste ist, dass das Antivirenprogramm vor dem Ende des Entpackens auf die Datei stürzt und der Entpackvorgang mit der Meldung "Die Datei wird von einem anderen Prozess belegt" beendet wird. Wir haben zwar Probleme damit, den lokalen Ordner mit Artefakten vom Scan auszuschließen, aber es ist nicht sehr gut, aber wir haben uns nichts anderes ausgedacht.
Prozessverbesserung
, " " — .
- (service-now.com) VSTS Work Items. develop — .
- CI feature . —
- "self-service"
- — . , .
- : , CI/CD ,
- ( )
- - ( ) . — , .
- VSTS , , .
Zusammenfassung
- MS VisualStudio Team Services ( — Azure Devops) . — GIT
- (/)
- git / GitFlow .
- code review .
- CI. , feature , .
- . , .
- ( ) — . - .
- - 1 . - .
- "" .
Nein, nein. | | Dauer |
---|
1 | — , | 6 |
2 | — | 3 |
3 | — | 5 |
— 14
, , , .
, — 250 * .