教程:使用 Canary 部署策略进行 Kubernetes 部署
Azure DevOps Services | Azure DevOps Server 2022
本分步指南介绍如何将 Kubernetes 清单任务与 canary
策略结合使用。 Canary 部署策略将应用程序的新版本部署在稳定的生产版本旁边。
使用关联的工作流来部署代码,并比较基线和 Canary 应用部署。 根据评估,决定是升级还是拒绝 Canary 部署。
本教程使用 Docker 注册表和 Azure Resource Manager 服务连接连接到 Azure 资源。 对于 Azure Kubernetes 服务 (AKS) 专用群集或禁用了本地帐户的群集,Azure 资源管理器服务连接是更好的连接方式。
先决条件
至少具有用户权限的 Azure DevOps 项目。
一个 Azure 帐户。 免费创建帐户。
具有推送权限的 Azure 容器注册表实例。
已部署 Azure Kubernetes 服务 (AKS) 群集。 当部署 AKS 群集或之后,可以将 AKS 群集附加到 Azure 容器注册表群集。
一个 GitHub 帐户。 创建免费 GitHub 帐户。
https://github.com/MicrosoftDocs/azure-pipelines-canary-k8s GitHub 存储库分支。
重要
在以下流程中,系统可能会提示你创建 GitHub 服务连接或重定向到 GitHub 以登录、安装或授权 Azure Pipelines。 按照屏幕上的说明完成该过程。 有关详细信息,请参阅访问 GitHub 存储库。
GitHub 存储库文件
GitHub 存储库包含以下文件:
文件 | 说明 |
---|---|
./app/app.py | 简单的基于 Flask 的 Web 服务器。 该文件根据 success_rate 变量的值为良好响应和不良响应的数量设置了一个自定义计数器。 |
./app/Dockerfile | 用于在每次更改 app.py 时生成映像。 每次更改都会触发生成管道来构建映像并将其推送到容器注册表。 |
./manifests/deployment.yml | 包含与已发布镜像对应的 sampleapp 部署工作负载的规范。 可以将此清单文件用于部署对象的稳定版本,并用于导出工作负载的基线和 Canary 变体。 |
./manifests/service.yml | 创建 sampleapp 服务。 此服务将请求路由到由稳定部署、基准部署和 Canary 部署启动的 Pod。 |
./misc/fortio.yml | 设置 fortio 部署。 此部署是一个负载测试工具,用于向已部署 sampleapp 的服务发送请求流。 请求流在三种部署下路由到 Pod:稳定、基准和 Canary。 |
创建服务连接
- 在 Azure DevOps 项目中,转到项目设置>管道>服务连接。
- 创建与 Azure 容器注册表实例关联的名为 azure-pipelines-canary-acr 的 Docker 注册表服务连接。
- 为资源组创建名为 azure-pipelines-canary-k8s 的 Azure Resource Manager 与工作负荷标识的服务连接。
添加生成阶段
在 Azure DevOps 项目中,转到管道>创建管道或新建管道。
为代码位置选择 GitHub,然后选择分叉的 azure-pipelines-canary-k8s 存储库。
在“配置”选项卡上,选择“初学者管道”。
在审阅选项卡上,用以下代码替换管道 YAML。
trigger: - main pool: vmImage: ubuntu-latest variables: imageName: azure-pipelines-canary-k8s # name of ACR image dockerRegistryServiceConnection: azure-pipelines-canary-acr # name of ACR service connection imageRepository: 'azure-pipelines-canary-k8s' # name of image repository containerRegistry: example.azurecr.io # name of Azure container registry tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build stage jobs: - job: Build displayName: Build pool: vmImage: ubuntu-latest steps: - task: Docker@2 displayName: Build and push image inputs: containerRegistry: $(dockerRegistryServiceConnection) repository: $(imageName) command: buildAndPush Dockerfile: app/Dockerfile tags: | $(tag)
如果创建的 Docker 注册表服务连接与名为
example.azurecr.io
的容器注册表相关联,则映像将设置为example.azurecr.io/azure-pipelines-canary-k8s:$(Build.BuildId)
。选择保存并运行,并确保作业成功运行。
编辑清单文件
在存储库分支中,编辑 manifests/deployment.yml,将 <foobar>
替换为容器注册表的 URL,例如 example.azurecr.io/azure-pipelines-canary-k8s
。
设置连续部署
现在,设置持续部署,部署 Canary 阶段,并通过手动审批来升级或拒绝 Canary。
创建环境
可以使用 YAML 或经典进行部署。
- 在 Azure DevOps 项目中,转到管道>环境,然后选择创建环境或新建环境。
- 在第一个新建环境屏幕上,在名称下输入 akscanary,在资源下选择 Kubernetes,然后选择下一步。
- 按如下所示填写 Kubernetes 资源屏幕:
- 提供程序:选择 Azure Kubernetes 服务。
- Azure 订阅:选择自己的 Azure 订阅。
- 群集:选择你的 AKS 群集。
- 命名空间:选择新建,并输入 canarydemo。
- 选择“验证和创建”。
添加 Canary 阶段
转到管道,选择创建的管道,然后选择编辑。
将整个管道 YAML 替换为以下代码。
此代码更改了你之前运行的
Docker@2
步骤以使用阶段,并添加了另外两个步骤来复制清单和 misc 目录作为连续阶段使用的工件。该代码还将一些值移动到变量中,以便以后在管道中更容易使用。 在
containerRegistry
变量中,将<example>
替换为容器注册表的名称。trigger: - main pool: vmImage: ubuntu-latest variables: imageName: azure-pipelines-canary-k8s dockerRegistryServiceConnection: azure-pipelines-canary-acr imageRepository: 'azure-pipelines-canary-k8s' containerRegistry: <example>.azurecr.io tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build stage jobs: - job: Build displayName: Build pool: vmImage: ubuntu-latest steps: - task: Docker@2 displayName: Build and push image inputs: containerRegistry: $(dockerRegistryServiceConnection) repository: $(imageName) command: buildAndPush Dockerfile: app/Dockerfile tags: | $(tag) - publish: manifests artifact: manifests - publish: misc artifact: misc
在 YAML 文件末尾添加另一个阶段以部署 Canary 版本。 将值
my-resource-group
和my-aks-cluster
替换为资源组和 Azure Kubernetes 服务群集名称。trigger: - main pool: vmImage: ubuntu-latest variables: imageName: azure-pipelines-canary-k8s dockerRegistryServiceConnection: azure-pipelines-canary-acr imageRepository: 'azure-pipelines-canary-k8s' containerRegistry: yourcontainerregistry.azurecr.io #update with container registry tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build stage jobs: - job: Build displayName: Build pool: vmImage: ubuntu-latest steps: - task: Docker@2 displayName: Build and push image inputs: containerRegistry: $(dockerRegistryServiceConnection) repository: $(imageName) command: buildAndPush Dockerfile: app/Dockerfile tags: | $(tag) - publish: manifests artifact: manifests - publish: misc artifact: misc - stage: DeployCanary displayName: Deploy canary dependsOn: Build condition: succeeded() jobs: - deployment: Deploycanary displayName: Deploy canary pool: vmImage: ubuntu-latest environment: 'akscanary' strategy: runOnce: deploy: steps: - task: KubernetesManifest@1 displayName: Create Docker Registry Secret inputs: action: 'createSecret' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' secretType: 'dockerRegistry' secretName: 'my-acr-secret' dockerRegistryEndpoint: 'azure-pipelines-canary-acr' - task: KubernetesManifest@1 displayName: Deploy to Kubernetes cluster inputs: action: 'deploy' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' strategy: 'canary' percentage: '25' manifests: | $(Pipeline.Workspace)/manifests/deployment.yml $(Pipeline.Workspace)/manifests/service.yml containers: '$(containerRegistry)/$(imageRepository):$(tag)' imagePullSecrets: 'my-acr-secret' - task: KubernetesManifest@1 displayName: Deploy Forbio to Kubernetes cluster inputs: action: 'deploy' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' manifests: '$(Pipeline.Workspace)/misc/*'
选择验证并保存,然后将管道直接保存到主分支。
添加用于提升或拒绝 Canary 部署的手动批准
可以使用 YAML 或经典进行手动干预。
- 创建名为 akspromote 的新 Kubernetes 环境。
- 从环境列表中打开新的 akspromote 环境,然后在审批和检查选项卡上选择审批。
- 在审批屏幕上,在审批者下添加自己的用户帐户。
- 展开高级,并确保选中允许审批者审批自己的运行。
- 选择创建。
向管道添加升级和拒绝阶段
转到管道,选择创建的管道,然后选择编辑。
在 YAML 文件的末尾添加以下
PromoteRejectCanary
阶段,以提升更改。- stage: PromoteRejectCanary displayName: Promote or Reject canary dependsOn: DeployCanary condition: succeeded() jobs: - deployment: PromoteCanary displayName: Promote Canary pool: vmImage: ubuntu-latest environment: 'akspromote' strategy: runOnce: deploy: steps: - task: KubernetesManifest@1 displayName: Create Docker Registry Secret for akspromote inputs: action: 'createSecret' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' secretType: 'dockerRegistry' secretName: 'my-acr-secret' dockerRegistryEndpoint: 'azure-pipelines-canary-acr' - task: KubernetesManifest@1 displayName: promote canary inputs: action: 'promote' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' strategy: 'canary' manifests: '$(Pipeline.Workspace)/manifests/*' containers: '$(containerRegistry)/$(imageRepository):$(tag)' imagePullSecrets: 'my-acr-secret' ```
在回滚更改的文件末尾添加以下
RejectCanary
阶段。- stage: RejectCanary displayName: Reject canary dependsOn: PromoteRejectCanary condition: failed() jobs: - deployment: RejectCanary displayName: Reject Canary pool: vmImage: ubuntu-latest environment: 'akscanary' strategy: runOnce: deploy: steps: - task: KubernetesManifest@1 displayName: Create Docker Registry Secret for reject canary inputs: action: 'createSecret' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'kubernetes-testing' kubernetesCluster: 'my-aks-cluster' secretType: 'dockerRegistry' secretName: 'my-acr-secret' dockerRegistryEndpoint: 'azure-pipelines-canary-acr' - task: KubernetesManifest@1 displayName: Reject canary deployment inputs: action: 'reject' connectionType: 'azureResourceManager' azureSubscriptionConnection: 'azure-pipelines-canary-sc' azureResourceGroup: 'my-resource-group' kubernetesCluster: 'my-aks-cluster' namespace: 'default' strategy: 'canary' manifests: '$(Pipeline.Workspace)/manifests/*' ```
选择验证并保存,然后将管道直接保存到主分支。
部署稳定版本
对于管道的首次运行,群集中不存在工作负载的稳定版本及其基线或 Canary 版本。 按如下所示部署 sampleapp
工作负载的稳定版本。
可以使用 YAML 或经典部署稳定版本。
- 在 app/app.py 中,将
success_rate = 50
更改为success_rate = 100
。 此更改会触发管道,生成映像,并将其推送到容器注册表,还会触发DeployCanary
阶段。 - 由于你在
akspromote
环境中配置了审批,因此发布会在运行该阶段之前等待。 在生成运行摘要页面上,选择查看,然后选择审批。
一旦获得批准,管道就会将 manifests/deployment.yml 中的 sampleapp
工作负载的稳定版本部署到命名空间。
启动 Canary 工作流并拒绝审批
群集中现在存在稳定版本的 sampleapp
工作负载。 接下来,对模拟应用程序进行以下更改。
- 在 app/app.py 中,将
success_rate = 50
更改为success_rate = 100
。 此更改会触发管道,生成映像,并将其推送到容器注册表,还会触发DeployCanary
阶段。 - 由于你在
akspromote
环境中配置了审批,因此发布会在运行该阶段之前等待。 - 在生成运行摘要页面上,选择“审核”,然后在后续对话框中选择“拒绝”。 这会拒绝部署。
拒绝后,管道将阻止代码部署。
清理
如果不打算继续使用此应用程序,请删除 Azure 门户中的资源组和 Azure DevOps 中的项目。