【DevOps】使用Azure DevOps为Azure静态网站配置多阶段部署

2020-03-13T20:50:03+08:00 | 7分钟阅读 | 更新于 2020-03-13T20:50:03+08:00

Macro Zhao

【DevOps】使用Azure DevOps为Azure静态网站配置多阶段部署

推荐超级课程:

@TOC

Azure静态网站可以自动配置Azure DevOps Pipeline来构建和部署您的应用程序。这是快速启动和运行您的应用程序的好方法。
对于生产应用程序,通常首先将应用程序部署到暂存环境,然后再部署到生产环境。在本文中,我们将引导您如何配置一个健壮的Azure DevOpsPipeline,该Pipeline将执行以下操作:

  • 构建应用程序、Azure Functions API和Playwright测试
  • 将应用程序部署到暂存环境
  • 自动使用Playwright测试验证暂存应用程序
  • 等待手动批准
  • 将您的应用程序部署到生产环境

示例应用程序

我们将使用一个.NET6全栈应用程序。源代码可以在CSDN下载。

  • 前端:Blazor WebAssembly
  • 后端:Azure Functions
  • 测试:Playwright

要跟随本教程,请将存储库导入您的Azure DevOps项目。
您可以使用类似的步骤来处理Node.js等其他应用程序。

创建静态网站和部署Pipeline


Azure静态网站生成Azure DevOpsPipeline 的功能,以部署您的应用程序。

您可以在一步中创建静态网站和部署Pipeline。

  1. 在Azure门户中,搜索并创建一个新的静态网站。

  2. 在创建过程中,选择“Azure DevOps”作为部署源,并选择包含应用程序的DevOps存储库和分支。

    168908827-22dd512a-cec7-4e27-aca4-993428f2a03e

  3. 在构建预设中,选择“Blazor”。这将自动填充应用程序和API文件夹位置。

    168909032-b817a631-6e74-4739-8c4a-1ff480f955a3

创建应用程序时,将在存储库中创建一个新的PipelineYAML文件。它将自动运行。构建和部署应用程序需要几分钟时间。

168909383-0d275e2f-15ae-40e8-aa2d-56a208f7a6f4

在浏览器或本地编辑器中打开PipelineYAML文件,查看其内容。它包含一个AzureStaticWebApp任务,可以自动构建和部署应用程序。

创建AzurePipeline环境


AzurePipeline允许您定义环境。环境对于添加手动批准到您的Pipeline非常有用。
我们将创建两个环境——暂存和生产。它们将对应我们部署到的两个Azure静态网站环境。
要创建Pipeline环境,请在“Pipeline”下选择“环境”。

168909775-61e3e243-c298-4fcf-a5ba-8af0a94a7a5d


创建一个名为“Staging”的环境。因为您不需要对这个阶段的 manual approval,所以不需要配置其他内容。
接下来,创建一个名为“Production”的新环境。因为我们希望在部署到生产之前需要手动批准,所以您可以配置环境以要求手动批准。

  1. 在环境的“Approvals and checks”中,选择“Approvals”。

  2. 添加自己作为批准者并创建批准策略。

    168910157-d32f767f-c608-4047-b0f5-66b7e4beb2f2

为Azure静态网站环境添加密码保护


Azure静态网站提供了预览环境,允许您在部署到生产之前测试您的应用程序。最初,预览环境仅适用于GitHub的拉取请求。最近,静态网站引入了任意定义命名预览环境 的功能。例如,我们可以创建一个名为“Staging”的环境。
预览环境默认是公开的。这对于开源项目来说很棒,但有时我们希望保护它们免受公共访问。
您可以为您的应用程序的预览环境添加密码保护。

  1. 在Azure门户中,打开您的静态网站。
  2. 在“配置”中,选择“常规设置”选项卡。
  3. 选择“仅保护暂存环境”并输入密码。

    168910485-02aacade-3961-41fd-aa9c-54cd14df5aab


    您还可以选择保护所有环境。但在这个应用程序中,我们希望生产环境对公众开放。

配置多阶段 Pipeline


现在我们已经配置了Azure Pipeline环境和密码保护,我们可以配置 Pipeline了。
在浏览器或本地编辑器中打开 PipelineYAML文件。用 azure-pipelines.yml 的内容替换它。
我们将逐一介绍 Pipeline的不同部分。它有三个主要阶段:构建、部署到暂存和生产。

第1阶段:构建应用程序

trigger:
- main

pool:
  vmImage: ubuntu-latest

stages:

