使用 Azure Pipelines 和 Cloud Run 创建 CI/CD 流水线


本教程介绍了如何使用 Azure PipelinesCloud RunContainer Registry为 ASP.NET MVC Core Web 应用创建持续集成/持续部署 (CI/CD) 流水线。

CI/CD 流水线使用两个 Google Cloud 项目(一个用于开发,一个用于生产),如下图所示。

展示 Azure 构建和发布流水线如何与 Google Cloud 生产和开发流水线进行交互的架构。

在流水线的开端,开发者将提交对示例代码库的更改。此操作会触发流水线创建一个发布并将其部署到开发集群中的 Cloud Run。然后,发布管理员要升级版本,使其部署到生产项目中。

本教程适用于开发者和 DevOps 工程师。它假定您具备 .NET Core、Azure Pipelines、Cloud Run 和 git 的基础知识。要完成本教程,您需要拥有 Azure DevOps 帐号的管理员权限。

目标

  • 将 Container Registry 连接到 Azure Pipelines 以发布 Docker 映像。
  • 准备 .NET Core 示例应用以部署到 Cloud Run 中。
  • 设置 Azure Pipelines 和 Google Cloud 之间的身份验证。
  • 使用 Azure Pipelines 发布管理功能来编排 Cloud Run 部署。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

查看 Azure DevOps 价格页面,了解使用 Azure DevOps 时可能需要支付的所有费用。

准备工作

在本教程中,您将使用两个单独的项目,一个用于开发,一个用于生产。使用单独的项目可让您在将发布部署到生产环境之前对其进行测试,还可单独管理身份和访问权限管理 (IAM) 角色和权限。

  1. 创建一个 Google Cloud 项目用于开发。本教程将此项目称为开发项目
  2. 创建一个用于生产环境的 Google Cloud 项目。本教程将此项目称为生产项目
  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 确保您拥有 Azure DevOps 帐号,并且拥有其管理员权限。如果您还没有 Azure DevOps 帐号,可以在 Azure DevOps 首页上注册一个。

创建 Azure DevOps 项目

