本教程介绍如何使用 Azure Pipelines、Google Kubernetes Engine (GKE)、Google Container Registry 为 ASP.NET MVC Web 应用创建持续集成/持续部署 (CI/CD) 流水线。在本教程中,您可以在两个示例应用之间进行选择:
- 使用 .NET 6.0 并在 Linux 上运行的 ASP.NET Core Web 应用
- 使用.NET Framework 4 并在 Windows 上运行的 ASP .NET MVC Web 应用
CI/CD 流水线使用两个独立的 GKE 集群,一个用于开发,一个用于生产,如下图所示。
在流水线的开端,开发者将提交对示例代码库的更改。此操作会触发流水线创建一个发布并将其部署到开发集群。然后,发布管理员可以提升该发布,以便将其部署到生产集群中。
本教程适用于开发者和 DevOps 工程师。本教程假定您具备 Microsoft .NET、Azure Pipelines、GKE 的基本知识。本教程还要求您对 Azure DevOps 帐号具有管理访问权限。
目标
- 将 Google Container Registry 连接到 Azure Pipelines 以发布 Docker 映像。
- 准备 .NET 示例应用以部署到 GKE 中。
- 无需使用旧版身份验证即可安全地针对 GKE 进行身份验证。
- 使用 Azure Pipelines 发布管理来编排 GKE 部署。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
- GKE
- Cloud Load Balancing
- Cloud Storage(用于 Container Registry)
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
查看 Azure DevOps 价格页面,了解使用 Azure DevOps 时可能需要支付的所有费用。
准备工作
通常建议使用不同的项目来处理开发工作负载与生产工作负载,这样就能分别授予 Identity and Access Management (IAM) 角色与权限。为简单起见,本教程使用一个项目来处理两个 GKE 群集,一个集群用于开发环境,另一个用于生产环境。
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
- 确保您拥有 Azure DevOps 帐号并具有管理员访问权限。 如果您还没有 Azure DevOps 帐号,可以在 Azure DevOps 首页上注册一个。
创建 Azure DevOps 项目
您可以使用 Azure DevOps 来管理源代码、运行构建和测试以及将部署编排到 GKE。首先,在 Azure DevOps 帐号中创建一个项目。
- 转到 Azure DevOps 首页 (https://dev.azure.com/YOUR_AZURE_DEVOPS_ACCOUNT_NAME)。
- 点击新建项目。
- 输入项目名称,例如
CloudDemo
。 - 将可见性设置为私有,然后点击创建。
- 创建项目后,点击左侧菜单中的存储库。
- 点击导入,从 GitHub 创建
dotnet-docs-samples
代码库分支,然后设置以下值:- 代码库类型:
Git
- 克隆网址:
https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
- 代码库类型:
点击导入。
导入流程完成后,您将看到
dotnet-docs-samples
代码库的源代码。
将 Azure Pipelines 连接到 Google Container Registry
您必须先将 Azure Pipelines 连接到 Container Registry,然后才能为 CloudDemo
应用设置持续集成。此连接允许 Azure Pipelines 将容器映像发布到 Container Registry。
设置一个服务帐号用于发布映像
在您的项目中创建 Google Cloud 服务帐号:
打开 Google Cloud 控制台。
-
In the Google Cloud console, activate Cloud Shell.
为了节省输入项目 ID 和 Compute Engine 区域选项的时间,请运行以下命令以设置默认配置值:
gcloud config set project PROJECT_ID gcloud config set compute/zone us-central1-a
将
PROJECT_ID
替换为项目的 ID。在项目中启用 Container Registry API:
gcloud services enable containerregistry.googleapis.com
创建一个服务帐号,供 Azure Pipelines 用于发布 Docker 映像:
gcloud iam service-accounts create azure-pipelines-publisher \ --display-name="Azure Pipelines Publisher"
向服务帐号授予 Storage Admin IAM 角色 (
roles/storage.admin
),以允许 Azure Pipelines 推送到 Container Registry:AZURE_PIPELINES_PUBLISHER=azure-pipelines-publisher@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \ --member serviceAccount:$AZURE_PIPELINES_PUBLISHER \ --role roles/storage.admin
生成服务帐号密钥:
gcloud iam service-accounts keys create azure-pipelines-publisher.json \ --iam-account $AZURE_PIPELINES_PUBLISHER tr -d '\n' < azure-pipelines-publisher.json > azure-pipelines-publisher-oneline.json
查看服务帐号密钥文件的内容:
echo $(<azure-pipelines-publisher-oneline.json)
执行以下步骤之一时,需要用到服务帐号密钥。
为 Google Container Registry 创建服务连接
在 Azure Pipelines 中,为 Container Registry 创建新的服务连接:
- 在 Azure DevOps 菜单中,选择项目设置,然后选择管道 > 服务连接。
- 点击 Create service connection。
- 从列表中选择 Docker 注册表,然后点击下一步。
- 在对话框中,为以下字段输入值:
- Registry type:Others
- Docker Registry:
https://gcr.io/PROJECT_ID
,注意将PROJECT_ID
替换为您的项目名称(例如https://gcr.io/azure-pipelines-test-project-12345
)。 - Docker ID:
_json_key
- 密码:粘贴
azure-pipelines-publisher-oneline.json
的内容。 - 服务连接名称:
gcr-tutorial
- 点击保存以创建连接。
持续构建
您现可使用 Azure Pipelines 来设置持续集成。对于推送到 Git 代码库的每个提交项,Azure Pipelines 都会构建代码并将构建工件打包到 Docker 容器中。随后,容器会被发布到 Container Registry。
该代码库已包含以下 Dockerfile:
.NET/Linux
.NET Framework/Windows
您现在可以创建使用 YAML 语法的新流水线:
- 使用 Visual Studio 或命令行
git
客户端克隆新的 Git 代码库并查看main
分支。 - 在代码库的根目录中,创建一个名为
azure-pipelines.yml
的文件。 将以下代码复制到该文件中:
.NET/Linux
resources: - repo: self fetchDepth: 1 pool: vmImage: ubuntu-20.04 trigger: - main variables: TargetFramework: 'net6.0' BuildConfiguration: 'Release' DockerImageName: '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: CmdLine@1 displayName: 'Lock image version in deployment.yaml' inputs: filename: /bin/bash arguments: '-c "awk ''{gsub(\"CLOUDDEMO_IMAGE\", \"gcr.io/$(DockerImageName):$(Build.BuildId)\", $0); print}'' applications/clouddemo/netcore/deployment.yaml > $(build.artifactstagingdirectory)/deployment.yaml"' - 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)'
.NET Framework/Windows
resources: - repo: self fetchDepth: 1 pool: vmImage: windows-2019 # Matches WINDOWS_LTSC in GKE demands: - msbuild - visualstudio trigger: - master variables: Solution: 'applications/clouddemo/net4/CloudDemo.Mvc.sln' BuildPlatform: 'Any CPU' BuildConfiguration: 'Release' DockerImageName: 'PROJECT_ID/clouddemo' steps: - task: NuGetCommand@2 displayName: 'NuGet restore' inputs: restoreSolution: '$(Solution)' - task: VSBuild@1 displayName: 'Build solution' inputs: solution: '$(Solution)' msbuildArgs: '/p:DeployOnBuild=true /p:PublishProfile=FolderProfile' platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - task: PowerShell@2 displayName: 'Lock image version in deployment.yaml' inputs: targetType: 'inline' script: '(Get-Content applications\clouddemo\net4\deployment.yaml) -replace "CLOUDDEMO_IMAGE","gcr.io/$(DockerImageName):$(Build.BuildId)" | Out-File -Encoding ASCII $(build.artifactstagingdirectory)\deployment.yaml' - 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/net4/Dockerfile' command: buildAndPush repository: '$(DockerImageName)'
将
PROJECT_ID
替换为您的项目名称,然后保存该文件。提交您的更改并将其推送到 Azure Pipelines。
Visual Studio
- 打开团队资源管理器,然后点击主页图标。
- 点击更改。
- 输入类似
Add pipeline definition
的提交消息。 - 点击全部提交并推送。
命令行
暂存所有已修改的文件:
git add -A
将更改提交到本地代码库:
git commit -m "Add pipeline definition"
将更改推送到 Azure DevOps:
git push
在 Azure DevOps 菜单中,选择流水线,然后点击 创建流水线。
选择 Azure Repos Git。
选择您的代码库。
在查看流水线 YAML页面上,点击运行。
系统会触发新的构建。构建流程可能需要大约六分钟时间才能完成。
如需验证映像是否已发布到 Container Registry,请在 Google Cloud 控制台中切换到您的项目,选择 Container Registry > 映像,然后点击 clouddemo。
系统会显示一个映像以及此映像的标记。该标记与 Azure Pipelines 中运行的构建的数字 ID 相对应。
持续部署
现在,Azure Pipelines 会自动构建代码并为每个提交项发布 Docker 映像,因此您可以专注于部署工作。
与其他一些持续集成系统不同,Azure Pipelines 对构建和部署进行了区分,并为所有与部署相关的任务提供了一组标记为“版本管理”的专用工具。
Azure Pipelines 版本管理围绕以下概念构建:
- “版本”是指构成应用特定版本的一组工件,通常是构建流程的结果。
- “部署”是指提取一个发布并将其部署到特定环境的流程。
- 一次部署会执行一组“任务”,这些任务可分组为多个“作业”。
- 使用“阶段”可以将流水线分段,并可将部署编排到多个环境,例如开发和测试环境。
CloudDemo 构建流程产生的主要工件是 Docker 映像。但是,由于 Docker 映像已发布到 Container Registry,不在 Azure Pipelines 的处理范围内。 因此,映像不适合作为版本的定义。
若要部署到 Kubernetes,您还需要一个清单,它类似于材料清单。此清单不仅定义了 Kubernetes 应该创建和管理的资源,还指定了要使用的 Docker 映像的确切版本。Kubernetes 清单非常适合用作定义 Azure Pipelines 版本管理中的版本的工件。
配置 Kubernetes 部署
如需在 Kubernetes 中运行 CloudDemo,您需要以下资源:
- 一个 Deployment,它定义运行由构建生成的 Docker 映像的单个 pod。
- 一个让负载均衡器能够访问该 pod 的 NodePort 服务。
- 一个 Ingress,用于通过 Cloud HTTP(S) 负载均衡器将应用公开到公共互联网。
代码库已包含以下 Kubernetes 清单,用于定义这些资源:
.NET/Linux
.NET Framework/Windows
设置开发和生产环境
在返回 Azure Pipelines 版本管理之前,您需要创建 GKE 集群。
创建 GKE 集群
返回到 Cloud Shell 实例。
为您的项目启用 GKE API:
gcloud services enable container.googleapis.com
使用以下命令创建开发集群。请注意,这可能需要几分钟才能完成:
.NET/Linux
gcloud container clusters create azure-pipelines-cicd-dev --enable-ip-alias
.NET Framework/Windows
gcloud container clusters create azure-pipelines-cicd-dev --enable-ip-alias gcloud container node-pools create azure-pipelines-cicd-dev-win \ --cluster=azure-pipelines-cicd-dev \ --image-type=WINDOWS_LTSC \ --no-enable-autoupgrade \ --machine-type=n1-standard-2
使用以下命令创建生产集群。请注意,这可能需要几分钟才能完成:
.NET/Linux
gcloud container clusters create azure-pipelines-cicd-prod --enable-ip-alias
.NET Framework/Windows
gcloud container clusters create azure-pipelines-cicd-prod --enable-ip-alias gcloud container node-pools create azure-pipelines-cicd-prod-win \ --cluster=azure-pipelines-cicd-prod \ --image-type=WINDOWS_LTSC \ --no-enable-autoupgrade \ --machine-type=n1-standard-2
将 Azure Pipelines 连接到开发集群
就像您可以使用 Azure Pipelines 连接到 Container Registry 之类的外部 Docker 注册表一样,Azure Pipelines 也支持集成外部 Kubernetes 集群。
您可以使用 Google Cloud 服务帐号向 Container Registry 进行身份验证,但 Azure Pipelines 不支持使用 Google Cloud 服务帐号进行 GKE 身份验证。必须改用 Kubernetes 服务帐号。
如需将 Azure Pipelines 连接到开发集群,必须先创建一个 Kubernetes 服务帐号。
在 Cloud Shell 中,连接到开发集群:
gcloud container clusters get-credentials azure-pipelines-cicd-dev
为 Azure Pipelines 创建 Kubernetes 服务帐号:
kubectl create serviceaccount azure-pipelines-deploy
为 Azure Pipelines 创建包含令牌凭据的 Kubernetes Secret:
kubectl create secret generic azure-pipelines-deploy-token --type=kubernetes.io/service-account-token --dry-run -o yaml \ | kubectl annotate --local -o yaml -f - kubernetes.io/service-account.name=azure-pipelines-deploy \ | kubectl apply -f -
通过创建集群角色绑定将
cluster-admin
角色分配给服务帐号:kubectl create clusterrolebinding azure-pipelines-deploy --clusterrole=cluster-admin --serviceaccount=default:azure-pipelines-deploy
确定集群的 IP 地址:
gcloud container clusters describe azure-pipelines-cicd-dev --format=value\(endpoint\)
您稍后将用到此地址。
在 Azure DevOps 菜单中,选择 Project settings,然后选择 Pipelines > Service connections。
点击 New service connection。
选择 Kubernetes,然后点击 Next。
进行以下设置。
- Authentication method:Service account。
- Server URL:
https://PRIMARY_IP
。 将PRIMARY_IP
替换为您之前确定的 IP 地址。 - Secret:您之前创建的 Kubernetes Secret。如需获取 Secret,请运行以下命令,复制 Secret,然后将 Secret 复制到 Azure 页面中。
kubectl get secret azure-pipelines-deploy-token -o yaml
- Service connection name:
azure-pipelines-cicd-dev
。
点击保存。
将 Azure Pipelines 连接到生产集群
如需将 Azure Pipelines 连接到生产集群,您可以采用相同的方法。
在 Cloud Shell 中,连接到生产集群:
gcloud container clusters get-credentials azure-pipelines-cicd-prod
为 Azure Pipelines 创建 Kubernetes 服务帐号:
kubectl create serviceaccount azure-pipelines-deploy
通过创建集群角色绑定将
cluster-admin
角色分配给服务帐号:kubectl create clusterrolebinding azure-pipelines-deploy --clusterrole=cluster-admin --serviceaccount=default:azure-pipelines-deploy
确定集群的 IP 地址:
gcloud container clusters describe azure-pipelines-cicd-prod --format=value\(endpoint\)
您稍后将用到此地址。
在 Azure DevOps 菜单中,选择 Project settings,然后选择 Pipelines > Service connections。
点击 New service connection。
选择 Kubernetes,然后点击 Next。
进行以下设置:
- Authentication method:Service account。
- Server URL:
https://PRIMARY_IP
。 将PRIMARY_IP
替换为您之前确定的 IP 地址。 - Secret:在 Cloud Shell 中运行以下命令并复制输出结果:
kubectl get secret $(kubectl get serviceaccounts azure-pipelines-deploy -o custom-columns=":secrets[0].name") -o yaml
- Service connection name:
azure-pipelines-cicd-prod
。
点击保存。
配置版本流水线
设置 GKE 基础架构后,返回 Azure Pipelines 设置自动部署,其中包括以下内容:
- 部署到开发环境。
- 在开始部署到生产环境之前请求手动审批。
- 部署到生产环境。
创建版本定义
首先,创建一个新的版本定义。
- 在 Azure DevOps 菜单中,选择 Pipelines > Releases。
- 点击 New pipeline。
- 从模板列表中,选择 Empty job。
- 当系统提示您输入阶段名称时,请输入
Development
。 - 在屏幕顶部,将版本命名为 CloudDemo-KubernetesEngine。
- 在流水线图中,点击工件旁边的添加。
选择 Build 并添加以下设置:
- Source type:Build
- Source (build pipeline):选择构建定义(应该只有一个选项)
- Default version:
Latest
- Source Alias:
manifest
点击添加。
在项目框中,点击持续部署触发器(闪电图标)来添加部署触发器。
在持续部署触发器下,将开关设置为已启用。
点击保存。
如果需要,请输入注释,然后点击确定来进行确认。
该流水线会显示如下内容。
部署到开发集群
创建发布定义后,您现在可以配置针对 GKE 开发集群的部署。
- 在菜单中,切换到 Tasks 标签页。
点击 Agent job 并配置以下设置:
- Agent pool:Azure Pipelines
- Agent specification:ubuntu-18.04
在 Agent job 旁边,点击 Add a task to agent job
以向阶段添加步骤。选择 Deploy to Kubernetes 任务,然后点击 Add。
点击新添加的任务并配置以下设置:
- Display name:
Deploy
- Action:deploy
- Kubernetes service connection:azure-pipelines-cicd-dev
- Namespace:
default
- Strategy:None
- Manifests:
manifest/drop/deployment.yaml
- Display name:
点击保存。
如果需要,请输入注释,然后点击确定来进行确认。
部署到生产集群
最后,配置针对 GKE 生产集群的部署。
- 在菜单中,切换到 Pipeline 标签。
- 在“阶段”框中,选择添加 > 新阶段。
- 从模板列表中,选择 Empty job。
- 当系统提示您输入阶段名称时,请输入
Production
。 - 点击新创建阶段的闪电形图标。
进行以下设置:
- 选择触发器:After stage
- 阶段:Dev
- Pre-deployment approvals:(启用)
- Approvers:选择您自己的用户名。
流水线现在应如下所示:
切换到 Tasks 标签页。
将鼠标悬停在 Tasks 标签页上,然后选择 Tasks > Production。
点击 Agent job 并配置以下设置:
- Agent pool:Azure Pipelines
- Agent specification:ubuntu-18.04
点击 Add a task to agent job
以向阶段添加步骤。选择 Deploy to Kubernetes 任务,然后点击 Add。
点击新添加的任务并配置以下设置:
- Display name:
Deploy
- Action:deploy
- Kubernetes service connection:azure-pipelines-cicd-prod
- Namespace:
default
- Strategy:None
- Manifests:
manifest/drop/deployment.yaml
- Display name:
点击保存。
如果需要,请输入注释,然后点击确定来进行确认。
运行流水线
现在您已经配置了整个流水线,接下来可通过更改源代码来测试该流水线:
在本地计算机上,从您之前克隆的 Git 代码库中打开
Index.cshtml
文件:.NET/Linux
文件位于
applications\clouddemo\netcore\CloudDemo.MvcCore\Views\Home\
中.NET Framework/Windows
文件位于
applications\clouddemo\net4\CloudDemo.Mvc\Views\Home\
中在第 26 行中,将
ViewBag.Title
的值从Home Page
更改为This app runs on GKE
。提交您的更改并将其推送到 Azure Pipelines。
Visual Studio
- 打开团队资源管理器,然后点击主页图标。
- 点击更改。
- 输入类似
Change site title
的提交消息。 - 点击全部提交并推送。
命令行
暂存所有已修改的文件:
git add -A
将更改提交到本地代码库:
git commit -m "Change site title"
将更改推送到 Azure Pipelines:
git push
在 Azure DevOps 菜单中,选择管道。系统会触发构建。
构建完成后,选择管道 > 发布。系统会启动发布流程。
点击 Release-1 打开详细信息页面,等待 Development 阶段的状态切换为 Succeeded。
在 Google Cloud 控制台中,选择 Kubernetes Engine > Service 和 Ingress > Ingress。
找到 azure-pipelines-cicd-dev 集群的 Ingress 服务,并等待其状态切换为正常。这可能需要几分钟。
打开同一行的前端列中的链接。您可能会先看到一个错误,因为负载均衡器在几分钟后才能使用。待其准备就绪后,观察 CloudDemo 是否已部署并正在使用自定义标题。
在 Azure Pipelines 中,点击 Prod 阶段下方的 Approve 按钮,以将部署提升到生产环境。
如果您没有看到该按钮,则可能需要先批准或拒绝以前的版本。
必要时,请输入注释,然后点击 Approve 进行确认。
等待 Prod 环境的状态切换到 Succeeded。您可能需要在浏览器中手动刷新页面才能看到此信息。
在 Google Cloud 控制台中,刷新 Service 和 Ingress 页面。
找到 azure-pipelines-cicd-prod 集群的 Ingress 服务,并等待其状态切换为正常。这可能需要几分钟。
打开同一行的前端列中的链接。同样,您可能会先看到一个错误,因为负载均衡器在几分钟后才能使用。待其准备就绪后,您会再次看到显示自定义标题的 CloudDemo 应用,而这次在生产集群中运行。
清理
学完本教程后,请删除您创建的实体,避免日后再为这些实体付费。
删除 Azure Pipelines 项目
如需删除 Azure Pipelines 项目,请参阅 Azure DevOps Services 文档。删除 Azure Pipelines 项目会导致所有源代码更改丢失。
删除 Google Cloud 开发和项目
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
后续步骤
- 配置 Container Registry 的精细访问权限控制。
- 了解如何在 Compute Engine 上部署高可用性 SQL Server 组。
- 了解如何在 Google Cloud Platform 上使用 .NET。
- 安装 Cloud Tools for Visual Studio。
- 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud Architecture Center。