- stage: Build

  jobs:
  - job: build
    displayName: Build app

    steps:
    
    - task: UseDotNet@2
      displayName: Install .NET SDK
      inputs:
        packageType: 'sdk'
        version: '6.0.x'
        
    - script: |    
        dotnet publish -c Release -o "$(Build.ArtifactStagingDirectory)/frontend"
      displayName: Build Blazor frontend
      workingDirectory: $(System.DefaultWorkingDirectory)/Client
      
    - script: |  
        dotnet publish -c Release -o "$(Build.ArtifactStagingDirectory)/api"
      displayName: Build Azure Functions API
      workingDirectory: $(System.DefaultWorkingDirectory)/Api
      
    - script: |
        dotnet build -c Release -o "$(Build.ArtifactStagingDirectory)/tests"
      displayName: Build Playwright tests
      workingDirectory: $(System.DefaultWorkingDirectory)/PlaywrightTests

    - task: PublishBuildArtifacts@1
      displayName: Publish artifacts
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)'
        ArtifactName: 'drop'
        publishLocation: 'Container'

Pipeline在main分支有任何更改时触发。如果您的应用程序使用不同的分支,您可以更改它。
Pipeline然后构建应用程序、Azure Functions API和Playwright测试。然后输出工件。所有环境的相同构建工件被部署,确保您在其他环境中测试的应用程序与部署到生产的相同。

第2阶段:部署到暂存并运行Playwright测试

- stage: deploy_staging
  displayName: Deploy to staging

  jobs:
    - deployment: deploy
      displayName: Deploy and test
      environment: Staging
      variables:
      # Change the variable group name to match the one in the generated pipeline
      - group: Azure-Static-Web-Apps-calm-coast-0df39b910-variable-group
      strategy:
        runOnce:
          deploy:
            steps:

            - download: none
            - checkout: none

            - task: DownloadBuildArtifacts@1
              displayName: Download artifacts
              inputs:
                buildType: current
                downloadType: single
                artifactName: drop
                downloadPath: $(System.ArtifactsDirectory)

            - task: AzureStaticWebApp@0
              displayName: Deploy to staging environment
              inputs:
                app_location: frontend/wwwroot
                api_location: api
                skip_app_build: true
                skip_api_build: true
                verbose: true
                azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_CALM_COAST_0DF39B910)
                deployment_environment: staging
                workingDirectory: $(System.ArtifactsDirectory)/drop

            - task: UseDotNet@2
              displayName: Install .NET SDK
              inputs:
                packageType: 'sdk'
                version: '6.0.x'

            - script: |
                chmod -R a+x $(System.ArtifactsDirectory)/drop/tests
                sudo --preserve-env=PLAYWRIGHT_BROWSERS_PATH pwsh $(System.ArtifactsDirectory)/drop/tests/playwright.ps1 install --with-deps chromium
                dotnet test $(System.ArtifactsDirectory)/drop/tests/PlaywrightTests.dll --logger trx
              displayName: Run Playwright tests on staging app
              env:
                PLAYWRIGHT_BROWSERS_PATH: $(Build.SourcesDirectory)/browsers
                LOGIN_PASSWORD: $(LOGIN_PASSWORD)
            
            - task: PublishTestResults@2
              condition: succeededOrFailed()
              inputs:
                testRunner: VSTest
                testResultsFiles: '**/*.trx'
                

第3阶段:部署到生产


- stage: deploy_production
  displayName: Deploy to production

  jobs:
  - deployment: deploy
    displayName: Deploy
    environment: Production
    variables:
    # Change the variable group name to match the one in the generated pipeline
    - group: Azure-Static-Web-Apps-calm-coast-0df39b910-variable-group
    strategy:
      runOnce:
        deploy:
          steps:
          - download: none
          - checkout: none

          - task: DownloadBuildArtifacts@1
            displayName: Download artifacts
            inputs:
              buildType: current
              downloadType: single
              artifactName: drop
              downloadPath: $(System.ArtifactsDirectory)

          - task: AzureStaticWebApp@0
            displayName: Deploy to production environment
            inputs:
              app_location: frontend/wwwroot
              api_location: api
              skip_app_build: true
              skip_api_build: true
              verbose: true
              azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_CALM_COAST_0DF39B910)
              workingDirectory: $(System.ArtifactsDirectory)/drop

这个阶段引用了Azure Pipeline中的“生产”环境。因为我们配置了这个环境以要求批准,所以这将触发在运行此阶段之前的手动批准步骤。
与上一个阶段类似,这个阶段下载工件并使用AzureStaticWebApp任务进行部署。这次,没有设置deployment_environment,因为我们希望部署到静态网站的生产环境。

运行多阶段 Pipeline


现在 Pipeline已经设置好,您可以通过保存文件来运行它。如果您在本地编辑了它,不要忘记将其推送到您的Azure DevOps存储库。
在“部署到暂存”阶段运行后,您应该会看到Playwright测试已经运行以验证暂存环境,并且结果已经发布。

168911185-3eea2e66-0aaf-4a86-af65-956618abc6d3


Pipeline运行被暂停,因为“部署到生产”阶段需要批准。

168912243-6450a5fa-6e95-4ac8-8f65-b1a7bc8d3e6b


