Creación de una canalización de integración continua e implementación continua con DSC

Azure Pipelines | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018 | TFS 2017

Nota:

En Microsoft Team Foundation Server (TFS) 2018 y versiones anteriores, las canalizaciones de compilación y versión se denominan definiciones, las ejecuciones se denominan compilaciones, las conexiones de servicio se denominan puntos de conexión de servicio, las fases se denominan entornos y los trabajos se denominan fases.

En este ejemplo se muestra cómo crear una canalización de integración continua/implementación continua (CI/CD) mediante PowerShell, DSC y Pester.

Una vez que se construye y configura la canalización, puede usarla para implementar, configurar y probar completamente un servidor DSN y los registros host asociados. Este proceso simula la primera parte de una canalización que se usaría en un entorno de desarrollo.

Una canalización CI/CD automatizada le ayuda a actualizar software de forma más rápida y confiable, garantizando que se pruebe todo el código y que una compilación actual del código esté disponible en todo momento.

Requisitos previos

Para usar este ejemplo, debe estar familiarizado con lo siguiente:

Qué necesitará

Para compilar y ejecutar este ejemplo, necesitará un entorno con varios equipos o máquinas virtuales.

Remoto

El equipo donde llevará a cabo todo el trabajo de configuración y ejecución del ejemplo. El equipo cliente debe ser un equipo Windows que tenga instalado lo siguiente:

Azure DevOps Suscripción

Una organización de Azure DevOps. Si no tiene ninguna, puede crearla gratis. (Una Azure DevOps organización es diferente de la GitHub organización. Asíételes el mismo nombre si desea alinearlos).

TFSSrv

El equipo que hospeda el servidor TFS donde definirá la compilación y la versión. Este equipo debe tener instalado Team Foundation Server 2017.

BuildAgent

El equipo que ejecuta el agente de compilación Windows que compila el proyecto. Este equipo debe tener instalado y en ejecución un agente de compilación Windows. Consulte Implementación de un agente en Windows para instrucciones sobre cómo instalar y ejecutar un agente de compilación Windows.

También necesita instalar los módulos xDnsServer y xNetworking de DSC en este equipo.

TestAgent1

El equipo que la configuración de DSC configuró como servidor DNS en este ejemplo. El equipo debe ejecutar Windows Server 2016.

TestAgent2

El equipo que hospeda el sitio web que se configura en este ejemplo. El equipo debe ejecutar Windows Server 2016.

Adición del código a un repositorio

Comenzaremos creando un repositorio git e importando el código desde el repositorio local en el equipo cliente. Si todavía no clonó el repositorio Demo_CI en el equipo cliente, ejecute el siguiente comando de git para hacerlo ahora:

git clone https://github.com/PowerShell/Demo_CI
  1. En el equipo cliente, vaya al servidor TFS en un explorador web.

  2. Cree un proyecto de equipo denominado Demo_CI.

    Asegúrese de que el control de versiones esté establecido en Git.

  3. En el equipo cliente, agregue un elemento remoto en el repositorio que acaba de crear en TFS con el comando siguiente:

    git remote add tfs <YourTFSRepoURL>

    Donde <YourTFSRepoURL> es la dirección URL de clonación al repositorio TFS que creó que en el paso anterior.

    Si no sabe dónde encontrar esta dirección URL, consulte cómo clonar un repositorio Git existente.

  4. Inserte el código del repositorio local en el repositorio TFS con el comando siguiente:

    git push tfs --all

  5. El repositorio TFS se rellenará con el código de Demo_CI.

  1. Vaya a la suscripción Azure DevOps en un explorador web.

  2. Cree un proyecto de equipo denominado Demo_CI. Asegúrese de que el control de versiones esté establecido en Git.

  3. En el equipo cliente, agregue un remoto al repositorio que acaba de crear con el siguiente comando:

    git remote add devops <YourDevOpsRepoURL>

    Donde <YourDevOpsRepoURL> es la dirección URL de clonación Azure DevOps repositorio que creó en el paso anterior.

    Si no sabe dónde encontrar esta dirección URL, consulte cómo clonar un repositorio Git existente.

  4. Inserte el código del repositorio local en el repositorio TFS con el comando siguiente:

    git push devops --all

  5. El Azure DevOps se rellenará con el Demo_CI código.

