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:

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:

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
  1. Navigieren Sie auf Ihrem Clientcomputer in einem Webbrowser zu Ihrem TFS-Server.

  2. Erstellen Sie ein neues Teamprojekt mit dem Namen Demo_CI.

    Stellen Sie sicher, dass Versionskontrolle auf Git festgelegt ist.

  3. 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.

  4. Pushen Sie mit dem folgenden Befehl den Code aus Ihrem lokalen Repository in Ihr TFS-Repository:

    git push tfs --all

  5. Das TFS-Repository wird mit dem Demo_CI-Code aufgefüllt.

  1. Navigieren Sie zu Ihrem Azure DevOps-Abonnement in einem Webbrowser.

  2. Erstellen Sie ein neues Teamprojekt mit dem Namen Demo_CI. Stellen Sie sicher, dass Versionskontrolle auf Git festgelegt ist.

  3. 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.

  4. Pushen Sie mit dem folgenden Befehl den Code aus Ihrem lokalen Repository in Ihr TFS-Repository:

    git push devops --all

  5. 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

  1. Legen Sie die TargetType-Eigenschaft auf File Path.
  2. Legen Sie die FilePath-Eigenschaft auf initiate.ps1.
  3. 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

  1. Festlegen von TestResultsFormat aufNUnit
  2. Festlegen von TestResultsFiles aufInfraDNS/Tests/Results/*.xml
  3. Legen Sie TestRunTitle auf Unit.
  4. 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

  1. Fügen Sie Inhalte jede der folgenden Zeilen hinzu:

    initiate.ps1
    **\deploy.ps1
    **\Acceptance\**
    **\Integration\**
    
  2. 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

  1. Festlegen von TargetPath auf $(Build.ArtifactStagingDirectory)\
  2. Festlegen des Artefaktenames aufDeploy
  3. 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.

  1. Klicken Sie in TFS auf die Registerkarte "Buildversion&"
  2. Wählen Sie die Builddefinition DNS Infra aus, und klicken Sie auf Bearbeiten.
  3. Klicken Sie auf die Registerkarte "Trigger"
  4. Wählen Sie Continuous Integration (CI) aus, und wählen Sie in der Dropdownliste für den Branch refs/heads/ci-cd-example aus.
  5. 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

  1. Festlegen des ZielPath-Felds auf $(Build.DefinitionName)\Deploy\initiate.ps1"
  2. Legen Sie das Feld Argumente auf -fileName Deploy fest.

Erstes Veröffentlichen der Testergebnisse

  1. Wählen Sie für das Feld "TestResultsFormat" ausNUnit.
  2. Festlegen des Felds "TestResultsFiles " auf $(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Integration*.xml
  3. Festlegen des TestRunTitle-Satzes auf Integration
  4. Bedingung festlegen aufsucceededOrFailed()

Zweites Veröffentlichen der Testergebnisse

  1. Wählen Sie für das Feld "TestResultsFormat" ausNUnit.
  2. Festlegen des Felds "TestResultsFiles " auf $(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Acceptance*.xml
  3. Festlegen des TestRunTitle-Satzes auf Acceptance
  4. Bedingung festlegen aufsucceededOrFailed()

Ü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.