您可以使用 Azure DevOps 来管理源代码、运行构建和测试以及将部署编排到 Cloud Run。首先,在 Azure DevOps 帐号中创建一个项目。

  1. 转到 Azure DevOps 首页 (https://dev.azure.com/YOUR_AZURE_DEVOPS_ACCOUNT_NAME)。
  2. 点击新建项目
  3. 输入项目名称,例如 CloudDemo
  4. Visibility 设置为 Private,然后点击 Create project
  5. 创建项目后,点击左侧菜单中的存储库
  6. 点击导入,从 GitHub 创建 dotnet-docs-samples 代码库分支,然后设置以下值:
    • 代码库类型Git
    • 克隆网址https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
  7. 点击导入

    导入流程完成后,您将看到 dotnet-docs-samples 代码库的源代码。

将 Azure Pipelines 连接到 Container Registry

您必须先将 Azure Pipelines 连接到 Container Registry,然后才能为 CloudDemo 应用设置持续集成。此连接允许 Azure Pipelines 将容器映像发布到 Container Registry。

设置一个服务帐号用于发布映像

在生产项目中创建 Google Cloud 服务帐号

  1. 在 Google Cloud 控制台中,切换到生产项目。
  2. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

  3. 初始化以下环境变量:

    DEV_PROJECT_ID=DEV_PROJECT_ID
    PROD_PROJECT_ID=PROD_PROJECT_ID
    

    请替换以下内容:

    • DEV_PROJECT_ID:您的开发项目的 ID
    • PROD_PROJECT_ID:您的生产项目的 ID
  4. 在生产项目中启用 Container Registry API:

    gcloud services enable containerregistry.googleapis.com \
        --project=$PROD_PROJECT_ID
    
  5. 创建一个服务帐号,供 Azure Pipelines 用于发布 Docker 映像:

    gcloud iam service-accounts create azure-pipelines-publisher \
        --display-name="Azure Pipelines Publisher" \
        --project=$PROD_PROJECT_ID
    
  6. 向服务帐号授予 Storage Admin IAM 角色 (roles/storage.admin),以允许 Azure Pipelines 推送到 Container Registry:

    AZURE_PIPELINES_PUBLISHER=azure-pipelines-publisher@$PROD_PROJECT_ID.iam.gserviceaccount.com
    
    gcloud projects add-iam-policy-binding $PROD_PROJECT_ID \
        --member serviceAccount:$AZURE_PIPELINES_PUBLISHER \
        --role roles/storage.admin \
        --project=$PROD_PROJECT_ID
    
  7. 生成服务帐号密钥:

    gcloud iam service-accounts keys create azure-pipelines-publisher.json \
        --iam-account $AZURE_PIPELINES_PUBLISHER \
        --project=$PROD_PROJECT_ID
    
    tr -d '\n' < azure-pipelines-publisher.json > azure-pipelines-publisher-oneline.json
    
  8. 查看服务帐号密钥文件的内容:

    echo $(<azure-pipelines-publisher-oneline.json)
    

    执行以下步骤之一时,需要用到服务帐号密钥。

为 Container Registry 创建服务连接

在 Azure Pipelines 中,为 Container Registry 创建新的服务连接:

  1. 在 Azure DevOps 菜单中,选择项目设置,然后选择管道 > 服务连接
  2. 点击 Create service connection
  3. 从列表中选择 Docker 注册表,然后点击下一步
  4. 在对话框中,为以下字段输入值:
    • 注册表类型其他
    • Docker 注册表https://gcr.io/PROD_PROJECT_ID,请将 PROD_PROJECT_ID 替换为您的生产项目名称(例如 https://gcr.io/azure-pipelines-test-project-12345)。
    • Docker ID_json_key
    • 密码:粘贴 azure-pipelines-publisher-oneline.json 的内容。
    • 服务连接名称gcr-tutorial
  5. 点击保存以创建连接。

持续构建

您现可使用 Azure Pipelines 来设置持续集成。对于推送到 Git 代码库的每个提交项,Azure Pipelines 都会构建代码并将构建工件打包到 Docker 容器中。随后,容器会被发布到 Container Registry。

该代码库已包含以下 Dockerfile:

#
# Copyright 2020 Google LLC
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

FROM mcr.microsoft.com/dotnet/aspnet:6.0
EXPOSE 8080

#------------------------------------------------------------------------------
# Copy publishing artifacts.
#------------------------------------------------------------------------------

WORKDIR /app
COPY CloudDemo.MvcCore/bin/Release/net6.0/publish/ /app/

ENV ASPNETCORE_URLS=http://0.0.0.0:8080

#------------------------------------------------------------------------------
# Run application in Kestrel.
#------------------------------------------------------------------------------

ENTRYPOINT ["dotnet", "CloudDemo.MvcCore.dll"]

您现在可以创建使用 YAML 语法的新流水线:

  1. 使用 Visual Studio 或命令行 git 客户端克隆新的 Git 代码库
  2. 在代码库的根目录中,创建一个名为 azure-pipelines.yml 的文件。
  3. 将以下代码复制到该文件中:

    resources:
    - repo: self
      fetchDepth: 1
    queue:
      name: Hosted Ubuntu 1604
    trigger:
    - master
    variables:
      TargetFramework: 'net6.0'
      BuildConfiguration: 'Release'
      DockerImageName: 'PROD_PROJECT_ID/CloudDemo'
    steps:
    - task: DotNetCoreCLI@2
      displayName: Publish
      inputs:
        projects: 'applications/clouddemo/netcore/CloudDemo.MvcCore.sln'
        publishWebProjects: false
        command: publish
        arguments: '--configuration $(BuildConfiguration) --framework=$(TargetFramework)'
        zipAfterPublish: false
        modifyOutputPath: false
    - task: PublishBuildArtifacts@1
      displayName: 'Publish Artifact'
      inputs:
        PathtoPublish: '$(build.artifactstagingdirectory)'
    - task: Docker@2
      displayName: 'Login to Container Registry'
      inputs:
        command: login
        containerRegistry: 'gcr-tutorial'
    - task: Docker@2
      displayName: 'Build and push image'
      inputs:
        Dockerfile: 'applications/clouddemo/netcore/Dockerfile'
        command: buildAndPush
        repository: '$(DockerImageName)'
    

    PROJECT_ID 替换为您的生产项目名称,然后保存该文件。

    由于 Cloud Run 是基于 Linux 的环境,因此该流水线使用基于 Linux 的构建代理。

  4. 提交您的更改并将其推送到 Azure Pipelines。

    Visual Studio

    1. 打开团队资源管理器,然后点击主页图标。
    2. 点击更改
    3. 输入类似 Add pipeline definition 的提交消息。
    4. 点击全部提交并推送

    命令行

    1. 暂存所有已修改的文件:

      git add -A
      
    2. 将更改提交到本地代码库:

      git commit -m "Add pipeline definition"
      
    3. 将更改推送到 Azure DevOps:

      git push
      
  5. 在 Azure DevOps 菜单中,选择流水线,然后点击 创建流水线

  6. 选择 Azure Repos Git

  7. 选择您的代码库。

  8. 查看流水线 YAML页面上,点击运行

    系统会触发新的构建。构建流程可能约两分钟后才能完成。

  9. 如需验证映像是否已发布到 Container Registry,请在 Google Cloud 控制台中切换到生产项目,选择 Container Registry > 映像,然后点击 CloudDemo

    这会显示一个映像及其标记。该标记与 Azure Pipelines 中运行的构建的数字 ID 相对应。

持续部署

现在,Azure Pipelines 会自动构建代码并为每个提交项发布 Docker 映像,因此您可以专注于部署工作。

与其他一些持续集成系统不同,Azure Pipelines 对构建和部署进行了区分,并为所有与部署相关的任务提供了一组标记为“版本管理”的专用工具。

Azure Pipelines 版本管理围绕以下概念构建:

  • “版本”是指构成应用特定版本的一组工件,通常是构建流程的结果。
  • “部署”是指提取一个发布并将其部署到特定环境的流程
  • 一次部署会执行一组“任务”,这些任务可分组为多个“作业”
  • 使用“阶段”可以将流水线分段,并可将部署编排到多个环境,例如开发和测试环境

您可以将发布流水线设置为在新构建完成时触发。流水线包括两个阶段:开发和生产。在每个阶段,发布流水线都使用构建流水线生成的 Docker 映像,然后将流水线部署到 Cloud Run。

在将每个 Docker 映像发布到 Container Registry 之前,您之前配置的构建流水线会为其添加标记。因此,在发布流水线中,您可以使用 $BUILD_BUILDID 变量来标识要部署的适当的 Docker 映像。

配置 Cloud Run

Cloud Run 是一个全托管式无服务器环境,因此您无需预配任何基础架构。为了帮助确保 Cloud Run 部署的安全,您需要设置 IAM。

部署和运行 Cloud Run 服务涉及多个身份,如下图所示。

在 Cloud Run 部署中作为服务帐号运行的身份。

其中每个身份都以服务帐号的形式实现,并用于特定用途,如下表所述。

服务帐号 使用者 用途 所需的角色
Azure Pipelines 发布者 构建流水线 将 Docker 映像发布到 Container Registry roles/storage.admin(仅限生产项目)
Azure Pipelines 部署者 发布流水线 启动 Cloud Run 部署 roles/run.admin
启动 CloudDemo 服务 roles/iam.serviceAccountUser
Cloud Run 服务代理 Cloud Run 从 Container Registry 中拉取 Docker 映像 roles/storage.objectViewer(仅限生产项目)
CloudDemo 运行者(运行时服务帐号 CloudDemo 服务 访问 Google Cloud 上的资源

您已创建并配置 Azure Pipelines 发布者服务帐号。在以下部分中,您将创建并配置其余服务帐号。

配置 Cloud Run 服务帐号

  1. 打开 Cloud Shell。

  2. 初始化以下环境变量:

    DEV_PROJECT_ID=DEV_PROJECT_ID
    PROD_PROJECT_ID=PROD_PROJECT_ID
    

    请替换以下内容:

    • DEV_PROJECT_ID:您的开发项目的 ID
    • PROD_PROJECT_ID:您的生产项目的 ID
  3. 在开发和生产项目中启用 Cloud Run API 和 Compute Engine API:

    gcloud services enable run.googleapis.com --project=$DEV_PROJECT_ID
    gcloud services enable run.googleapis.com --project=$PROD_PROJECT_ID
    

    启用这些 API 会在项目中创建 Cloud Run 服务代理帐号。

  4. 在存储 Docker 映像的生产项目中,向两个 Cloud Run 服务代理帐号授予访问 Container Registry 的权限:

    DEV_PROJECT_NUMBER=$(gcloud projects describe $DEV_PROJECT_ID \
        --format='value(projectNumber)')
    PROD_PROJECT_NUMBER=$(gcloud projects describe $PROD_PROJECT_ID \
        --format='value(projectNumber)')
    
    gcloud projects add-iam-policy-binding $PROD_PROJECT_ID \
        --member=serviceAccount:service-$DEV_PROJECT_NUMBER@serverless-robot-prod.iam.gserviceaccount.com \
        --role roles/storage.objectViewer
    
    gcloud projects add-iam-policy-binding $PROD_PROJECT_ID \
        --member=serviceAccount:service-$PROD_PROJECT_NUMBER@serverless-robot-prod.iam.gserviceaccount.com \
        --role roles/storage.objectViewer
    

配置 CloudDemo 运行者帐号

现在,您可以配置 CloudDemo 运行者帐号,这是 CloudDemo 服务的自定义运行时服务帐号

  • 创建名为 CloudDemo-runner 的服务帐号:

    gcloud iam service-accounts create clouddemo-runner \
        --display-name="CloudDemo Runner" \
        --project=$DEV_PROJECT_ID
    
    gcloud iam service-accounts create clouddemo-runner \
        --display-name="CloudDemo Runner" \
        --project=$PROD_PROJECT_ID
    
    DEV_CLOUDDEMO_RUNNER=clouddemo-runner@$DEV_PROJECT_ID.iam.gserviceaccount.com
    
    PROD_CLOUDDEMO_RUNNER=clouddemo-runner@$PROD_PROJECT_ID.iam.gserviceaccount.com
    

配置 Azure Pipelines 部署者帐号

最后,创建并配置 Azure Pipelines 部署者帐号,Azure 发布流水线将使用该帐号部署到 Cloud Run。

  1. 创建名为 azure-pipelines-deployer 的服务帐号:

    gcloud iam service-accounts create azure-pipelines-deployer \
        --display-name="Azure Pipelines Deployer" \
        --project=$PROD_PROJECT_ID
    
    AZURE_PIPELINES_DEPLOYER=azure-pipelines-deployer@$PROD_PROJECT_ID.iam.gserviceaccount.com
    
  2. 分配所需的 IAM 角色,在开发项目中部署新的 Cloud Run 服务或修订版本:

    gcloud projects add-iam-policy-binding $DEV_PROJECT_ID \
        --member serviceAccount:$AZURE_PIPELINES_DEPLOYER \
        --role roles/run.admin
    
    gcloud iam service-accounts add-iam-policy-binding \
        $DEV_CLOUDDEMO_RUNNER \
        --member=serviceAccount:$AZURE_PIPELINES_DEPLOYER \
        --role="roles/iam.serviceAccountUser" \
        --project=$DEV_PROJECT_ID
    
  3. 为生产项目分配同一组角色:

    gcloud projects add-iam-policy-binding $PROD_PROJECT_ID \
        --member serviceAccount:$AZURE_PIPELINES_DEPLOYER \
        --role roles/run.admin
    
    gcloud iam service-accounts add-iam-policy-binding \
        $PROD_CLOUDDEMO_RUNNER \
        --member=serviceAccount:$AZURE_PIPELINES_DEPLOYER \
        --role="roles/iam.serviceAccountUser" \
        --project=$PROD_PROJECT_ID
    
  4. 生成服务帐号密钥:

    gcloud iam service-accounts keys create azure-pipelines-deployer.json \
        --iam-account=$AZURE_PIPELINES_DEPLOYER \
        --project=$PROD_PROJECT_ID
    
    cat azure-pipelines-deployer.json | base64 -w 0
    

    您可以在配置发布流水线时使用此命令的输出。

配置发布流水线

现在,您可以返回 Azure Pipelines 来自动执行部署,其中包含以下步骤:

  • 部署到开发环境。
  • 在开始部署到生产环境之前请求手动审批。
  • 部署到生产环境。

创建发布定义

首先,创建一个发布定义:

  1. 在 Azure DevOps 菜单中,选择管道 > 发布
  2. 点击新建管道
  3. 从模板列表中,选择 Empty job
  4. 当系统提示您输入阶段名称时,请输入 Development
  5. 在屏幕顶部,将流水线命名为 CloudDemo
  6. 在流水线图中,点击工件旁边的添加
  7. 选择构建并添加以下设置:
    • 源类型构建
    • 源(构建管道):选择构建定义(应该只有一个选项)
    • 默认版本最新
    • 源别名build
  8. 点击添加
  9. 项目框中,点击持续部署触发器(闪电图标)来添加部署触发器。
  10. 持续部署触发器下,将开关设置为已启用
  11. 点击保存
  12. 如果需要,请输入注释,然后点击确定来进行确认。

    该流水线会显示如下内容。

    自动部署设置的流水线视图。

部署到开发项目

创建发布定义后,您现可为开发项目配置 Cloud Run 部署。

  1. 在菜单中,切换到任务标签页。
  2. 点击代理作业
  3. 代理规范设置为 ubuntu-18.04
  4. 代理作业旁边,点击向代理作业添加任务 ,来向阶段添加步骤。
  5. 选择命令行任务,然后点击添加
  6. 点击新添加的任务并配置以下设置:

    1. 显示名Deploy image to development project
    2. 脚本

      gcloud auth activate-service-account \
          --quiet \
          --key-file <(echo $(ServiceAccountKey) | base64 -d) && \
      gcloud run deploy clouddemo \
          --quiet \
          --service-account=clouddemo-runner@$(CloudRun.ProjectId.Development).iam.gserviceaccount.com \
          --allow-unauthenticated \
          --image=gcr.io/$(ContainerRegistry.ProjectId)/clouddemo:$BUILD_BUILDID \
          --platform=managed \
          --region=$(CloudRun.Region) \
          --project=$(CloudRun.ProjectId.Development)
      

      此命令会从环境变量获取服务帐号密钥,然后使用 gcloud CLI 将应用部署到 Cloud Run。Azure Pipelines 代理默认使用 gcloud CLI。

  7. 切换到变量标签页,然后添加以下变量。

    名称 Secret
    ServiceAccountKey 之前为 azure-pipelines-deployer 创建的服务帐号密钥。
    ContainerRegistry.ProjectId 您的生产项目的 ID。
    CloudRun.Region 之前选择的用于部署 Cloud Run 资源的地区。
    CloudRun.ProjectId.Development 您的开发项目的 ID。
    CloudRun.ProjectId.Production 您的生产项目的 ID。
  8. 点击保存

  9. 如果需要,请输入注释,然后点击确定来进行确认。

部署到生产集群

最后,配置生产项目的部署:

  1. 在菜单中,切换到管道标签页。
  2. 在“阶段”框中,选择添加 > 新阶段
  3. 从模板列表中,选择 Empty job
  4. 当系统提示您输入阶段名称时,请输入 Production
  5. 点击新创建阶段的闪电形图标。
  6. 进行以下设置:

    1. 选择触发器阶段后
    2. 阶段开发
    3. 部署前审批:(已启用)
    4. 审批者:选择您的用户名。

    流水线会显示如下视图。

    集群部署设置的流水线视图。

  7. 切换到任务标签页。

  8. 将鼠标悬停在任务标签页上,然后选择任务 > 生产

  9. 点击代理作业

  10. 代理规范设置为 ubuntu-18.04

  11. 点击向代理作业添加任务 ,来向阶段添加步骤。

  12. 选择命令行任务,然后点击添加

  13. 点击新添加的任务并配置以下设置:

    1. 显示名Deploy image to production project
    2. 脚本

      gcloud auth activate-service-account \
          --quiet \
          --key-file <(echo $(ServiceAccountKey) | base64 -d) && \
      gcloud run deploy clouddemo \
          --quiet \
          --service-account=clouddemo-runner@$(CloudRun.ProjectId.Production).iam.gserviceaccount.com \
          --allow-unauthenticated \
          --image=gcr.io/$(ContainerRegistry.ProjectId)/clouddemo:$BUILD_BUILDID \
          --platform=managed \
          --region=$(CloudRun.Region) \
          --project=$(CloudRun.ProjectId.Production)
      
  14. 点击保存

  15. 如果需要,请输入注释,然后点击确定来进行确认。

运行流水线

现在您已经配置了整个流水线,接下来可通过更改源代码来测试该流水线:

  1. 在本地计算机上,从您之前克隆的 Git 代码库中打开 applications\clouddemo\netcore\CloudDemo.MvcCore\Views\Home\Index.cshtml 文件。
  2. 在第 26 行中,将 ViewBag.Title 的值从 Home Page 更改为 Home Page Cloud Run
  3. 提交您的更改并将其推送到 Azure Pipelines。

    Visual Studio

    1. 打开团队资源管理器,然后点击主页图标。
    2. 点击更改
    3. 输入类似 Change site title 的提交消息。
    4. 点击全部提交并推送

    命令行

    1. 暂存所有已修改的文件:

      git add -A
      
    2. 将更改提交到本地代码库:

      git commit -m "Change site title"
      
    3. 将更改推送到 Azure Pipelines:

      git push
      
  4. 在 Azure DevOps 菜单中,选择管道。系统会触发构建。

  5. 构建完成后,选择管道 > 发布。系统会启动发布流程。

  6. 点击 Release-1 打开详细信息页面,等待开发阶段的状态切换为已成功

  7. 在 Google Cloud 控制台中,切换到开发项目。

  8. 在菜单中,选择计算 > Cloud Run

    clouddemo 服务已成功部署。

    Cloud Run 中的部署状态。

  9. 点击 clouddemo 以查看更多详细信息。

    系统会显示一个网址来说明 Cloud Run 已预配该服务。

  10. 在新的浏览器标签页中打开该网址,验证 CloudDemo 应用是否已部署并使用自定义标题。

  11. 在 Azure Pipelines 中,点击审批生产阶段旁),将部署提升到生产环境。

  12. (可选)输入注释。

  13. 点击审批进行确认,然后等待生产环境的状态切换为已成功

  14. 在 Google Cloud 控制台中,切换到生产项目。

  15. 在菜单中,选择计算 > Cloud Run

    clouddemo 服务将部署到生产项目。

  16. 点击 clouddemo 以查看更多详细信息。

    系统会显示一个网址来说明 Cloud Run 已预配该服务。

  17. 在新的浏览器标签页中打开该网址,验证 CloudDemo 应用是否已部署到生产项目并使用自定义标题。

清理

学完本教程后,请删除您创建的实体,避免日后再为这些实体付费。

删除 Azure Pipelines 项目

如需删除 Azure Pipelines 项目,请参阅 Azure DevOps Services 文档。删除 Azure Pipelines 项目会导致所有源代码更改丢失。

删除 Google Cloud 开发和生产项目

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