练习 - 在 Azure Pipelines 中运行负载测试

已完成

在本部分中,你将在发布管道中运行你创建的测试计划。 测试计划使用 Apache JMeter 来运行负载测试。

下面是运行测试的方式:

  • 提取并签出实现测试的 Git 分支。
  • 修改管道以安装 JMeter,运行测试计划,将结果转换为 JUnit,然后将结果发布到 Azure Pipelines。
  • 将分支推送到 GitHub,查看 Azure Pipelines 中运行的测试,然后检查结果。

从 GitHub 中提取分支

在本部分中,你将从 GitHub 提取 jmeter 分支,签出或切换到该分支。

该分支包含在之前的模块中使用过的 Space Game 项目。 它还包含要从其开始的 Azure Pipelines 配置。

  1. 在 Visual Studio Code 中打开集成终端。

  2. 要从 Microsoft 的存储库中下载名为 jmeter 的分支,并切换到该分支,请运行以下 git fetchgit checkout 命令:

    git fetch upstream jmeter
    git checkout -B jmeter upstream/jmeter
    

    回想一下,“upstream”指的是 Microsoft GitHub 存储库。 项目的 Git 配置能够识别 upstream 远程库,因为当你从 Microsoft 的存储库中创建该项目的分支并在本地克隆它时,就建立了这种关系。

    稍后,你会将此分支推送到 GitHub 存储库(称作 origin)。

  3. (可选)在 Visual Studio Code 中,打开 azure-pipelines.yml 文件。 检查初始配置。

    该配置类似于在此学习路径前面的模块中创建的配置。 它只生成应用程序的发布配置。 为了简洁起见,它省略了在之前的模块中设置的触发器、手动审批和测试。

    备注

    可以使用更可靠的配置来指定参与生成过程的分支。 例如,为了帮助验证代码质量,你可以在每次对任何分支推送更改时运行单元测试。 你还可以将应用程序部署到执行更全面测试的环境。 但仅当你有拉取请求、候选发布或将代码合并到主分支时,才能执行此部署。

    有关详细信息,请参阅使用 Git 和 GitHub 在生成管道中实现代码工作流生成管道触发器

  4. (可选)在 Visual Studio Code 中,可签出 JMeter 测试计划文件 (LoadTest.jmx) 和 XLST 转换 (JMeter2JUnit.xsl)。 XLST 文件将 JMeter 输出转换为 JUnit,以便 Azure Pipelines 可以可视化结果。

将变量添加到 Azure Pipelines

团队的原始测试计划为在“过渡”环境中运行的 Space Game 网站的主机名提供硬编码值。

为了使测试计划更灵活,你的版本会使用 JMeter 属性。 将属性视为可从命令行设置的变量。

下面是 hostname 变量在 JMeter 中的定义方式:

Screenshot of setting the hostname variable in Apache JMeter.

下面是 hostname 变量使用 函数读取 hostname 变量的方式。

Screenshot for reading the hostname variable in Apache JMeter.

对应的测试计划文件 (LoadTest.jmx) 指定此变量,并使用它来设置主机名。

从命令行运行 JMeter 时,需使用 -J 参数来设置 hostname 属性。 下面是一个示例:

apache-jmeter-5.4.3/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=tailspin-space-game-web-staging-1234.azurewebsites.net

在此处设置 Azure Pipelines 中的 STAGING_HOSTNAME 变量。 此变量指向“过渡”环境中在应用服务上运行的站点的主机名。 还可将 jmeterVersion 设置为指定要安装的 JMeter 的版本。

代理运行时,这些变量将作为环境变量自动导出到代理,因此,你的管道配置可通过以下方式运行 JMeter:

apache-jmeter-5.4.3/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=$(STAGING_HOSTNAME)

