教程:使用 Canary 部署策略进行 Kubernetes 部署

Azure DevOps Services | Azure DevOps Server 2022

Canary 部署策略是指在稳定生产版本旁边部署应用程序的新版本。 然后,可以在提升或拒绝部署之前查看 Canary 版本与基线的比较情况。

本分步指南介绍了如何使用 Kubernetes 清单任务的 Canary 策略。 具体而言,你将了解如何为 Kubernetes 设置 Canary 部署,以及用于评估代码的关联工作流。 然后,使用该代码比较基线和 Canary 应用部署,以便决定是提升还是拒绝 Canary 部署。

如果你使用的是 Azure Kubernetes 服务,则最好使用 Azure 资源管理器服务连接类型连接到专用群集或禁用了本地帐户的群集。

先决条件

代码示例

在 GitHub 上为以下存储库创建分支。

https://github.com/MicrosoftDocs/azure-pipelines-canary-k8s

下面简要概述了本指南中使用的存储库中的文件:

  • ./app:
    • app.py - 一个简单的基于 Flask 的 Web 服务器,使用适用于 Python 应用程序的 Prometheus 检测库进行检测。 根据 success_rate 变量的值,为给出的正确响应和错误响应数设置自定义计数器。
    • Dockerfile - 用于生成映像,其中包含对 app.py 所做的每次更改。 每次更改后,都会触发生成管道,生成映像并将其推送到容器注册表。
  • ./manifests:
    • deployment.yml - 包含与之前发布的映像对应的 sampleapp 部署工作负载的规范。 此清单文件不仅用于稳定版本的部署对象,还可用于派生工作负载的基线和 Canary 变体。
    • service.yml - 创建 sampleapp 服务。 此服务将请求路由到由前面提到的部署(稳定、基线和 Canary)启动的 Pod。
  • ./misc
    • service-monitor.yml - 用于设置 ServiceMonitor 对象。 此对象设置 Prometheus 指标抓取。
    • fortio-deploy.yml - 用于设置 fortio 部署。 此部署稍后用作负载测试工具,用于将请求流发送到之前部署的 sampleapp 服务。 将发送到 sampleapp 的请求流路由到所有三种部署(稳定、基线和 Canary)下的 Pod。

注意

在本指南中,将使用 Prometheus 进行代码检测和监视。 任何等效的解决方案(如 Azure Application Insights)都可以用作替代方法。

安装 prometheus-operator

若要在群集上安装 Prometheus,请在开发计算机上使用以下命令。 必须安装 kubectl 和 Helm,并且必须将上下文设置为要部署的群集。 Grafana(稍后用于在仪表板上可视化基线和 Canary 指标)作为此 Helm 图表的一部分安装。

首先将 Prometheus Community Kubernetes Helm Chart 存储库添加到 Helm 安装。 然后,我们将安装 kube-prometheus 堆栈,它是 Kubernetes 清单、Grafana 仪表板和 Prometheus 规则的集合。

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update # update local cache
helm install --name sampleapp  prometheus-community/kube-prometheus-stack

创建服务连接

  1. 转到 Azure DevOps 菜单中的“项目设置”>“管道”>“服务连接”。
  2. 创建与容器注册表关联的 Docker 注册表服务连接。 将其命名为“azure-pipelines-canary-k8s”。
  3. 为要部署到的 Kubernetes 群集和命名空间创建 Kubernetes 服务连接。 将其命名为“azure-pipelines-canary-k8s”。

注意

如果你使用的是 Azure Kubernetes 服务,则最好使用 Azure 资源管理器服务连接类型连接到专用群集或禁用了本地帐户的群集。

设置持续集成

  1. 转到“管道”>“创建管道”,然后选择存储库。

  2. 在“配置”选项卡上,选择“初学者管道”。

  3. 在“审阅”选项卡上,将管道 YAML 替换为此代码。

    trigger:
    - main
    
    pool:
      vmImage: ubuntu-latest
    
    variables:
      imageName: azure-pipelines-canary-k8s
    
    steps:
    - task: Docker@2
      displayName: Build and push image
      inputs:
        containerRegistry: azure-pipelines-canary-k8s #replace with name of your Docker registry service connection
        repository: $(imageName)
        command: buildAndPush
        Dockerfile: app/Dockerfile
        tags: |
          $(Build.BuildId)
    

    如果创建的 Docker 注册表服务连接与 example.azurecr.io 相关联,则映像将基于上述配置连接到 example.azurecr.io/azure-pipelines-canary-k8s:$(Build.BuildId)

编辑清单文件

在 manifests/deployment.yml 中,将 <example> 替换为容器注册表的 URL。 例如,替换后,映像字段应类似于 contosodemo.azurecr.io/azure-pipelines-canary-k8s

