Kubernetes 部署的 Canary 部署策略

Azure DevOps Services

Canary 部署策略意味着在稳定的生产版本旁边部署应用程序的新版本。 然后,可以查看 Canary 版本与基线的比较方式,然后再升级或拒绝部署。

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

先决条件

代码示例

在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 Pod 的请求流 (稳定、基线和 canary) 。

备注

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

安装 prometheus-operator

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

helm install --name sampleapp stable/prometheus-operator

创建服务连接

  1. 转到Project设置>Pipelines>服务连接
  2. 创建与容器注册表关联的 Docker 注册表服务连接 。 将其命名 为 azure-pipelines-canary-k8s
  3. 为要部署到的 Kubernetes 群集和命名空间创建 Kubernetes 服务连接 。 将其命名 为 azure-pipelines-canary-k8s

设置持续集成

  1. 转到 Pipelines>Create Pipeline,然后选择存储库。

  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. 转到 Pipelines>EnvironmentsCreate>环境

  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. 转到 Pipelines>EnvironmentsNew>环境

  2. 配置新环境。

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

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

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

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

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

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

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

  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 = 5success_rate = 10. 此更改触发管道,导致映像生成并将映像推送到容器注册表。 它还将触发阶段 DeployCanary
  2. 由于你在环境中配置了审批 akspromote ,因此发布将在运行该阶段之前等待。
  3. 在运行摘要中,选择 ReviewApprove>。 这样会将 (清单/deployment.yml) 中的部署的工作负荷稳定版本sampleapp部署到命名空间。

启动 canary 工作流

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

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

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

模拟请求

在开发计算机上运行以下命令,并使其保持运行状态,以在服务中 sampleapp 发送持续的请求流。 sampleapp通过稳定sampleapp部署将请求路由到 Pod,并将请求路由到由部署sampleapp-canary启动的 sampleapp-baseline 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 重写了该值,否则可以使用以下值:

    • 用户名:管理员
    • password:prom-operator
  4. 从左侧菜单中,选择“+>仪表板>Graph

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

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

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

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

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

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

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

    注意

    基线指标和金丝雀指标面板仅在某些条件下具有可用于比较的指标。 这些条件是在 部署 canary 阶段成功完成时, 升级/拒绝 Canary 阶段正在等待手动干预。

    提示

    为 Grafana 仪表板设置批注,以直观地描绘部署 CanaryPromote/reject canary 的阶段完成事件。 这有助于你了解何时开始将基线与 Canary 进行比较,以及何时分别完成了 Canary 的促销或拒绝。

比较基线和金丝雀

  1. 此时,部署 canary 阶段已根据从) 1020 更改为success_rate (成功完成 (。 升级/拒绝 Canary 阶段正在等待手动干预。 现在可以比较由 Grafana 仪表板中基线和金丝雀变体) 确定 custom_status=good 的成功率 (。 它应类似于以下内容:

    Screenshot that shows a comparison of baseline and canary metrics.

  2. 根据对金丝雀的成功率较高的观察,促进金丝雀。 在手动干预任务中选择 “恢复 ”。