So rollen Sie Updates in der Produktion automatisch

Das Starten der neuen Version im Kampfeinsatz ist immer ein nervöses Ereignis. Besonders wenn der Prozess viele manuelle Operationen beinhaltet. Der menschliche Faktor ist eine schreckliche Sache. „Es wäre schön, diesen Prozess zu automatisieren“ - diese Idee ist so alt wie die gesamte IT-Welt. Und dafür gibt es einen Begriff - Continuous Deployment. Ja, das Problem ist, dass es keine eindeutige Möglichkeit gibt, diese kontinuierliche Bereitstellung zu konfigurieren. Dieser Prozess ist sehr stark an den technologischen Stapel des Projekts und seiner Umgebung gebunden.

In diesem Artikel möchte ich praktische Erfahrungen beim Einrichten automatischer Systemaktualisierungen teilen, ohne den Betrieb für eine bestimmte technologische Umgebung zu unterbrechen, nämlich: Die Webanwendung ASP.NET MVC + Azure SQL + Entity Framework im Code First-Modus hat die Anwendung in Azure als App Service bereitgestellt Die Montage und Bereitstellung erfolgt über Azure DevOps (ehemals Visual Studio Team Services).



Auf den ersten Blick ist alles sehr einfach. Azure App Service hat das Konzept eines Bereitstellungssteckplatzes. Laden Sie dort die neue Version herunter und aktivieren Sie sie. Es wäre jedoch einfach, wenn das Projekt auf einem nicht relationalen DBMS basieren würde, in dem es kein starres Datenschema gibt. In diesem Fall ja - nur die neue Version nimmt Verkehr und Voila auf. Bei einem relationalen DBMS ist jedoch alles etwas komplizierter.

Die Hauptfaktoren, die uns daran hindern, eine kontinuierliche Bereitstellung für unseren Technologie-Stack zu implementieren, sind folgende:

  • Die alte Version der Anwendung kann nicht mit der neuen Datenbankstruktur arbeiten
  • Das Aktualisieren der Datenbankstruktur kann viel Zeit in Anspruch nehmen und ist mit der Anwendung selbst über den automatischen Migrationsmechanismus nicht immer möglich.

Ich werde es erklären. Angenommen, Sie haben eine neue Version in einem parallelen Steckplatz oder in einem Sicherungsdatenzentrum bereitgestellt und die Anwendung von Migrationen gestartet. Nehmen wir an, wir haben drei Migrationen, und, entsetzt, zwei sind gerollt, und die dritte ist gefallen. In diesem Moment passiert nichts mit den Arbeitsservern. Das Entity Framework überprüft die Version nicht für jede Anforderung, aber Sie können das Problem wahrscheinlich nicht schnell lösen. Zu diesem Zeitpunkt kann sich die Belastung der Anwendung erhöhen, und die Plattform startet eine zusätzliche Instanz der Anwendung für Sie, und sie wird natürlich nicht gestartet, da sich die Datenbankstruktur geändert hat. Ein erheblicher Teil der Benutzer erhält Fehler. Daher ist das Risiko einer automatischen Anwendung von Migrationen groß.



Was den zweiten Punkt betrifft, kann Ihre Migration eine Art von Befehlen enthalten, deren Ausführungszeit 30 Sekunden überschreitet und deren Standardprozedur mit dem Timeout abfällt. Zusätzlich zu diesen Punkten gefällt mir persönlich nicht, dass Sie bei automatischen Migrationen gezwungen sind, einen Teil der Infrastruktur auf eine neue Version zu aktualisieren. Und wenn dies für einen Modus mit Slots in Azure nicht so beängstigend ist, erhalten Sie für einen Modus mit einem Backup-Rechenzentrum einen Teil der Infrastruktur mit einer bekanntermaßen nicht funktionsfähigen Anwendung. Es ist alles gefährlich, es wird im ungünstigsten Moment schießen.

Was tun?


