Erstellen einer Pipeline für Continuous Integration und Continuous Deplyoment mit DSC
Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018
Hinweis
In Microsoft Team Foundation Server (TFS) 2018 und früheren Versionen werden Build- und Release-Pipelines als Definitionen bezeichnet, Ausführungen werden als Builds bezeichnet, Dienstverbindungen werden als Dienstendpunkte bezeichnet, Stages werden als Umgebungen bezeichnet und Aufträge werden als Phasen bezeichnet.
In diesem Beispiel wird veranschaulicht, wie Sie mithilfe von PowerShell, DSC und Pester eine Pipeline (Continuous Integration/Continuous Deployment, CI/CD) erstellen.
Nachdem die Pipeline erstellt und konfiguriert wurde, können Sie sie für das vollständige Bereitstellen, Konfigurieren und Testen eines DNS-Servers sowie der zugeordneten Hosteinträge verwenden. Dieser Prozess simuliert den ersten Teil einer Pipeline, die in einer Entwicklungsumgebung verwendet werden würde.
Eine automatisierte CI/CD-Pipeline unterstützt Sie bei einer schnelleren und zuverlässigeren Aktualisierung Ihrer Software. Außerdem wird sichergestellt, dass der gesamte Code getestet wird und dass immer ein aktueller Build Ihres Codes verfügbar ist.
Voraussetzungen
Um dieses Beispiel zu verwenden, sollten Sie mit folgenden Konzepten und Modellen vertraut sein:
- CI-CD-Konzepte, eine gute Referenz bietet The Release Pipeline Model (Das Releasepipeline-Modell)
- Git-Quellcodeverwaltung
- Das Pester-Testframework
- Desired State Configuration(DSC)
Benötigte Komponenten
Um dieses Beispiel zu erstellen und auszuführen, benötigen Sie eine Umgebung mit verschiedenen physischen und/oder virtuellen Computern.
Client
Dies ist der Computer, auf dem Sie die gesamte Arbeitseinrichtung vornehmen und das Beispiel ausführen. Der Clientcomputer muss ein Windows-Computer sein, auf dem folgende Komponenten installiert sind:
- Git-Client
- Ein aus https://github.com/PowerShell/Demo_CI geklontes lokales Git-Repository
- ein Text-Editor, z. B. Visual Studio Code
Azure DevOps-Abonnement
Eine Azure DevOps-Organisation. Wenn Sie noch keines besitzen, können Sie es kostenlos erstellen. (Eine Azure DevOps-Organisation unterscheidet sich von Ihrer GitHub-Organisation. Geben Sie ihnen denselben Namen, wenn Sie die Ausrichtung zwischen ihnen wünschen.)
TFSSrv
Der Computer, der den TFS-Server hostet, auf dem Sie Build und Release definieren. Auf diesem Computer muss Team Foundation Server 2017 installiert sein.
BuildAgent
Der Computer, auf dem der Windows-Build-Agent ausgeführt wird, mit dem das Projekt erstellt wird. Auf diesem Computer muss ein Windows-Build-Agent installiert sein und ausgeführt werden. Anweisungen zum Installieren und Ausführen eines Windows-Build-Agents finden Sie unter Bereitstellen eines Agents unter Windows.
Sie müssen außerdem die DSC-Module xDnsServer
und xNetworking
auf diesem Computer installieren.
TestAgent1
Dies ist der Computer, der anhand der DSC-Konfiguration in diesem Beispiel als DNS-Server konfiguriert wird. Auf dem Computer muss Windows Server 2016 ausgeführt werden.
TestAgent2
Dies ist der Computer, der die in diesem Beispiel konfigurierte Website hostet. Auf dem Computer muss Windows Server 2016 ausgeführt werden.
Hinzufügen des Codes zu einem Repository
Wir beginnen mit dem Erstellen eines Git-Repositorys und importieren den Code aus Ihrem lokalen Repository auf dem Clientcomputer. Wenn Sie das Demo_CI-Repository noch nicht auf Ihren Clientcomputer geklont haben, holen Sie dies jetzt nach, indem Sie den folgenden Git-Befehl ausführen:
git clone https://github.com/PowerShell/Demo_CI
Navigieren Sie auf Ihrem Clientcomputer in einem Webbrowser zu Ihrem TFS-Server.
Erstellen Sie ein neues Teamprojekt mit dem Namen Demo_CI.
Stellen Sie sicher, dass Versionskontrolle auf Git festgelegt ist.
Fügen Sie dem soeben auf Ihrem Clientcomputer in TFS erstellten Repository mit dem folgenden Befehl ein Remoterepository hinzu:
git remote add tfs <YourTFSRepoURL>
Hierbei steht
<YourTFSRepoURL>
für die URL zum Klonen des TFS-Repositorys, das Sie im vorherigen Schritt erstellt haben.Wenn Sie nicht wissen, wo Sie diese URL finden, lesen Sie den Artikel Klonen eines vorhandenen Git-Repositorys.
Pushen Sie mit dem folgenden Befehl den Code aus Ihrem lokalen Repository in Ihr TFS-Repository:
git push tfs --all
Das TFS-Repository wird mit dem Demo_CI-Code aufgefüllt.
Navigieren Sie zu Ihrem Azure DevOps-Abonnement in einem Webbrowser.
Erstellen Sie ein neues Teamprojekt mit dem Namen Demo_CI. Stellen Sie sicher, dass Versionskontrolle auf Git festgelegt ist.
Fügen Sie auf Ihrem Clientcomputer dem soeben erstellten Repository eine Remoteverbindung mit dem folgenden Befehl hinzu:
git remote add devops <YourDevOpsRepoURL>
Wo
<YourDevOpsRepoURL>
befindet sich die Klon-URL zum Azure DevOps-Repository, das Sie im vorherigen Schritt erstellt haben.Wenn Sie nicht wissen, wo Sie diese URL finden, lesen Sie den Artikel Klonen eines vorhandenen Git-Repositorys.
Pushen Sie mit dem folgenden Befehl den Code aus Ihrem lokalen Repository in Ihr TFS-Repository:
git push devops --all
Das Azure DevOps-Repository wird mit dem Demo_CI Code aufgefüllt.
Hinweis
Dieses Beispiel verwendet den Code im ci-cd-example
-Branch des Git-Repositorys. Stellen Sie sicher, dass Sie diese Verzweigung als Standardzweig in Ihrem Projekt angeben und für die VON Ihnen erstellte CI/CD ausgelöst wird.
Grundlegendes zum Code
Sehen wir uns vor dem Erstellen der Build- und Bereitstellungspipelines einige Codeabschnitt an, um deren Bedeutung zu verstehen. Öffnen Sie auf dem Clientcomputer Ihren bevorzugten Text-Editor, und navigieren Sie zum Stamm Ihres Git-Repositorys „Demo_CI“.
Die DSC-Konfiguration
Öffnen Sie die Datei DNSServer.ps1
(ausgehend vom Stamm des lokalen Demo_CI-Repositorys, ./InfraDNS/Configs/DNSServer.ps1
).
Diese Datei enthält die DSC-Konfiguration zum Einrichten des DNS-Servers. Dies ist der vollständige Code:
configuration DNSServer
{
Import-DscResource -module 'xDnsServer','xNetworking', 'PSDesiredStateConfiguration'
Node $AllNodes.Where{$_.Role -eq 'DNSServer'}.NodeName
{
WindowsFeature DNS
{
Ensure = 'Present'
Name = 'DNS'
}
xDnsServerPrimaryZone $Node.zone
{
Ensure = 'Present'
Name = $Node.Zone
DependsOn = '[WindowsFeature]DNS'
}
foreach ($ARec in $Node.ARecords.keys) {
xDnsRecord $ARec
{
Ensure = 'Present'
Name = $ARec
Zone = $Node.Zone
Type = 'ARecord'
Target = $Node.ARecords[$ARec]
DependsOn = '[WindowsFeature]DNS'
}
}
foreach ($CName in $Node.CNameRecords.keys) {
xDnsRecord $CName
{
Ensure = 'Present'
Name = $CName
Zone = $Node.Zone
Type = 'CName'
Target = $Node.CNameRecords[$CName]
DependsOn = '[WindowsFeature]DNS'
}
}
}
}
Beachten Sie die Node
-Anweisung:
Node $AllNodes.Where{$_.Role -eq 'DNSServer'}.NodeName
Hiermit werden alle Knoten gesucht, die in den vom DevEnv.ps1
-Skript erstellten Konfigurationsdaten mit der Rolle DNSServer
definiert wurden.
Weitere Informationen zur Methode Where
finden Sie unter about_arrays.
Die Verwendung von Konfigurationsdaten zum Definieren von Knoten ist im Rahmen der Continuous Integration wichtig, weil sich Knoteninformationen je nach Umgebung wahrscheinlich ändern. Mithilfe von Konfigurationsdaten können Sie problemlos Änderungen an Knoteninformationen durchführen, ohne den Konfigurationscode ändern zu müssen.
Im ersten Ressourcenblock ruft die Konfiguration WindowsFeature auf, um sicherzustellen, dass das DNS-Feature aktiviert ist. Die folgenden Ressourcenblöcke rufen Ressourcen aus dem xDnsServer-Modul auf, um die primäre Zone und DNS-Einträge zu konfigurieren.
Beachten Sie, dass die zwei xDnsRecord
-Blöcke von foreach
-Schleifen umschlossen sind, mit denen die Arrays in den Konfigurationsdaten durchlaufen werden. Auch hier werden die Konfigurationsdaten durch das DevEnv.ps1
-Skript erstellt, das wir nachfolgend betrachten.
Konfigurationsdaten
Die DevEnv.ps1
-Datei (ausgehend vom Stamm des lokalen Demo_CI-Repositorys, ./InfraDNS/DevEnv.ps1
), gibt die umgebungsspezifischen Konfigurationsdaten in einer Hashtabelle an und übergibt diese Hashtabelle an einen Aufruf der New-DscConfigurationDataDocument
-Funktion, die in DscPipelineTools.psm
(./Assets/DscPipelineTools/DscPipelineTools.psm1
) definiert ist.
Die Datei DevEnv.ps1
enthält Folgendes:
param(
[parameter(Mandatory=$true)]
[string]
$OutputPath
)
Import-Module $PSScriptRoot\..\Assets\DscPipelineTools\DscPipelineTools.psd1 -Force
# Define Unit Test Environment
$DevEnvironment = @{
Name = 'DevEnv';
Roles = @(
@{ Role = 'DNSServer';
VMName = 'TestAgent1';
Zone = 'Contoso.com';
ARecords = @{'TFSSrv1'= '10.0.0.10';'Client'='10.0.0.15';'BuildAgent'='10.0.0.30';'TestAgent1'='10.0.0.40';'TestAgent2'='10.0.0.50'};
CNameRecords = @{'DNS' = 'TestAgent1.contoso.com'};
}
)
}
return New-DscConfigurationDataDocument -RawEnvData $DevEnvironment -OutputPath $OutputPath
Die New-DscConfigurationDataDocument
-Funktion (definiert in \Assets\DscPipelineTools\DscPipelineTools.psm1
) erstellt programmgesteuert ein Dokument mit Konfigurationsdaten aus der Hashtabelle (Knotendaten) und dem Array (Nicht-Knotendaten), die als Parameter RawEnvData
und OtherEnvData
übergeben werden.
Im vorliegenden Fall wird nur der Parameter RawEnvData
verwendet.
Das psake-Buildskript
Das in Build.ps1
(ausgehend vom Stamm des Demo_CI-Repositorys, ./InfraDNS/Build.ps1
) definierte psake-Buildskript definiert Tasks, die Bestandteil des Builds sind. Darüber hinaus wird definiert, von welchen weiteren Tasks jeder Task abhängt. Beim Aufruf des psake-Skripts wird sichergestellt, dass der angegebene Task (oder der Task Default
, sofern kein Task angegeben wird) ausgeführt wird, ebenso wie alle abhängigen Komponenten (dieser Vorgang ist rekursiv, d.h. abhängige Komponenten von abhängigen Komponenten werden ausgeführt usw.).
In diesem Beispiel ist der Default
-Task so definiert:
Task Default -depends UnitTests
Der Default
-Task weist selbst keine Implementierung auf, sondern hängt vom CompileConfigs
-Task ab.
Die Ergebniskette aus Taskabhängigkeiten stellt sicher, dass alle Tasks im Buildskript ausgeführt werden.
In diesem Beispiel wird das psake-Skript durch einen Aufruf von Invoke-PSake
in der Initiate.ps1
-Datei aufgerufen (diese befindet sich im Stamm des Demo_CI-Repositorys):
param(
[parameter()]
[ValidateSet('Build','Deploy')]
[string]
$fileName
)
#$Error.Clear()
Invoke-PSake $PSScriptRoot\InfraDNS\$fileName.ps1
<#if($Error.count)
{
Throw "$fileName script failed. Check logs for failure details."
}
#>
Wenn wir die Builddefinition für unser Beispiel erstellen, geben wir unsere Psake-Skriptdatei als fileName
Parameter für dieses Skript an.
Das Buildskript definiert die folgenden Tasks:
GenerateEnvironmentFiles
Führt DevEnv.ps1
aus, mit der die Konfigurationsdatendatei generiert wird.
InstallModules
Installiert die von der Konfiguration DNSServer.ps1
benötigten Module.
ScriptAnalysis
Ruft PSScriptAnalyzer auf.
UnitTests
Führt die Pester-Komponententests aus.
CompileConfigs
Kompiliert die Konfiguration (DNSServer.ps1
) unter Verwendung der über den GenerateEnvironmentFiles
-Task generierten Konfigurationsdaten in eine MOF-Datei.
Clean
Erstellt die für das Beispiel verwendeten Ordner und entfernt alle Testergebnisse, Konfigurationsdatendateien und Module aus vorherigen Ausführungen.
Das psake-Bereitstellungsskript
Das in Deploy.ps1
(ausgehend vom Stamm des Demo_CI-Repositorys, ./InfraDNS/Deploy.ps1
) definierte psake-Bereitstellungsskript definiert Tasks zum Bereitstellen und Ausführen der Konfiguration.
Deploy.ps1
definiert die folgenden Tasks:
DeployModules
Startet eine PowerShell-Sitzung auf TestAgent1
und installiert die Module mit den DSC-Ressourcen, die für die Konfiguration erforderlich sind.
DeployConfigs
Ruft das Cmdlet Start-DscConfiguration auf, um die Konfiguration auf TestAgent1
auszuführen.
IntegrationTests
Führt die Pester-Integrationstests aus.
AcceptanceTests
Führt die Pester-Akzeptanztests aus.
Clean
Entfernt alle in vorherigen Ausführungen installierten Module und stellt sicher, dass die Testergebnisordner vorhanden sind.
Testskripts
Die Akzeptanz-, Integrations- und Komponententests werden in Skripts im Ordner Tests
(ausgehend vom Stamm des Demo_CI-Repositorys, ./InfraDNS/Tests
) definiert, in Dateien namens DNSServer.tests.ps1
in den jeweiligen Ordnern.
Die Testskripts verwenden die Pester- und PoshSpec-Syntax.
Komponententests
Mit den Komponententests werden die DSC-Konfigurationen selbst getestet. So wird sichergestellt, dass die Konfigurationen bei ihrer Ausführung wie erwartet funktionieren. Das Skript für den Komponententest verwendet Pester.
Integrationstests
Mit den Integrationstests wird die Konfiguration des Systems getestet. So wird gewährleistet, dass das System bei der Integration in andere Komponenten wie erwartet konfiguriert wird. Diese Tests werden nach der Konfiguration mit DSC auf dem Zielknoten ausgeführt. Das Skript für die Integrationstests verwendet eine Kombination aus der Pester- und PoshSpec-Syntax.
Akzeptanztests
Über die Akzeptanztests wird getestet, ob sich das System wie erwartet verhält. Beispielsweise wird getestet, ob eine Webseite bei der Abfrage die richtigen Informationen zurückgibt. Diese Tests werden remote vom Zielknoten aus ausgeführt, um ein realistisches Szenario für den Test zu schaffen. Das Skript für die Integrationstests verwendet eine Kombination aus der Pester- und PoshSpec-Syntax.
Definieren des Builds
Nachdem wir nun unseren Code in ein Repo hochgeladen haben, und sehen wir uns an, was es tut, lassen Sie uns unsere Build definieren.
Nachfolgend werden nur die Buildschritte abgedeckt, die Sie dem Build hinzufügen. Anweisungen zum Erstellen einer Builddefinition in Azure DevOps finden Sie unter Erstellen und Warteschlange einer Builddefinition.
Erstellen Sie eine neue Builddefinition (wählen Sie die Startpipelinevorlage ) mit dem Namen "InfraDNS" aus. Fügen Sie Ihrer Builddefinition die folgenden Schritte hinzu:
- PowerShell
- Testergebnisse veröffentlichen
- Dateien kopieren
- Artefakt veröffentlichen
Nachdem Sie diese Buildschritte hinzugefügt haben, bearbeiten Sie die Eigenschaften jedes Schritts folgendermaßen:
PowerShell
- Legen Sie die TargetType-Eigenschaft auf
File Path
. - Legen Sie die FilePath-Eigenschaft auf
initiate.ps1
. - Fügen Sie
-fileName build
zur Eigenschaft Argumente hinzu.
Mit diesem Buildschritt wird die Datei initiate.ps1
ausgeführt, mit der das psake-Buildskript aufgerufen wird.
Testergebnisse veröffentlichen
- Festlegen von TestResultsFormat auf
NUnit
- Festlegen von TestResultsFiles auf
InfraDNS/Tests/Results/*.xml
- Legen Sie TestRunTitle auf
Unit
. - Stellen Sie sicher, dass sowohl SteuerungsoptionenAktiviert als auch Immer ausführen ausgewählt sind.
Mit diesem Buildschritt werden die Komponententests im Pester-Skript ausgeführt, das wir uns weiter oben angesehen haben. Die Ergebnisse werden im Ordner InfraDNS/Tests/Results/*.xml
gespeichert.
Dateien kopieren
Fügen Sie Inhalte jede der folgenden Zeilen hinzu:
initiate.ps1 **\deploy.ps1 **\Acceptance\** **\Integration\**
Legen Sie TargetFolder auf
$(Build.ArtifactStagingDirectory)\
fest.
Mit diesem Schritt werden der Build und die Testskripts in das Stagingverzeichnis kopiert, sodass im nächsten Schritt eine Veröffentlichung als Buildartefakte erfolgen kann.
Artefakt veröffentlichen
- Festlegen von TargetPath auf
$(Build.ArtifactStagingDirectory)\
- Festlegen des Artefaktenames auf
Deploy
- Legen Sie "Aktiviert" fest.
true
Aktivieren von Continuous Integration
Jetzt richten wir einen Trigger ein, mit dem das Projekt immer dann erstellt wird, wenn eine Änderung in den ci-cd-example
-Branch des Git-Repositorys eingecheckt wird.
- Klicken Sie in TFS auf die Registerkarte "Buildversion&"
- Wählen Sie die Builddefinition
DNS Infra
aus, und klicken Sie auf Bearbeiten. - Klicken Sie auf die Registerkarte "Trigger"
- Wählen Sie Continuous Integration (CI) aus, und wählen Sie in der Dropdownliste für den Branch
refs/heads/ci-cd-example
aus. - Klicken Sie auf "Speichern" und dann " OK".
Nun löst jede Änderung im Git-Repository einen automatisierten Build aus.
Erstellen der Releasedefinition
Erstellen wir jetzt eine Releasedefinition, damit das Projekt immer dann in der Entwicklungsumgebung bereitgestellt wird, wenn der Code eingecheckt wird.
Fügen Sie hierzu eine neue Releasedefinition hinzu, die der zuvor erstellten Builddefinition InfraDNS
zugeordnet ist. Stellen Sie sicher, dass Continuous Deployment ausgewählt ist, damit immer dann eine neue Release ausgelöst wird, wenn ein neuer Build erstellt wurde. (Was sind Releasepipelines?), und konfigurieren Sie sie wie folgt:
Fügen Sie der Releasedefinition die folgenden Schritte hinzu:
- PowerShell
- Testergebnisse veröffentlichen
- Testergebnisse veröffentlichen
Bearbeiten Sie die Schritte wie folgt:
PowerShell
- Festlegen des ZielPath-Felds auf
$(Build.DefinitionName)\Deploy\initiate.ps1"
- Legen Sie das Feld Argumente auf
-fileName Deploy
fest.
Erstes Veröffentlichen der Testergebnisse
- Wählen Sie für das Feld "TestResultsFormat" aus
NUnit
. - Festlegen des Felds "TestResultsFiles " auf
$(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Integration*.xml
- Festlegen des TestRunTitle-Satzes auf
Integration
- Bedingung festlegen auf
succeededOrFailed()
Zweites Veröffentlichen der Testergebnisse
- Wählen Sie für das Feld "TestResultsFormat" aus
NUnit
. - Festlegen des Felds "TestResultsFiles " auf
$(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Acceptance*.xml
- Festlegen des TestRunTitle-Satzes auf
Acceptance
- Bedingung festlegen auf
succeededOrFailed()
Überprüfen Ihrer Ergebnisse
Wenn Sie nun Änderungen in der ci-cd-example
Zweigstelle pushen, wird ein neuer Build gestartet. Bei einem erfolgreichen Abschluss des Builds wird eine neue Bereitstellung ausgelöst.
Sie können die Ergebnisse der Bereitstellung überprüfen, indem Sie einen Browser auf dem Clientcomputer öffnen und zu www.contoso.com
navigieren.
Nächste Schritte
In diesem Beispiel wird der DNS-Server TestAgent1
konfiguriert, damit die URL www.contoso.com
in TestAgent2
aufgelöst wird. Es erfolgt jedoch keine tatsächliche Bereitstellung einer Website. Das Skeleton hierfür wird im Repository unterhalb des Ordners WebApp
bereitgestellt. Sie können die bereitgestellten Stubs zum Erstellen von psake-Skripts, Pester-Tests und DSC-Konfigurationen verwenden, um Ihre eigene Website bereitzustellen.