Nota:

En este ejemplo se usa el código que se encuentra en la rama ci-cd-example del repositorio GIT. Asegúrese de especificar esta rama como la rama predeterminada en el proyecto y para los desencadenadores de CI/CD que cree.

Descripción del código

Antes de crear las canalizaciones de compilación e implementación, observemos parte del código para comprender qué es lo que pasa. En el equipo cliente, abra el editor de texto de su preferencia y vaya a la raíz del repositorio Git de Demo_CI.

La configuración de DSC

Abra el archivo DNSServer.ps1 (en la raíz del repositorio Demo_CI local, ./InfraDNS/Configs/DNSServer.ps1).

Este archivo contiene la configuración de DSC que configura el servidor DNS. A continuación se muestra en su totalidad:

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'
            }
        }
    }
}

Observe la instrucción Node:

Node $AllNodes.Where{$_.Role -eq 'DNSServer'}.NodeName

Esta instrucción encuentra cualquier nodo que se haya definido con un rol de DNSServer en los DNSServer, creados por el script DevEnv.ps1.

Puede obtener más información sobre el método Where en Where.

Usar datos de configuración para definir nodos es importantes cuando se hace CI, porque es probable que la información de los nodos cambie entre los entornos y usar los datos de configuración le permite hacer cambios con facilidad en la información de los nodos sin cambiar el código de configuración.

En el primer bloque de recursos, la configuración llama a WindowsFeature para asegurarse de que la característica DNS está habilitada. Los bloques de recursos siguientes llaman a recursos del módulo xDnsServer para configurar la zona principal y los registros DNS.

Tenga en cuenta que los dos bloques xDnsRecord están encapsulados en bucles foreach que iteran a través de matrices en los datos de configuración. Como ya indicamos, el script DevEnv.ps1 crea los datos de configuración, los que analizaremos a continuación.

Datos de configuración

El archivo DevEnv.ps1 (de la raíz del repositorio Demo_CI local, ./InfraDNS/DevEnv.ps1) especifica los datos de configuración específicos del entorno en una tabla hash y, luego, pasa esa tabla hash a una llamada a la función New-DscConfigurationDataDocument, que se define en DscPipelineTools.psm (./Assets/DscPipelineTools/DscPipelineTools.psm1).

El archivo DevEnv.ps1:

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

La función New-DscConfigurationDataDocument (que se define en \Assets\DscPipelineTools\DscPipelineTools.psm1) crea mediante programación un documento de datos de configuración desde la tabla hash (datos de nodo) y la matriz (datos no de nodo) que se pasan como los parámetros RawEnvData y OtherEnvData.

En el caso en cuestión, solo se usa el parámetro RawEnvData.

El script de compilación psake

El script de compilación psake definido en (de la raíz del repositorio Demo_CI, ./InfraDNS/Build.ps1) define las tareas que forman parte de la compilación. También define las otras tareas de las que depende cada tarea. Cuando se invoca, el script psake garantiza que la tarea especificada (o la tarea llamada Default, si no se especifica ninguna) se ejecute y que también se ejecuten todas las dependencias (esta acción es recursiva, por lo que se ejecutan las dependencias de las dependencias, y así sucesivamente).

En este ejemplo, la tarea Default se define de la siguiente manera:

Task Default -depends UnitTests

La tarea Default no tiene implementación, pero sí tiene una dependencia de la tarea CompileConfigs. La cadena de dependencias de tareas resultante asegura que se ejecuten todas las tareas del script de compilación.