当您批准 Pipeline时, Pipeline将再次运行。
批准后,应用程序将部署到生产环境。

深入了解


在我们结束本文之前,我们想更深入地探讨静态网站环境和Playwright测试。

Azure静态网站环境

要查看您创建的环境,请在Azure门户中点击您的静态网站的“环境”选项卡。您应该会看到生产环境和暂存环境。

168911293-571d1057-c36b-4ff2-88df-3cdf20154318

Playwright测试

Playwright测试位于PlaywrightTests项目中,它们是用C#编写的。
Playwright 可以用来使用真实浏览器自动化测试Web应用程序。示例中的测试使用Chrome(Chromium),但Playwright也支持Firefox、WebKit和Microsoft Edge。
这是一个C#中的Playwright测试示例。它导航浏览器到应用程序的主页,点击“获取数据”链接,并确认数据从后端API获取并成功渲染。


[Test]
public async Task ShouldLoadWeather()
{
    await using var browser = await Playwright.Chromium.LaunchAsync();
    var page = await browser.NewPageAsync();
    await GoToHomePage(page);

    await page.ClickAsync("a[href='fetchdata']");

    var h1 = await page.QuerySelectorAsync("div#app main h1");
    var h1Text = h1 == null ? "" : await h1.TextContentAsync();
    Assert.AreEqual("Weather forecast", h1Text);

    var rowsSelector = "div#app main table tbody tr";
    // wait for table to have rows
    await page.WaitForFunctionAsync($"document.querySelectorAll('{rowsSelector}').length");
    var rows = await page.QuerySelectorAllAsync(rowsSelector);
    Assert.AreEqual(5, rows.Count);
}

测试使用AZURESTATICWEBAPP_STATIC_WEB_APP_URL环境变量来确定要测试的应用程序URL。在Azure Pipeline中,这个变量在成功部署后由Azure静态网站任务设置。因为测试在部署到暂存环境后运行,所以变量包含暂存静态网站环境的URL。

测试的另一个有趣方面是以下代码,用于导航到主页:

private async Task GoToHomePage(IPage page)
{
    await page.GotoAsync($"{siteBaseUrl}/");
    var homePageLocatorTask = page.Locator("text=Hello, world!").WaitForAsync();
    var passwordPageLocatorTask = page.Locator("text=Password protected").WaitForAsync();
    var isPasswordPage = (await Task.WhenAny(homePageLocatorTask, passwordPageLocatorTask)) == passwordPageLocatorTask;

    if (isPasswordPage)
    {
        System.Console.WriteLine("Found password page, logging in...");
        var passwordInput = await page.QuerySelectorAsync("input[type=password]");
        if (passwordInput == null)
        {
            throw new Exception("Could not find password input");
        }
        await passwordInput.TypeAsync(sitePassword);
        await page.ClickAsync("button");
        await page.Locator("text=Hello, world!").WaitForAsync();
    }
}

这段代码在浏览器会话未认证时自动登录到暂存环境。在运行时,sitePassword变量设置为LOGIN_PASSWORD秘密变量的值。

总结


随着命名预览环境和自动 Pipeline生成等功能的增加,现在可以配置复杂、健壮的 Pipeline,从Azure DevOps构建和部署到Azure静态网站。

© 2011 - 2025 Macro Zhao的分享站

关于我

如遇到加载502错误,请尝试刷新😄

Hi,欢迎访问 Macro Zhao 的博客。Macro Zhao(或 Macro)是我在互联网上经常使用的名字。

我是一个热衷于技术探索和分享的IT工程师,在这里我会记录分享一些关于技术、工作和生活上的事情。

我的CSDN博客:
https://macro-zhao.blog.csdn.net/

欢迎你通过评论或者邮件与我交流。
Mail Me

推荐好玩(You'll Like)
  • AI 动·画
    • 这是一款有趣·免费的能让您画的画中的角色动起来的AI工具。
    • 支持几十种动作生成。
我的项目(My Projects)
  • 爱学习网

  • 小乙日语App

    • 这是一个帮助日语学习者学习日语的App。
      (当然初衷也是为了自用😄)
    • 界面干净,简洁,漂亮!
    • 其中包含 N1 + N2 的全部单词和语法。
    • 不需注册,更不需要订阅!完全免费!
  • 小乙日文阅读器

    • 词汇不够?照样能读日语名著!
    • 越读积累越多,积跬步致千里!
    • 哪里不会点哪里!妈妈再也不担心我读不了原版读物了!
赞助我(Sponsor Me)

如果你喜欢我的作品或者发现它们对你有所帮助,可以考虑给我买一杯咖啡 ☕️。这将激励我在未来创作和分享更多的项目和技术。🦾

👉 请我喝一杯咖啡

If you like my works or find them helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and share more projects in the future. 🦾

👉 Buy me a coffee