Übung: Bereitstellen einer Web-App in Azure
In Ihrem Spielzeugunternehmen hat Ihr Websiteentwicklungsteam die neueste Version der Website in Ihr Git-Repository committet. Nun können Sie Ihre Pipeline aktualisieren, um die Website zu erstellen und auf Azure App Service bereitzustellen.
In diesem Prozess führen Sie die folgenden Aufgaben aus:
- Fügen Sie eine neue Pipelinevorlage für den Buildauftrag hinzu.
- Aktualisieren Sie die Pipeline, um den Buildauftrag aufzunehmen.
- Fügen Sie eine neue Feuerprobe hinzu.
- Aktualisieren Sie die Bereitstellungsphase, um die Anwendung bereitzustellen.
- Ausführen der Pipeline.
Hinzufügen einer Pipelinevorlage für den Buildauftrag
Fügen Sie neue Auftragsdefinition hinzu, die die erforderlichen Schritte zum Erstellen der Websiteanwendung enthält.
Öffnen Sie Visual Studio Code.
Erstellen Sie im Ordner deploy/pipeline-templates eine neue Datei mit dem Namen build.yml.
Fügen Sie der Pipelinevorlagendatei build.yml den folgenden Inhalt hinzu:
jobs: - job: Build displayName: Build application and database pool: vmImage: windows-latest steps: # Build, copy, and publish the website. - task: DotNetCoreCLI@2 displayName: Build publishable website inputs: command: 'publish' publishWebProjects: true - task: CopyFiles@2 displayName: Copy publishable website inputs: sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Website/bin' contents: '**/publish.zip' targetFolder: '$(Build.ArtifactStagingDirectory)/website' flattenFolders: true - task: PublishBuildArtifacts@1 displayName: Publish website as pipeline artifact inputs: pathToPublish: '$(Build.ArtifactStagingDirectory)/website' artifactName: 'website'
Der Auftrag führt einen Buildschritt aus, um den Quellcode der Websiteanwendung in eine kompilierte Datei umzuwandeln, die in Azure ausgeführt werden kann. Der Auftrag kopiert dann das kompilierte Artefakt in einen temporären Stagingordner und veröffentlicht es als Pipelineartefakt.
Speichern Sie die geänderte Datei.
Umbenennen der ersten Pipelinephase und Hinzufügen eines Buildauftrags
Öffnen Sie die Datei azure-pipelines.yml im Ordner deploy.
Ändern Sie die Lint-Phase. Benennen Sie sie in Build um, und fügen Sie einen Buildauftrag hinzu, der die erstellte Pipelinevorlage build.yml verwendet.
trigger: batch: true branches: include: - main pool: vmImage: ubuntu-latest stages: - stage: Build jobs: # Build the Visual Studio solution. - template: pipeline-templates/build.yml # Lint the Bicep file. - template: pipeline-templates/lint.yml # Deploy to the test environment. - template: pipeline-templates/deploy.yml parameters: environmentType: Test # Deploy to the production environment. - template: pipeline-templates/deploy.yml parameters: environmentType: Production
Speichern Sie die geänderte Datei.
Aktualisieren der Datei für die Feuerprobe
Die Websiteentwickler haben der Website einen Integritätsendpunkt hinzugefügt. Dieser Endpunkt überprüft, ob die Website online ist und die Datenbank erreichen kann. Hier fügen Sie eine neue Feuerprobe hinzu, um die Integritätsprüfung aus Ihrer Bereitstellungspipeline aufzurufen.
Öffnen Sie die Datei Website.Tests.ps1 im Ordner deploy.
Fügen Sie einen neuen Testfall hinzu, der die Integritätsprüfung aufruft. Der Testfall schlägt fehl, wenn nicht der Antwortcode 200 für einen erfolgreichen Test angegeben wird.
param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $HostName ) Describe 'Toy Website' { It 'Serves pages over HTTPS' { $request = [System.Net.WebRequest]::Create("https://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -Be 200 -Because "the website requires HTTPS" } It 'Does not serves pages over HTTP' { $request = [System.Net.WebRequest]::Create("http://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -BeGreaterOrEqual 300 -Because "HTTP is not secure" } It 'Returns a success code from the health check endpoint' { $response = Invoke-WebRequest -Uri "https://$HostName/health" -SkipHttpErrorCheck Write-Host $response.Content $response.StatusCode | Should -Be 200 -Because "the website and configuration should be healthy" } }
Speichern Sie die geänderte Datei.
Hinzufügen der Ausgabe zur Bicep-Datei
Sie möchten einen Bereitstellungsschritt hinzufügen, der Ihre Website in Azure App Service veröffentlicht, aber der Veröffentlichungsschritt erfordert den Namen der App Service-App. Hier machen Sie den App-Namen als Ausgabe aus Ihrer Bicep-Datei verfügbar.
Öffnen Sie die Datei main.bicep im Ordner deploy.
Fügen Sie am Ende des Dateiinhalts den Namen der App Service-App als Ausgabe hinzu.
output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName
Speichern Sie die geänderte Datei.
Aktualisieren der Bereitstellungsphase
Öffnen Sie im Ordner deploy/pipeline-templates die Datei deploy.yml.
Konfigurieren Sie in der Definition des Bereitstellungsauftrags der Bereitstellungsphase (in der Nähe von Zeile 59) den Auftrag für die Verwendung des von Windows gehosteten Agentpools:
- stage: Deploy_${{parameters.environmentType}} displayName: Deploy (${{parameters.environmentType}} Environment) jobs: - deployment: DeployWebsite displayName: Deploy website pool: vmImage: windows-latest variables: - group: ToyWebsite${{parameters.environmentType}} environment: ${{parameters.environmentType}} strategy:
Einige der Pipelineschritte, die Sie später für die Arbeit mit Ihrer Datenbank hinzufügen, erfordern die Ausführung des Windows-Betriebssystems. Sie können verschiedene Agentpools für verschiedene Aufträge in Ihrer Pipeline verwenden, sodass die anderen Aufträge weiterhin den Ubuntu Linux-Pipeline-Agentpool verwenden.
Fügen Sie im Schritt SaveDeploymentOutputs des Bereitstellungsauftrags eine neue Pipelinevariable mit dem Wert des App-Namens aus der Bicep-Bereitstellungsausgabe hinzu:
- bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
Beachten Sie, dass auf die Variable
appServiceAppHostName
die EigenschaftisOutput=true
angewendet wird, da diese Variable in der Feuerprobephase verwendet wird. DieappServiceAppName
Variable wird festgelegt und in derselben Pipelinephase und in demselben Auftrag verwendet. Daher ist dieisOutput=true
Einstellung nicht erforderlich.Fügen Sie am Ende des Inhalts des Bereitstellungsauftrags einen neuen Schritt hinzu, um die App in Azure App Service bereitzustellen:
steps: - checkout: self - task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) deploymentOutputs: deploymentOutputs - bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs) - task: AzureRmWebAppDeployment@4 name: DeployWebsiteApp displayName: Deploy website inputs: appType: webApp ConnectionType: AzureRM azureSubscription: ToyWebsite${{parameters.environmentType}} ResourceGroupName: $(ResourceGroupName) WebAppName: $(appServiceAppName) Package: '$(Pipeline.Workspace)/website/publish.zip'
Hinweis
Achten Sie beim Einzug der YAML-Datei darauf, dass der neue Bereitstellungsschritt auf der gleichen Ebene wie der Schritt
DeployBicepFile
eingezogen wird. Wenn Sie nicht sicher sind, kopieren Sie den gesamten Inhalt der Datei deploy.yml aus dem Beispiel im nächsten Schritt.Beachten Sie, dass Sie das Artefakt nicht explizit in die Pipelinedefinition heruntergeladen haben. Da Sie einen Bereitstellungsauftrag verwenden, lädt Azure Pipelines das Artefakt automatisch für Sie herunter.
Überprüfen des Inhalts der Datei deploy.yml und Committen Ihrer Änderungen
Vergewissern Sie sich, dass die Datei deploy.yml wie im folgenden Beispiel aussieht:
parameters: - name: environmentType type: string - name: deploymentDefaultLocation type: string default: westus3 stages: - ${{ if ne(parameters.environmentType, 'Production') }}: - stage: Validate_${{parameters.environmentType}} displayName: Validate (${{parameters.environmentType}} Environment) jobs: - job: ValidateBicepCode displayName: Validate Bicep code variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureResourceManagerTemplateDeployment@3 name: RunPreflightValidation displayName: Run preflight validation inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} location: ${{parameters.deploymentDefaultLocation}} deploymentMode: Validation resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) - ${{ if eq(parameters.environmentType, 'Production') }}: - stage: Preview_${{parameters.environmentType}} displayName: Preview (${{parameters.environmentType}} Environment) jobs: - job: PreviewAzureChanges displayName: Preview Azure changes variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureCLI@2 name: RunWhatIf displayName: Run what-if inputs: azureSubscription: ToyWebsite${{parameters.environmentType}} scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az deployment group what-if \ --resource-group $(ResourceGroupName) \ --template-file deploy/main.bicep \ --parameters environmentType=$(EnvironmentType) \ reviewApiUrl=$(ReviewApiUrl) \ reviewApiKey=$(ReviewApiKey) - stage: Deploy_${{parameters.environmentType}} displayName: Deploy (${{parameters.environmentType}} Environment) jobs: - deployment: DeployWebsite displayName: Deploy website pool: vmImage: windows-latest variables: - group: ToyWebsite${{parameters.environmentType}} environment: ${{parameters.environmentType}} strategy: runOnce: deploy: steps: - checkout: self - task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) deploymentOutputs: deploymentOutputs - bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs) - task: AzureRmWebAppDeployment@4 name: DeployWebsiteApp displayName: Deploy website inputs: appType: webApp ConnectionType: AzureRM azureSubscription: ToyWebsite${{parameters.environmentType}} ResourceGroupName: $(ResourceGroupName) WebAppName: $(appServiceAppName) Package: '$(Pipeline.Workspace)/website/publish.zip' - stage: SmokeTest_${{parameters.environmentType}} displayName: Smoke Test (${{parameters.environmentType}} Environment) jobs: - job: SmokeTest displayName: Smoke test variables: appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ] steps: - task: PowerShell@2 name: RunSmokeTests displayName: Run smoke tests inputs: targetType: inline script: | $container = New-PesterContainer ` -Path 'deploy/Website.Tests.ps1' ` -Data @{ HostName = '$(appServiceAppHostName)' } Invoke-Pester ` -Container $container ` -CI - task: PublishTestResults@2 name: PublishTestResults displayName: Publish test results condition: always() inputs: testResultsFormat: NUnit testResultsFiles: 'testResults.xml'
Speichern Sie die geänderte Datei.
Committen und pushen Sie Ihre Änderungen im Visual Studio Code-Terminal in Ihr Git-Repository, indem Sie die folgenden Befehle ausführen:
git add . git commit -m "Build and deploy website application" git push
Führen Sie die Pipeline aus.
Wechseln Sie in Ihrem Browser zu Pipelines.
Wählen Sie die letzte Ausführung Ihrer Pipeline aus.
Warten Sie, bis die Buildphase erfolgreich abgeschlossen wurde.
Die Pipeline wird angehalten, bevor sie die Phase Validieren (Testumgebung) ausführt, da die Pipeline die Berechtigung zur Verwendung der Variablengruppe benötigt, auf die die Phase verweist. Sie müssen den Zugriff der Pipeline auf die Variablengruppe genehmigen, da Sie die Pipeline zum ersten Mal in diesem Projekt ausführen. Wenn Sie die Pipeline noch mal ausführen, müssen Sie den Zugriff auf dieselbe Variablengruppe nicht genehmigen.
Wählen Sie Ansicht aus.
Wählen Sie Zulassen aus.
Wählen Sie Zulassen aus.
Die Phase Überprüfen (Testumgebung) wurde erfolgreich abgeschlossen.
Die Pipeline wird fortgesetzt, und die Phase Bereitstellen (Testumgebung) wird erfolgreich abgeschlossen. Die Pipeline führt dann die Phase Feuerprobe (Testumgebung) aus, aber die Feuerprobephase schlägt fehl.
Wählen Sie die Phase Feuerprobe (Testumgebung) aus, um das Pipelineprotokoll zu öffnen.
Wählen Sie den Schritt Run smoke tests (Feuerprobe ausführen) aus, um den zugehörigen Abschnitt des Pipelineprotokolls anzuzeigen.
Beachten Sie, dass das Pipelineprotokoll die Antwort der Integritätsprüfung enthält. Die Antwort gibt an, dass es ein Problem mit der Kommunikation der Anwendung mit der Azure SQL-Datenbank gibt. Die Datenbank wird noch nicht bereitgestellt oder konfiguriert, weshalb die Website nicht darauf zugreifen kann. In der nächsten Übung beheben Sie dieses Konfigurationsproblem.