En este ejemplo, el script psake se invoca con una llamada a Invoke-PSake en el archivo Initiate.ps1 (ubicado en la raíz del repositorio Demo_CI):

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."
}
#>

Cuando creamos la definición de compilación para nuestro ejemplo, proporcionaremos nuestro archivo de script psake como fileName parámetro para este script.

El script de compilación define las siguientes tareas:

GenerateEnvironmentFiles

Ejecuta DevEnv.ps1, que genera el archivo de datos de configuración.

InstallModules

Instala los módulos que requiere la configuración DNSServer.ps1.

ScriptAnalysis

Llama al PSScriptAnalyzer.

UnitTests

Ejecuta las pruebas unitarias Pester.

CompileConfigs

Compila la configuración (DNSServer.ps1) en un archivo MOF, usando los datos de configuración que genera la tarea GenerateEnvironmentFiles.

Clean

Crea la carpeta que se usa en el ejemplo y quita los resultados de pruebas, los archivos de datos de configuración y los módulos de ejecuciones anteriores.

El script de implementación psake

El script de implementación psake definido en (de la raíz del repositorio Demo_CI, ./InfraDNS/Deploy.ps1) define las tareas que implementan y ejecutan la configuración.

Deploy.ps1 define las tareas siguientes:

DeployModules

Inicia una sesión de PowerShell en TestAgent1 e instala los módulos que contienen los recursos de DSC que se requieren en la configuración.

DeployConfigs

Llama al cmdlet Start-DscConfiguration para ejecutar la configuración en .

IntegrationTests

Ejecuta las pruebas de integración Pester.

AcceptanceTests

Ejecuta las pruebas de aceptación Pester.

Clean

Quita los módulos que se instalaron en ejecuciones anteriores y se asegura de que exista la carpeta de resultados de pruebas.

Scripts de prueba

Las pruebas unitarias, de integración y de aceptación están definidas en la carpeta Tests (de la raíz del repositorio Demo_CI, ./InfraDNS/Tests), cada una en archivos llamados DNSServer.tests.ps1 en sus respectivas carpetas.

Los scripts de prueba usan la sintaxis de Pester y PoshSpec.

Pruebas unitarias

Las pruebas unitarias prueban las configuraciones de DSC para asegurarse de que hagan lo esperado cuando se ejecuten. El script de prueba unitaria usa Pester.

Pruebas de integración

Las pruebas de integración prueban la configuración del sistema para garantizar que cuando se integre con otros componentes, el sistema esté configurado según lo previsto. Estas pruebas se ejecutan en el nodo de destino una vez que se configura con DSC. El script de prueba de aceptación usa una combinación de sintaxis de Pester y PoshSpec.

Pruebas de aceptación

Las pruebas de aceptación prueban el sistema para asegurarse de que se comporte según lo previsto. Por ejemplo, se hacen pruebas para asegurarse de que una página web devuelve la información correcta cuando se consulta. Estas pruebas se ejecutan de manera remota desde el nodo de destino para probar escenarios reales. El script de prueba de aceptación usa una combinación de sintaxis de Pester y PoshSpec.

Definición de la compilación

Ahora que hemos cargado el código en un repositorio y hemos visto lo que hace, vamos a definir la compilación.

En esta sección, solo analizaremos los pasos de compilación que agregará a la misma. Para obtener instrucciones sobre cómo crear una definición de compilación en Azure DevOps, vea Crear y poner en cola una definición de compilación.

Cree una nueva definición de compilación (seleccione la plantilla Canalización de inicio) denominada "InfraDNS". Agregue los pasos siguientes a la definición de compilación:

  • PowerShell
  • Publicación de los resultados de las pruebas
  • Copiar archivos
  • Publish Artifact (Publicar artefacto)

Después de agregar estos pasos de compilación, edite las propiedades de cada paso de la siguiente manera:

PowerShell

  1. Establezca la propiedad targetType en .
  2. Establezca la propiedad filePath en .
  3. Agregue -fileName build a la propiedad -fileName build (Argumentos).

Este paso de compilación ejecuta el archivo initiate.ps1, que llama al script de compilación psake.

Publicación de los resultados de las pruebas

  1. Establezca TestResultsFormat en
  2. Establezca TestResultsFiles en
  3. Establezca TestRunTitle en .
  4. Asegúrese de que estén seleccionadas las opciones Control OptionsEnabled y Always run.

Este paso de compilación ejecuta las pruebas unitarias en el script Pester que analizamos anteriormente y almacena los resultados en la carpeta InfraDNS/Tests/Results/*.xml.

Copiar archivos

  1. Agregue cada una de las siguientes líneas a Contents (Contenido):

    initiate.ps1
    **\deploy.ps1
    **\Acceptance\**
    **\Integration\**
    
  2. Establezca TargetFolder en

Este paso copia los scripts de compilación y prueba en el directorio de almacenamiento provisional para que se puedan publicar como artefactos de compilación en el paso siguiente.

Publish Artifact (Publicar artefacto)

  1. Establezca TargetPath en
  2. Establezca ArtifactName en
  3. Establezca Habilitado en .

Habilitar la integración continua

Ahora configuraremos un desencadenador que hará que el proyecto se compile cada vez que se confirme un cambio en la rama ci-cd-example del repositorio Git.

  1. En TFS, haga clic en la pestaña Build Release (Versión de compilación).
  2. Seleccione la definición de compilación DNS Infra y haga clic en DNS Infra
  3. Haga clic en la pestaña Desencadenadores.
  4. Seleccione Integración continua (CI) y seleccione en la lista desplegable de ramas
  5. Haga clic en Guardar y, luego, en Aceptar

Ahora cualquier cambio en el repositorio git desencadena una compilación automatizada.

Creación de la definición de versión

Vamos a crear una definición de versión para que el proyecto se implemente en el entorno de desarrollo con cada confirmación de código.

Para esto, agregue una nueva definición de versión asociada con la definición de compilación InfraDNS que creó anteriormente. Asegúrese de seleccionar Implementación continua para que una nueva versión se desencadene cada vez que se complete una nueva compilación. (¿Qué son las canalizaciones de versión?) y configúrelo como se muestra a continuación:

Agregue los pasos siguientes a la definición de versión:

  • PowerShell
  • Publicación de los resultados de las pruebas
  • Publicación de los resultados de las pruebas

Edite los pasos de la siguiente manera:

PowerShell

  1. Establezca el campo TargetPath en
  2. Establezca el campo Arguments en

Publicación de los resultados de primera prueba

  1. Seleccione NUnit para el campo NUnit
  2. Establezca el campo TestResultsFiles en
  3. Establezca TestRunTitle en
  4. Establezca Condición en

Publicación de los resultados de la segunda prueba

  1. Seleccione NUnit para el campo NUnit
  2. Establezca el campo TestResultsFiles en
  3. Establezca TestRunTitle en
  4. Establezca Condición en

Comprobación de los resultados

Ahora, cada vez que se insertan cambios en la ci-cd-example rama, se iniciará una nueva compilación. Si la compilación se completa correctamente, se desencadena una nueva implementación.

Para comprobar el resultado de la implementación, abra un explorador en la máquina cliente y vaya a www.contoso.com.

Pasos siguientes

En este ejemplo se configura el servidor DNS TestAgent1 para que la dirección URL www.contoso.com se resuelva en TestAgent2, pero en realidad no implemente un sitio web. La estructura para hacerlo se proporciona en el repositorio que se encuentra en la carpeta WebApp. Puede usar los códigos auxiliares que se proporcionan para crear scripts psake, pruebas Pester y configuraciones de DSC a fin de implementar su propio sitio web.