Beginnen wir mit dem Schwierigsten - mit der Datenbank. Es wäre also schön, die Datenbankstruktur irgendwie automatisch zu aktualisieren, damit die alten Versionen der Anwendung weiterhin funktionieren. Darüber hinaus wäre es schön zu berücksichtigen, dass es solche Aktualisierungen gibt, bei denen ein separater Befehl für eine beträchtliche Zeit ausgeführt werden kann. Dies bedeutet, dass wir die Datenbank nicht mithilfe der integrierten Mechanismen aktualisieren müssen, sondern indem Sie ein separates SQL-Skript ausführen. Frage: Wie bereite ich es vor? Sie können dieses Prozesshandbuch erstellen. Wenn Sie eine separate Release Manager-Rolle im Team haben, können Sie diese zwingen, den Befehl in Visual Studio auszuführen:

update-database -script 

Sie generiert ein Skript und diese Person legt dieses Skript in einem bestimmten Projektordner ab. Aber Sie müssen zugeben, dass dies immer noch unpraktisch ist, zum einen der menschliche Faktor und zum anderen unnötige Schwierigkeiten, wenn zwischen den Releases mehr als eine Migration stattgefunden hat. Oder aus irgendeinem Grund wurde eine Version auf dem Zielsystem übersprungen. Wir müssen einen komplizierten Garten anlegen, in dem nachverfolgt wird, welche Migrationen bereits vorhanden sind und welche gestartet werden müssen. Es ist schwierig und vor allem ist dies das gleiche Fahrrad, das bereits im Migrationsmechanismus hergestellt wurde.

Und es ist richtig, den Skriptgenerierungs- und -ausführungsprozess in den Release-Berechnungsprozess einzubauen. Um ein Migrationsskript zu generieren, können Sie das Dienstprogramm migrate.exe verwenden, das im Entity Framework enthalten ist. Ich mache Sie darauf aufmerksam, dass Sie Entity Framework Version 6.2 oder höher benötigen, da die Option zur Skriptgenerierung in diesem Dienstprogramm erst im April 2017 angezeigt wurde. Der Dienstprogrammaufruf sieht folgendermaßen aus:

 migrate.exe Context.dll /connectionString="Data Source=localhost;Initial Catalog=myDB;User Id=sa;Password=myPassword;" /connectionProviderName="System.Data.SqlClient" /sc /startUpDirectory="c:\projects\MyProject\bin\Release" /verbose 

Der Name der Assembly wird angegeben, in dem sich Ihr Kontext befindet, die Verbindungszeichenfolge zur Zieldatenbank, der Anbieter und vor allem das Startverzeichnis, das sowohl die Assembly mit dem Kontext als auch die Entity Framework-Assembly enthält. Experimentieren Sie nicht mit den Namen des Arbeitsverzeichnisses, seien Sie einfacher. Wir sind auf die Tatsache gestoßen, dass migrate.exe das Verzeichnis nicht lesen konnte, in dessen Namen Leerzeichen und Zeichen ohne Buchstaben vorhanden waren.

Hier ist ein wichtiger Exkurs notwendig. Tatsache ist, dass nach Ausführung des obigen Befehls ein einzelnes SQL-Skript generiert wird, das alle Befehle für alle Migrationen enthält, die auf die Zieldatenbank angewendet werden müssen. Für Microsoft SQL Server ist dies nicht sehr gut. Tatsache ist, dass der Server Befehle ohne das GO-Trennzeichen als einzelnes Paket ausführt und einige Vorgänge nicht zusammen in einem einzelnen Paket ausgeführt werden können.

