Continuous Delivery mit Azure DevOps, Teil 3: Ausrollen per Geisterhand

Seite 2: Datenbank verbreiten

Inhaltsverzeichnis

Die so verteilte Web App wird allerdings nicht laufen können, weil sie ja noch keine Datenbank hat. Azure DevOps stellt einen Task "Azure SQL Database Deployment" bereit, mit dem man eine SQL-Server-Datenbank über ein Data-tier Application Package (DACPAC) oder ein SQL-Skript in SQL Azure installieren kann. Die Firma Redgate bietet zudem einen kostenfreien Erweiterungstask für Azure-DevOps-Release-Pipelines an, mit dem man eine bestehende SQL-Server-Datenbank direkt klonen kann. Im vorliegenden Fall soll jedoch ein anderer Weg eingeschlagen werden: Entity Framework Core, der eingesetzte objektrelationale Mapper, bietet die Möglichkeit, ein Datenbankschema per Programmcode zu installieren beziehungsweise auf eine neuere Version zu aktualisieren und auf Wunsch auch Daten mitzubringen.

Im Quellcode zum MiracleList-Backend gibt es eine .NET-Core-Konsolenanwendung mit Namen EFCTools, die Datenbankschemamigration und Testdatenbefüllung durchführt. Das Werkzeug soll nun im Rahmen der Release-Pipeline ausgeführt werden, was natürlich voraussetzt, dass es auf dem System vorhanden ist, das den Release erstellt. Dafür ist entweder ein weiterer Build-Prozess zu schaffen oder die bestehende Build-Definition zu erweitern. Für die zweite Lösung spricht, dass EFCTools die Geschäftsobjekte (Projekt "BO") und die Datenzugriffsschicht (Projekt "DAL") referenziert und neu kompiliert werden muss, wann immer sich diese Quellcodeprojekte ändern.

Anders als der eigentliche Programmcode, der von der Build- zur Release-Pipeline und schließlich zum Azure Web App Service durchgereicht wird, muss das Werkzeug in der Release-Pipeline startbar sein, also nicht als Zip-Archiv vorliegen, sondern in ein für die Release-Pipeline zugängliches Verzeichnis verpackt werden. Abbildung 3 zeigt die Erweiterung der Build-Pipeline "MiracleListBackend-CI".

Die nun erweiterte Build-Pipeline aus Teil 2 (Abb. 3)

Bei "dotnet publish" wird hier kein Platzhalter, sondern "src/EFCTools/EFCTools.csproj" fest eingetragen. Das Ausgabeverzeichnis ist dann

--output $(build.artifactstagingdirectory)/EFCTools

Dies greift der Task "Publish Build Artifact EFCTools" auf (s. Abb. 3). An dieser wie an vielen anderen Stellen spricht die Benutzeroberfläche noch von "VSTS/TFS" anstelle vom neuen Namen "Azure DevOps". In der Release Pipeline ist dann nur ein Task vom Typ "Command Line" notwendig, der wie folgt konfiguriert ist:

  • Tool = dotnet
  • Arguments = EFCTools.dll migrate createtestuser
  • Working folder = $(System.DefaultWorkingDirectory)/MiracleListBackend-CI/EFCTools/

Die Verbindungszeichenfolge bekommt das Werkzeug genau wie die Unit-Tests in der Build-Pipeline über die Umgebungsvariable "ConnectionStrings:MiracleListDB", die unter "Variables" zu setzen ist, dieses Mal aber mit der Verbindungszeichenfolge zu der SQL-Azure-Datenbank.

Damit legt EFCTools die Datenbank dann mit dem passenden Schema auf dem Datenbankserver an, aber die ASP.NET-Core-Backend-Anwendung muss ja die Verbindungszeichenfolge ebenfalls kennen. Hier hilft es nichts, die Umgebungsvariable auf dem Release-System zu setzen, da diese Anwendung ja nicht hier, sondern in dem Azure Web App Service irgendwo auf einem (Shared-)Cloud-Server gestartet wird. Man muss die Verbindungszeichenfolge daher in die appsettings.json-Datei der Webanwendung schreiben. Das ist möglich durch die Einstellung "JSON variable substitution" im Task "Azure App Service Deploy". Hier trägt man "appsettings.json" ein und definiert eine Variable mit der Verbindungszeichenfolge. Für diesen Fall müssen aber die Hierarchieebenen der JSON-Datei nicht wie bei der Umgebungsvariablen mit einem Doppelpunkt, sondern mit einem Punkt getrennt werden. Man braucht also die Verbindungszeichenfolge zweimal in folgenden beiden Variablen: ConnectionStrings.MiracleListDB und ConnectionStrings:MiracleListDB.

Abbildung 2 zeigt, dass zwischen dem Installieren der Datenbank und dem Ausrollen des App Service noch ein Schritt steht: Die PowerShell wird verwendet, um eine Umgebungsvariable "ReleaseDate" auf den aktuellen Tag und die aktuelle Uhrzeit zu setzen. Auch hier sorgt dann die "JSON variable substitution" im App Service Deployment Task dafür, dass dieser Wert nachher in der appsettings.json steht. Damit kann die Anwendung dann stets von sich selbst sagen, wann sie verbreitet wurde. In dem Task ist das PowerShell-Skript aus dem folgenden Listing erfasst:

# Set variable "AppInfo.ReleaseDate" which will be used by the "Azure App Service Deploy" task to replace values in the appsettings.json file

Write-Host "##vso[task.setvariable variable=AppInfo.ReleaseDate;]$(Get-Date -Format r)"

Das Skript besteht nur aus einer funktionalen Zeile, die merkwürdig erscheint: Das Commandlet Write-Host erzeugt eine Textzeile an der Standardausgabe. Diese ist der Weg, wie man der Release-Pipeline Anweisungen geben kann. Dass es sich um einen Befehl handelt, erkennt die Release-Pipeline an dem vorangestellten ##vso.