在更新管道配置之前,先添加管道变量。 为此,请执行以下操作:

  1. 在 Azure DevOps 中,转到“Space Game - web - Nonfunctional tests”项目。

  2. 在“Pipelines”下,选择“Library”。

  3. 选择“发布”变量组。

  4. 在“Variables”下,选择“+ Add”。

  5. 输入 STAGING_HOSTNAME 作为变量名称。 输入与“过渡”环境相对应的应用服务实例的 URL(如 tailspin-space-game-web-staging-1234.azurewebsites.net)作为它的值。

    重要

    值中不要包含 http://https:// 协议前缀。 JMeter 在测试运行时提供协议。

  6. 添加另一个名为 jmeterVersion 的变量。 指定 5.4.3 作为它的值。

    备注

    这是上次用于测试此模块的 JMeter 版本。 获取最新版本,请参阅下载 Apache JMeter

  7. 在页面顶部附近选择“保存”,将变量保存到管道中。

    变量组如下图所示:

    Screenshot of Azure Pipelines, showing the variable group. The group contains five variables.

修改管道配置

在本部分中,你将管道修改为能够在“过渡”阶段运行负载测试。

  1. 在 Visual Studio Code 中,打开 azure-pipelines.yml 文件。 然后,按如下所示修改文件。

    提示

    可替换整个文件,也可仅更新突出显示的部分。

    trigger:
    - '*'
    
    variables:
      buildConfiguration: 'Release'
    
    stages:
    - stage: 'Build'
      displayName: 'Build the web application'
      jobs:
      - job: 'Build'
        displayName: 'Build job'
        pool:
          vmImage: 'ubuntu-20.04'
          demands:
          - npm
    
        variables:
          wwwrootDir: 'Tailspin.SpaceGame.Web/wwwroot'
          dotnetSdkVersion: '6.x'
    
        steps:
        - task: UseDotNet@2
          displayName: 'Use .NET SDK $(dotnetSdkVersion)'
          inputs:
            version: '$(dotnetSdkVersion)'
    
        - task: Npm@1
          displayName: 'Run npm install'
          inputs:
            verbose: false
    
        - script: './node_modules/.bin/node-sass $(wwwrootDir) --output $(wwwrootDir)'
          displayName: 'Compile Sass assets'
    
        - task: gulp@1
          displayName: 'Run gulp tasks'
    
        - script: 'echo "$(Build.DefinitionName), $(Build.BuildId), $(Build.BuildNumber)" > buildinfo.txt'
          displayName: 'Write build info'
          workingDirectory: $(wwwrootDir)
    
        - task: DotNetCoreCLI@2
          displayName: 'Restore project dependencies'
          inputs:
            command: 'restore'
            projects: '**/*.csproj'
    
        - task: DotNetCoreCLI@2
          displayName: 'Build the project - $(buildConfiguration)'
          inputs:
            command: 'build'
            arguments: '--no-restore --configuration $(buildConfiguration)'
            projects: '**/*.csproj'
    
        - task: DotNetCoreCLI@2
          displayName: 'Publish the project - $(buildConfiguration)'
          inputs:
            command: 'publish'
            projects: '**/*.csproj'
            publishWebProjects: false
            arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
            zipAfterPublish: true
    
        - publish: '$(Build.ArtifactStagingDirectory)'
          artifact: drop
    
    - stage: 'Dev'
      displayName: 'Deploy to the dev environment'
      dependsOn: Build
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: dev
        variables:
        - group: Release
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameDev)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
    
    - stage: 'Test'
      displayName: 'Deploy to the test environment'
      dependsOn: Dev
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: test
        variables:
        - group: 'Release'
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameTest)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
    
    - stage: 'Staging'
      displayName: 'Deploy to the staging environment'
      dependsOn: Test
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: staging
        variables:
        - group: 'Release'
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameStaging)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
      - job: RunLoadTests
        dependsOn: Deploy
        displayName: 'Run load tests'
        pool:
          vmImage: 'ubuntu-20.04'
        variables:
        - group: Release
        steps:
        - script: |
            wget -c archive.apache.org/dist/jmeter/binaries/apache-jmeter-$(jmeterVersion).tgz
            tar -xzf apache-jmeter-$(jmeterVersion).tgz
          displayName: 'Install Apache JMeter'
        - script: apache-jmeter-$(jmeterVersion)/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=$(STAGING_HOSTNAME)
          displayName: 'Run Load tests'
        - script: |
            sudo apt-get update
            sudo apt-get install xsltproc
            xsltproc JMeter2JUnit.xsl Results.xml > JUnit.xml
          displayName: 'Transform JMeter output to JUnit'
        - task: PublishTestResults@2
          inputs:
            testResultsFormat: JUnit
            testResultsFiles: JUnit.xml
    

    下面对这些更改进行了概述:

    • RunLoadTests 作业从 Linux 代理执行负载测试。
    • RunLoadTests 作业取决于 Deploy 作业,以确保作业按正确的顺序运行。 在运行负载测试之前,需要将网站部署到应用服务。 如果未指定此依赖项,阶段中的作业可以按任何顺序运行,也可以并行运行。
    • 第一个 script 任务下载并安装 JMeter。 jmeterVersion 管道变量指定要安装的 JMeter 版本。
    • 第二个 script 任务运行 JMeter。 -J 参数通过从管道中读取 STAGING_HOSTNAME 变量来设置 JMeter 中的 hostname 属性。
    • 第三个 script 任务安装 xsltproc(一种 XSLT 处理器),并将 JMeter 输出转换为 JUnit。
    • PublishTestResults@2 任务将生成的 JUnit 报告 (JUnit.xml) 发布到管道。 Azure Pipelines 可帮助可视化测试结果。
  2. 在集成终端中,将 azure-pipelines.yml 添加到索引,提交更改,然后将分支推送到 GitHub。

    git add azure-pipelines.yml
    git commit -m "Run load tests with Apache JMeter"
    git push origin jmeter
    