In einigen Fällen funktioniert es beispielsweise nicht, einer Tabelle ein Feld hinzuzufügen und sofort einen Index für diese Tabelle mit einem neuen Feld zu erstellen. Dies reicht jedoch nicht aus. Einige Befehle erfordern bestimmte Umgebungseinstellungen, wenn das Skript ausgeführt wird. Solche Einstellungen sind standardmäßig aktiviert, wenn Sie über SQL Server Management Studio eine Verbindung zu SQL Server herstellen. Wenn das Skript jedoch über das Konsolendienstprogramm SQLCMD ausgeführt wird, müssen sie manuell festgelegt werden. Um all dies zu berücksichtigen, müssen Sie den Prozess zum Generieren des Migrationsskripts mit einer Datei ändern. Erstellen Sie dazu neben dem Datumskontext eine zusätzliche Klasse, die alles tut, was Sie brauchen:

  public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator { public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken) { var statements = base.Generate(migrationOperations, providerManifestToken); var result = new List<MigrationStatement>(); result.Add(new MigrationStatement { Sql = "SET QUOTED_IDENTIFIER ON;" }); foreach (var item in statements) { item.BatchTerminator = "GO"; result.Add(item); } return result; } } 

Damit das Entity Framework es verwenden kann, registrieren Sie es in der Konfigurationsklasse, die sich normalerweise im Ordner "Migrationen" befindet:

  public Configuration() { SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder()); …. } 

Danach enthält das resultierende Migrationsskript zwischen den einzelnen Anweisungen GO und am Anfang der Datei SET QUOTED_IDENTIFIER ON.

Hurra, die Vorbereitung ist abgeschlossen, es bleibt die Konfiguration des Prozesses selbst. Im Allgemeinen ist dies im Rahmen des Freigabeprozesses in Azure DevOps (VSTS / TFS) bereits recht einfach. Wir müssen ein PowerShell-Skript wie folgt erstellen:

 param ( [string] [Parameter(Mandatory=$true)] $dbserver, [string] [Parameter(Mandatory=$true)] $dbname, [string] [Parameter(Mandatory=$true)] $dbserverlogin, [string] [Parameter(Mandatory=$true)] $dbserverpassword, [string] [Parameter(Mandatory=$true)] $rootPath, [string] [Parameter(Mandatory=$true)] $buildAliasName, [string] [Parameter(Mandatory=$true)] $contextFilesLocation, ) Write-Host "Generating migration script..." $fullpath="$rootPath\$buildAliasName\$contextFilesLocation" Write-Host $fullpath & "$fullpath\migrate.exe" Context.dll /connectionProviderName="System.Data.SqlClient" /connectionString="Server=tcp:$dbserver.database.windows.net,1433;Initial Catalog=$dbname;Persist Security Info=False;User ID=$dbserverlogin;Password=$dbserverpassword;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" /startUpDirectory=$fullpath /verbose /scriptFile=1.SQL Write-Host "Running migration script..." & "SQLCMD" -S "$dbserver.database.windows.net" -U $dbserverlogin@$dbserver -P $dbserverpassword -d $dbname -i 1.SQL Write-Host "====Finished with migration script====" 

Fügen Sie die PowerShell-Skriptausführungseinheit zum Release-Berechnungsprozess hinzu. Der Block und seine Einstellungen können folgendermaßen aussehen:



Das PowerShell-Setup sieht ungefähr so ​​aus:



Es ist wichtig, nicht zu vergessen, die Datei migrate.exe aus dem Ordner <IhrProjekt> /packages/EntityFramework.6.2.0/tools/ zum Projekt hinzuzufügen und die Eigenschaft Immer kopieren festzulegen, damit dieses Dienstprogramm beim Erstellen des Projekts in das Ausgabeverzeichnis kopiert wird und Sie darauf zugreifen können Azure DevOps-Version.

Die Nuance . Wenn Ihr Projekt bei der Bereitstellung für Azure App Service auch WebJob verwendet, ist das Hinzufügen von Migrate.exe zu Ihrem Projekt nicht sicher. Wir sind mit der Tatsache konfrontiert, dass in dem Ordner, in dem Ihr WebJob veröffentlicht wird, die Azure-Plattform dumm die erste Exe-Datei startet, die auftaucht. Und wenn Ihr WebJob später alphabetisch kostet migrate.exe (und wir haben es getan), versucht es, migrate.exe anstelle Ihres Projekts auszuführen!