设置连续部署

以下部分提供设置持续部署的步骤,包括如何部署 Canary 阶段,以及如何通过手动干预来提升或拒绝 Canary。

部署 Canary 阶段

可以使用 YAML 或经典进行部署。

  1. 转到“管道”>“环境”>“创建环境”。

  2. 创建新环境。

    • 名称:akscanary
    • 资源:选择“Kubernetes”。
  3. 选择“下一步”,并按如下所示配置 Kubernetes 资源:

    • 提供商:Azure Kubernetes 服务
    • Azure 订阅:选择保留 Kubernetes 群集的订阅。
    • 群集:选择你的群集。
    • 命名空间:创建名为“canarydemo”的新命名空间。
  4. 选择“验证和创建”。

  5. 转到“管道”。 选择创建的管道,然后选择“编辑”。

  6. 将之前创建的步骤更改为立即使用阶段。 再添加两个步骤,将清单和杂项目录复制为项目供连续阶段使用。 可能还需要将几个值移动到变量,以便稍后在管道中使用。 完整的 YAML 现应如下所示。

    trigger:
    - main
    
    pool:
      vmImage: ubuntu-latest
    
    variables:
      imageName: azure-pipelines-canary-k8s
      dockerRegistryServiceConnection: dockerRegistryServiceConnectionName #replace with name of your Docker registry service connection
      imageRepository: 'azure-pipelines-canary-k8s'
      containerRegistry: example.azurecr.io #replace with the name of your container registry, Should be in the format 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
    
  7. 在 YAML 文件末尾添加一个阶段以部署 Canary 版本。

    - stage: DeployCanary
      displayName: Deploy canary
      dependsOn: Build
      condition: succeeded()
    
      jobs:
      - deployment: Deploycanary
        displayName: Deploy canary
        pool:
          vmImage: ubuntu-latest
        environment: 'akscanary.canarydemo'
        strategy:
          runOnce:
            deploy:
              steps:
              - task: KubernetesManifest@0
                displayName: Create imagePullSecret
                inputs:
                  action: createSecret
                  secretName: azure-pipelines-canary-k8s
                  dockerRegistryEndpoint: azure-pipelines-canary-k8s
    
              - task: KubernetesManifest@0
                displayName: Deploy to Kubernetes cluster
                inputs:
                  action: 'deploy'
                  strategy: 'canary'
                  percentage: '25'
                  manifests: |
                    $(Pipeline.Workspace)/manifests/deployment.yml
                    $(Pipeline.Workspace)/manifests/service.yml
                  containers: '$(containerRegistry)/$(imageRepository):$(tag)'
                  imagePullSecrets: azure-pipelines-canary-k8s
    
              - task: KubernetesManifest@0
                displayName: Deploy Forbio and ServiceMonitor
                inputs:
                  action: 'deploy'
                  manifests: |
                    $(Pipeline.Workspace)/misc/*
    
  8. 通过直接提交到主分支来保存管道。 此提交应已成功运行管道。

手动干预提升或拒绝 Canary

可以使用 YAML 或经典进行手动干预。

  1. 转到“管道”>“环境”>“新建环境”。

  2. 配置新环境。

    • 名称:akspromote
    • 资源:选择“Kubernetes”。
  3. 选择“下一步”,并按如下所示配置 Kubernetes 资源:

    • 提供商:Azure Kubernetes 服务
    • Azure 订阅:选择保留 Kubernetes 群集的订阅。
    • 群集:选择你的群集。
    • 命名空间:选择之前创建的命名空间 canarydemo。
  4. 选择“验证和创建”。

  5. 从环境列表中选择新的 akspromote 环境。

  6. 选择“审批和检查”>“批准”。 然后选择省略号图标(三个点)。

  7. 按如下所示配置审批:

    • 审批者:添加自己的用户帐户。
    • 高级:确保选中“允许审批者批准自己的运行”框。
  8. 选择“创建” 。

  9. 转到“管道”,然后选择创建的管道。 然后选择“编辑”。

  10. 在 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.canarydemo'
        strategy:
          runOnce:
            deploy:
              steps:            
              - task: KubernetesManifest@0
                displayName: promote canary
                inputs:
                  action: 'promote'
                  strategy: 'canary'
                  manifests: '$(Pipeline.Workspace)/manifests/*'
                  containers: '$(containerRegistry)/$(imageRepository):$(tag)'
                  imagePullSecrets: '$(imagePullSecret)'
    
  11. 在 YAML 文件末尾添加另一个阶段 RejectCanary 以回滚更改。

    - stage: RejectCanary
      displayName: Reject canary
      dependsOn: PromoteRejectCanary
      condition: failed()
    
      jobs:
      - deployment: RejectCanary
        displayName: Reject Canary
        pool: 
          vmImage: ubuntu-latest
        environment: 'akscanary.canarydemo'
        strategy:
          runOnce:
            deploy:
              steps:            
              - task: KubernetesManifest@0
                displayName: reject canary
                inputs:
                  action: 'reject'
                  strategy: 'canary'
                  manifests: '$(Pipeline.Workspace)/manifests/*'
    
  12. 选择“保存”保存 YAML 管道,然后将其直接提交到主分支。

部署稳定版本

可以使用 YAML 或经典部署稳定版本。

对于管道的首次运行,群集中不存在工作负载的稳定版本及其基线或 Canary 版本。 部署稳定版本:

  1. 在 app/app.py 中,将 success_rate = 5 更改为 success_rate = 10。 此更改会触发管道,导致生成映像并将其推送到容器注册表。 它还会触发 DeployCanary 阶段。
  2. 由于你在环境中配置了审批 akspromote ,因此发布在运行该阶段之前会等待。
  3. 在运行摘要中,选择“审阅”>“批准”。 这会将工作负载的稳定版本(manifests/deployment.yml 中的 sampleapp 部署)部署到命名空间。

启动 Canary 工作流

群集中现在存在稳定版本的工作负载 sampleapp。 接下来,对模拟应用程序进行以下更改:

在 app/app.py 中,将 success_rate = 10 更改为 success_rate = 20

此更改会触发生成管道,从而生成映像并将其推送到容器注册表。 此过程反过来会触发发布管道,并开始“部署 Canary”阶段。

模拟请求

在开发计算机上运行以下命令,并使其保持运行状态,以在 sampleapp 服务中发送恒定的请求流。 sampleapp 将请求路由到由稳定 sampleapp 部署启动的 Pod,以及由 sampleapp-baselinesampleapp-canary 部署启动的 Pod。 为 sampleapp 指定的选择器适用于所有这些 Pod。

FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio /usr/bin/fortio -- load -allow-initial-errors -t 0 http://sampleapp:8080/

设置 Grafana 仪表板

  1. 在本地开发计算机上运行以下端口转发命令,以便能够访问 Grafana。

    kubectl port-forward svc/sampleapp-grafana 3000:80
    
  2. 在浏览器中,打开以下 URL。

    http://localhost:3000/login
    
  3. 当系统提示输入凭据时,除非在 adminPassword Helm 图表安装过程中替代了 prometheus-operator 值,否则可以使用以下值:

    • 用户名:admin
    • 密码:prom-operator
  4. 从左侧菜单中,选择“+”>“仪表板”>“图形”。+

  5. 在新添加的面板上选择任意位置,然后键入 e 以编辑面板。

  6. 在“指标”选项卡上,输入以下查询:

    rate(requests_total{pod=~"sampleapp-.*", custom_status="good"}[1m])
    
  7. 在“常规”选项卡上,将此面板的名称更改为“所有 sampleapp Pod”。

  8. 在页面顶部的概述栏中,将持续时间范围更改为“过去 5 分钟”或“过去 15 分钟”。

  9. 若要保存此面板,请选择概述栏中的保存图标。

  10. 前面的面板将可视化所有变体的成功率指标。 其中包括稳定(来自 sampleapp 部署)、基线(来自 sampleapp-baseline 部署)和 Canary(来自 sampleapp-canary 部署)。 可以通过使用以下配置添加另一个面板来仅可视化基线和 Canary 指标:

    • 在“常规”选项卡上,为“标题”选择“sampleapp 基线和 Canary”。
    • 在“指标”选项卡上,使用以下查询:
    rate(requests_total{pod=~"sampleapp-baseline-.*|sampleapp-canary-.*", custom_status="good"}[1m])
    

    注意

    基线和 Canary 指标面板将仅在某些条件下提供指标以用于比较。 这些条件是“部署 Canary”阶段成功完成,并且“提升/拒绝 Canary”阶段正在等待手动干预。

    提示

    设置 Grafana 仪表板注释,以直观地描述“部署 Canary”和“提升/拒绝 Canary”的阶段完成事件。 这有助于你知道何时开始比较基线与 Canary,以及提升或拒绝 Canary 分别何时完成。

比较基线与 Canary

  1. 此时,“部署 Canary”阶段已成功完成(基于 success_rate10 更改为 20)。 “提升/拒绝 Canary”阶段正在等待手动干预。 现在,可以在 Grafana 仪表板中比较基线和 Canary 变体的成功率(由 custom_status=good 确定)。 该属性应与下面类似:

    显示基线和 Canary 指标的比较的屏幕截图。

  2. 观察结果显示 Canary 的成功率较高,由此提升 Canary。 在手动干预任务中选择“继续”。