观察 Azure Pipelines 运行测试

现在,你将观察管道运行。 你会看到负载测试在“过渡”阶段运行。

  1. 在 Azure Pipelines 中,转到生成并在运行时对其进行跟踪。

    在“过渡”阶段,可看见负载测试在网站部署后运行。

  2. 生成完成后,转到摘要页。

    Screenshot of Azure Pipelines, showing the completed stages.

    可看到已成功完成部署和负载测试。

  3. 在页面顶部附近,注意摘要。

    可看到 Space Game 网站的生成工件像往常一样发布。 另请注意“Tests and coverage”部分,其中显示负载测试已通过。

    A screenshot of Azure Pipelines, showing the test summary.

  4. 选择“测试摘要”以查看完整的报告。

    此报告显示两个测试均已通过。

    Screenshot of Azure Pipelines, showing the full test report.

    如果有任何测试失败,你都会看到失败的详细结果。 根据这些结果可调查失败的根源。

    回想一下,XSLT 文件会生成一个名为 JUnit.xml 的 JUnit 文件。 JUnit 文件可解答以下两个问题:

    • 平均请求时间是否少于一秒?
    • 是否有不到 10% 的请求时间会超过一秒?

    此报告证明满足了这些要求。 在报告中选择“Outcome”箭头了解更多信息。 然后确保仅选中“Passed”。

    Screenshot of Filtering passed tests in the test report.

    可看到“Average Response Time”和“Max Response Time”这两种测试用例均已成功。

    Screenshot of the test report, showing two successful test cases.

注意

你使用的是在基本层上运行的 B1 应用服务计划。 此计划适用于流量需求较低的应用,如测试环境中的应用。 而由于该计划,你的网站性能可能会低于预期。 实际操作时,可以选择一个与生产环境更匹配的适用于“过渡”环境的计划。 例如,“标准”计划和“高级”计划适用于生产工作负载。 这些计划在专用虚拟机实例上运行。