Wir haben also gelernt, wie die Datenbankversion durch Generieren eines Skripts während des Veröffentlichungsprozesses aktualisiert wird. Das Einfache ist: Deaktivieren Sie die Überprüfung der Migrationsversion, damit die alte Version unseres Codes weiterhin funktioniert, wenn beim Ausführen des Skripts Fehler auftreten. Ich denke, es besteht kein Grund zu sagen, dass Ihre Migrationen zerstörungsfrei sein sollten. Das heißt, Änderungen an der Datenbankstruktur sollten die Leistung der vorherigen Version nicht beeinträchtigen, aber besser als die beiden vorherigen. Um die Überprüfung zu deaktivieren, müssen Sie Web.config nur den folgenden Block hinzufügen:

  <entityFramework> <contexts> <context type="<full namespace for your DataContext class>, MyAssembly" disableDatabaseInitialization="true"/> </contexts> </entityFramework> 

Wobei der full namespace for your DataContext class der vollständige Namespace für Ihren Nachkommen von DbContext ist und MyAssembly der Name der Assembly ist, in der sich Ihr Kontext befindet.

Und schließlich ist es für uns äußerst wünschenswert, sicherzustellen, dass sich die Anwendung erwärmt, bevor Benutzer auf die neue Version umgestellt werden. Fügen Sie dazu web.config einen speziellen Block mit Links hinzu, die Ihre Anwendung während des Initialisierungsprozesses automatisch beendet:

  <system.webServer> <applicationInitialization doAppInitAfterRestart="true"> <add initializationPage="/" hostName="" /> </applicationInitialization> </system.webServer> 

Sie können mehrere Links hinzufügen, indem Sie einfach /> hinzufügen /> Es wird argumentiert, dass die Plattform in Azure beim Wechseln der Slots auf die Initialisierung der Anwendung wartet und erst dann den Datenverkehr auf die neue Version umschaltet.

Aber was ist mit einem Projekt auf .NET Core?


Alles ist viel einfacher und gleichzeitig anders. Ein Migrationsskript kann mit regulären Tools erstellt werden, basiert jedoch nicht auf der fertigen Assembly, sondern auf der Projektdatei. Daher sollte das Skript als Teil des Projektassemblierungsprozesses erstellt und als Assemblierungsartefakt enthalten sein. In diesem Fall enthält das Skript alle Befehle aller Migrationen ab Beginn der Zeit. Hierbei gibt es keine Probleme, da das Skript idempotent ist, d.h. Es kann wiederholt ohne Konsequenzen auf die Zielbasis angewendet werden. Dies hat eine weitere nützliche Konsequenz: Wir müssen den Skriptgenerierungsprozess nicht ändern, um die Befehle in Pakete zu unterteilen. Dafür wurde bereits alles getan.

Nun, speziell sehen die Schritte des Prozesses so aus. Fügen Sie im Erstellungsprozess die Aufgabe hinzu:



Wir konfigurieren es so, dass eine Datei mit Migrationen generiert wird:



Vergessen Sie nicht, dem PowerShell-Projekt ein Skript hinzuzufügen, das die Migration (oben beschrieben) und die Migrationsdatei selbst ausführt. Nach dem Erstellen des Projekts sehen die Artefakte möglicherweise ungefähr so ​​aus (zusätzlich zum eigentlichen Archiv mit der Assembly gibt es ein zusätzliches PS-Skript und ein SQL-Skript mit Migrationen):



Es bleibt nur im entsprechenden Release-Schritt, die Ausführung dieses PowerShell-Skripts auf die oben beschriebene Weise zu konfigurieren.

Über den Autor


Pavel Kutakov ist Experte für Cloud-Technologien, Entwickler und Architekt von Softwaresystemen in verschiedenen Geschäftsbereichen - vom weltweiten Bank-IP von den USA über Papua-Neuguinea bis hin zu einer Cloud-Lösung für den nationalen Lotteriebetreiber.

Source: https://habr.com/ru/post/de436670/


All Articles