练习 - 部署 Web 应用程序
在玩具公司,你的网站开发团队已将最新版本的网站提交到了 Git 存储库。 现在,你已准备好更新工作流以生成网站,并将其部署到 Azure 应用服务。
在此过程中,你将:
- 为生成作业添加新的被调用的工作流。
- 更新工作流以包含生成作业。
- 添加新的版本验收测试。
- 更新部署作业以部署应用程序。
- 运行工作流。
为生成作业添加可重用工作流
在这里,你将添加一个新的作业定义,其中包含生成网站应用程序所需的步骤。
打开 Visual Studio Code。
在 .github/workflows 文件夹中创建一个名为 build.yml 的新文件。
将以下内容添加到 build.yml 工作流文件中:
name: build-website on: workflow_call: jobs: build-application: name: Build application runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install .NET Core uses: actions/setup-dotnet@v3 with: dotnet-version: 3.1 - name: Build publishable website run: | dotnet publish --configuration Release working-directory: ./src/ToyCompany/ToyCompany.Website - name: Zip publishable website run: | zip -r publish.zip . working-directory: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish - name: Upload website as workflow artifact uses: actions/upload-artifact@v3 with: name: website path: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish/publish.zip
该作业将安装 .NET SDK 以生成解决方案。 然后该作业运行生成步骤,将网站应用程序的源代码转换为准备在 Azure 中运行的编译文件。 随后,该作业压缩已编译的项目并将其作为工作流项目上传。
保存对该文件所做的更改。
将生成作业添加到工作流
打开“workflow.yml”文件。
在“作业:”行下方的“Lint 分析”作业之前,添加一个名为“生成”的新作业,该作业使用刚刚定义的可重用工作流:
name: deploy-toy-website-end-to-end concurrency: toy-company on: push: branches: - main workflow_dispatch: permissions: id-token: write contents: read jobs: # Build the application and database. build: uses: ./.github/workflows/build.yml # Lint the Bicep file. lint: uses: ./.github/workflows/lint.yml
更新“部署-测试”作业,以依赖于新的“生成”作业:
# Deploy to the test environment. deploy-test: uses: ./.github/workflows/deploy.yml needs: [build, lint] with: environmentType: Test resourceGroupName: ToyWebsiteTest reviewApiUrl: https://sandbox.contoso.com/reviews secrets: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} reviewApiKey: ${{ secrets.REVIEW_API_KEY_TEST }}
更新“部署-生产”作业,使其也依赖于“生成”和“Lint 分析”作业。
# Deploy to the production environment. deploy-production: uses: ./.github/workflows/deploy.yml needs: - lint - build - deploy-test with: environmentType: Production resourceGroupName: ToyWebsiteProduction reviewApiUrl: https://api.contoso.com/reviews secrets: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} reviewApiKey: ${{ secrets.REVIEW_API_KEY_PRODUCTION }}
由于生产部署依赖于测试部署,因此严格来说,无需指定依赖项。 但是,如果重新排序或删除了作业或环境,则最好显式避免工作流错误地运行。
请注意,你以两种不同的方式指定
needs
列表 - 测试环境部署的依赖项列在一行中,而生产环境的依赖项则采用多行列表。 这两种方法是等效的。保存对该文件所做的更改。
更新版本验收测试文件
网站开发人员向网站添加了运行状况终结点。 此终结点检查网站是否联机,以及其能否访问数据库。 在这里,你将添加新的版本验收测试,以从部署工作流调用运行状况检查。
打开 deploy 文件夹中的 Website.Tests.ps1 文件。
添加调用运行状况检查的新测试用例。 如果响应代码不是表示成功的 200,则测试用例失败:
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" } }
保存对该文件所做的更改。
将输出添加到 Bicep 文件
你将很快添加一个将网站发布到 Azure 应用服务的部署步骤。 发布步骤需要应用服务应用的名称。 在这里,你将应用名称公开为 Bicep 文件的输出。
打开 deploy 文件夹中的 main.bicep 文件。
在文件内容的末尾,添加应用服务应用的名称作为输出:
output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName
保存对该文件所做的更改。
更新部署作业以传播输出
现在,需要更新“部署”作业,以获取 Bicep 部署的输出值,并使其可用于工作流的其余部分。
打开“.github/workflows”文件夹中的“deploy.yml”文件。
在“部署”作业的定义中,为
appServiceAppName
添加新输出:deploy: needs: validate environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest outputs: appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }} appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }} steps:
注意
当你开始在 Visual Studio Code 中使用 YAML 文件时,可能会看到一些红色波浪线,指示存在问题。 这是因为 YAML 文件的 Visual Studio Code 扩展有时会错误地猜测文件的架构。
可以忽略扩展报告的问题。 或者,如果你愿意,可以将以下代码添加到文件顶部以禁用扩展的猜测:
# yaml-language-server: $schema=./deploy.yml
添加作业以部署网站
在“部署”作业定义下方和“版本验收-测试”作业定义上方,定义一个新作业以将网站部署到应用服务:
deploy-website: needs: deploy environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/webapps-deploy@v2 name: Deploy website with: app-name: ${{ needs.deploy.outputs.appServiceAppName }} package: website/publish.zip
注意
请注意 YAML 文件的缩进,确保新作业与
deploy
作业在同一级别缩进。 如果不确定,请从下一步的示例中复制整个 deploy.yml 文件内容。请注意,作业依赖于使用
needs
关键字的“部署”作业。 此依赖项可确保在基础结构准备就绪之前不会部署网站。 它还允许作业访问“部署”作业的appServiceAppName
输出。另请注意,此作业包括下载工作流项目和登录 Azure 的步骤。 每个作业都在其自己的运行器中运行,因此需要是自包含的。
保存对该文件所做的更改。
验证 deploy.yml 文件内容,并提交更改
验证 deploy.yml 文件是否如以下示例所示:
name: deploy on: workflow_call: inputs: environmentType: required: true type: string resourceGroupName: required: true type: string reviewApiUrl: required: true type: string secrets: AZURE_CLIENT_ID: required: true AZURE_TENANT_ID: required: true AZURE_SUBSCRIPTION_ID: required: true reviewApiKey: required: true jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - if: inputs.environmentType != 'Production' uses: azure/arm-deploy@v1 name: Run preflight validation with: deploymentName: ${{ github.run_number }} resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} deploymentMode: Validate - if: inputs.environmentType == 'Production' uses: azure/arm-deploy@v1 name: Run what-if with: failOnStdErr: false resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} additionalArguments: --what-if deploy: needs: validate environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest outputs: appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }} appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }} steps: - uses: actions/checkout@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/arm-deploy@v1 id: deploy name: Deploy Bicep file with: failOnStdErr: false deploymentName: ${{ github.run_number }} resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} deploy-website: needs: deploy environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/webapps-deploy@v2 name: Deploy website with: app-name: ${{ needs.deploy.outputs.appServiceAppName }} package: website/publish.zip smoke-test: runs-on: ubuntu-latest needs: deploy steps: - uses: actions/checkout@v3 - run: | $container = New-PesterContainer ` -Path 'deploy/Website.Tests.ps1' ` -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' } Invoke-Pester ` -Container $container ` -CI name: Run smoke tests shell: pwsh
保存对该文件所做的更改。
在 Visual Studio Code 终端中,运行以下命令来提交更改并将其推送到 Git 存储库:
git add . git commit -m "Build and deploy website application" git push
这是你首次推送到此存储库,因此系统可能会提示你登录。
在Windows 上,键入 1 以使用 Web 浏览器进行身份验证,然后选择 Enter。
在 macOS 上,选择“授权”。
将会出现一个浏览器窗口。 你可能需要再次登录到 GitHub。 选择“授权”。
运行工作流
在浏览器中,转到“Actions”。
工作流的第一次运行(标记为“初始提交”)显示为失败。 创建存储库时,GitHub 会自动运行工作流。 由于机密当时未准备就绪,所以失败了。 你可以忽略此次失败。
选择 deploy-toy-website-end-to-end 工作流。
选择工作流的最新运行。
等待“生成”作业成功完成。
等待“部署-测试/部署”作业成功完成。
“注释”面板中列出了一些警告。 所有这些警告均因 Bicep 将信息性消息写入工作流日志的方式而起。 你可以忽略这些警告。
然后,工作流运行“部署-测试/版本验收-测试”作业,但版本验收测试失败:
选择“部署-测试/版本验收-测试”作业以打开工作流日志。
选择“运行版本验证测试”步骤,查看工作流日志的关联部分:
请注意,工作流日志指示网站和配置不正常。 应用程序与 Azure SQL 数据库的通信存在问题。 网站无法访问数据库是因为尚未部署或配置数据库。 你将很快解决此问题。