基于Argo Rollouts,Kubernetes的渐进式交付实践

关于渐进式交付

什么是渐进式交付

渐进式交付是以受控和渐进方式发布/更新的过程,采用渐进的方式从而降低发布风险,通常结合自动化和度量指标分析来驱动新应用的自动升级或自动回滚。

渐进式交付建立在持续集成和持续交付 (CI/CD) 的核心原则之上,通过在交付阶段,通过将新版本的曝光限制给一部分用户,观察和分析正确的行为,然后逐渐增加对更广泛的受众的曝光,同时不断验证正确性来实现。渐进式交付提供了安全措施和控制手段,以降低不断将新版本推送到生产环境的风险。

渐进式交付部署策略

渐进式交付的部署策略主要区别于全量发布与一次性发布,包含以下特点:

  • 增量的发布过程:将一次发布分成多个批次,并对每个批次的发布、停止进行控制。
  • 应用与流量双维度的灰度:如Canary Releases(金丝雀发布)、A/B Testing、Blue-green Deployments(蓝绿部署)。
  • 部署阶段可验证性:发布的每个批次,可以验证发布的正确性以及是否符合发布预期。

Blue-green Deployments(蓝绿部署)

使用Blue-green Deployments,在只存在旧版本应用程序的情况下,对新版本应用程序进行部署,此时生产环境存在新旧两个版本的应用程序,此期间,只有旧版本的应用程序会收到生产流量,允许开发人员在将实时流量切换到新版本之前针对新版本应用程序进行测试与度量指标分析,并根据测试结果决定是否进行版本的切换。

  • Phase 1
flowchart LR User --> Loadbalancer Loadbalancer --> |Network Traffic|Application-Version-1
  • Phase 2
flowchart LR User --> Loadbalancer Loadbalancer --> |Network Traffic|Application-Version-1 Loadbalancer -.- Application-Version-2
  • Phase 3
flowchart LR User --> Loadbalancer Loadbalancer -.- Application-Version-1 Loadbalancer --> |Network Traffic|Application-Version-2
  • Phase 4
flowchart LR User --> Loadbalancer Loadbalancer --> |Network Traffic|Application-Version-2

Canary Releases(金丝雀发布)

使用Canary Releases,在只存在旧版本应用程序的情况下,对新版本应用程序进行部署,会将一部分流量路由给新版本应用程序,同时将其余流量提供给旧版本应用程序;当新版本应用程序验证无误后,则新版本应用程序以逐步取代旧版本。一般来说,LoadBalancer可以为Canary Releases提供更多的流量路由策略。

  • Phase 1
flowchart LR User --> Loadbalancer Loadbalancer --> |"Network Traffic(100%)"|Application-Version-1
  • Phase 2
flowchart LR User --> Loadbalancer Loadbalancer --> |"Network Traffic(90%)"|Application-Version-1 Loadbalancer -.- |"Network Traffic(10%)"|Application-Version-2
  • Phase 3
flowchart LR User --> Loadbalancer Loadbalancer --> |"Network Traffic(60%)"|Application-Version-1 Loadbalancer -.- |"Network Traffic(40%)"|Application-Version-2
  • Phase 4
flowchart LR User --> Loadbalancer Loadbalancer --> |"Network Traffic(100%)"|Application-Version-2

A/B Testing

A/B Testing一般是用于比较、评估某一功能不同版本以确定哪个版本的表现、效果更好。A/B Testing一般基于LoadBalancer的Dynamic Routing进行实现,将用户分为A/B两组,A(control group)为控制组继续使用旧版本功能,B(treatment group)为治疗组,根据特定的用户标签进行路由,使用新版本功能。

flowchart LR User --> Loadbalancer Loadbalancer --> Application-Version-1 Loadbalancer --> |"Dynamic Routing Beta User"|Application-Version-2

渐进式交付与Kubernetes Workloads

Kubernetes Workloads是指在Kubernetes上运行的应用程序,Kubernetes提供的Workloads一般存在于Group apps/batch下,如常见的Deployment、StatefulSet、DaemonSet、CronJob; 通过对应的控制器,达到对一组Pod运行状态的管控。

其中Deployment与StatefulSet都是采用滚动升级的策略进行发布;

  • Deployment滚动升级策略:提供了maxUnavailable(发布过程最大不可用数量)和maxSurge(发布过程可超过replicas的最大弹性数量)参数进行滚动升级的控制。
  • StatefulSet滚动升级策略:提供了partition参数控制需要更新的Pod(ordinal>=partition)和maxUnavailable(发布过程中最大不可用数量)参数进行滚动升级的控制。

可以看到,Kubernetes提供的Workloads功能类似于流式全量发布、流式分批发布,对渐进式交付的支持不足,缺少了流量发布、进度管控的功能,这也衍生了一些支持渐进式交付的Kubernetes Controller,如Argo Rollouts、Flagger。Argo Rollouts与Flagger也代表了主流的两种Kubernetes渐进式交付控制器的实现方式:

  • Argo Rollouts:Argo Rollouts 设计了CRD Rollout 与 Rollout Controller,通过重写了原有Deployment的功能实现对Rollout功能进行了扩展。

image

  • Flagger:Flagger 则是在现有的Deployment上进行扩展,设计了CRD Rollout 与 Rollout Controller,在Kubernetes Deployment部署时进行功能的增强。

image

Argo Rollouts的优势是Workloads已经内置了Rollouts的能力,配置与实现简单,定制化能力强;缺点是对于一些社区方案,基于标准Kubernetes Workloads与系统存量的Deployment需要进行改造适配。 Flagger 的优势是支持标准Kubernetes Workloads,与社区方案兼容性强,缺点是发布过程中存在Double Workloads 资源的问题,发布会先升级用户部署的 Workloads,再升级 Primary(实际运行的),在这过程中需要准备双倍Pod资源的消耗。

Argo Rollouts

Argo Rollouts概述

Argo Rollouts由一个Kubernetes Controller与一组Kubernetes CRD组成,为Kubernetes提供更高级部署功能,如蓝绿(blue-green)、金丝雀(canary)、金丝雀分析(canary analysis)、实验(experimentation)和渐进式交付功能。

Argo Rollouts Controller特性

  • 蓝绿(blue-green)/金丝雀(canary)更新策略
  • 细粒度加权流量拆分
  • 自动回滚、自动推进,也支持手动处理
  • 支持多种Metrics指标集成以进行蓝绿和金丝雀分析
  • 与Ingress、Service Mesh集成,实现高级流量路由

Argo Rollouts Controller工作原理

与Kubernetes Deployment类似,Argo Rollouts Controller管理ReplicaSets的创建、缩放和删除。这些 ReplicaSet 由spec.templateRollout 资源中的字段定义,与Deployment 使用相同的pod模板。

spec.template更改时,会向Argo Rollouts Controller发出信号通知,告知将引入新的 ReplicaSet。Argo Rollouts Controller将使用该spec.strategy字段中定义的策略来确定旧ReplicaSet到新ReplicaSet的发布方式。一旦新的ReplicaSet被放大(可选择通过Analysis进行控制),控制器会将其标记为“稳定”。

如果在spec.template从稳定的ReplicaSet过渡到新的ReplicaSet的过程中发生了变化(发布过程中更改了应用版本信息),那么新ReplicaSet将被缩小,Argo Rollouts Controller会尝试反映更新spec.template字段的ReplicaSet。

Argo Rollouts 架构

image

  • Rollout Controller

核心Controller,监控集群事件并在Rollout资源类型发生变更时做出反应,读取部署rollout CRD资源信息,将集群置于部署定义中描述的相同状态。 而且,Argo Rollouts 不会篡改或响应正常部署资源上发生的任何更改。

  • Rollout Resouces

Rollout Resouces是Argo Rollouts引入和管理的Kubernetes CRD资源。它与原生Kubernetes Deployment资源兼容,通过额外的字段提供了高级控制部署方法(如​​金丝雀和蓝/绿)。

  • Ingress/Service

Argo Rollouts可以进行灵活的网络配置,在rollouts期间拥有不同的Service(如仅适用于新版本、仅适用于旧版本、新旧版本结合类型的Service)。专门针对Canary部署,Argo Rollouts支持多种服务网格和入口解决方案,例如用于以特定百分比拆分流量,而不是基于pod数量的简单平衡。

  • Analysis/AnalysisRun

Analysis与AnalysisRun是Argo Rollouts引入和管理的Kubernetes CRD资源。Analysis将Rollout连接到指标提供程序(如prometheus),并为指标定义阈值,阈值将决定Rollout是否成功以及后续的操作(继续发布、回滚,暂停发布)。 Analysis是查询指标的模板,AnalysisRun才是附加到Rollout的资源,因此可以在特定Rollout上或在集群上对Analysis进行全局定义,以供多个rollout共享。

安装Argo Rollouts

  • 安装Argo Rollouts
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
  • 安装Argo Rollouts Kubectl 插件 (Linux platform)
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
kubectl argo rollouts version
  • Argo Rollouts Dashboard

在包含Rollouts资源对象的命名空间中运行kubectl argo rollouts dashboard命令,访问本地3100端口进行查看。

基于Argo Rollouts的渐进式交付实践

基本使用

这里我们使用kustomize结合Argo Rollouts进行演示,因此需要使用transformer configs对kustomize进行扩展以理解CRD对象。具体配置可以参考:https://argoproj.github.io/argo-rollouts/features/kustomize/与项目/.kubernetes/overlays/manual。

  • Rollout部署
# rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: saken-manual
spec:
  replicas: 5
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}
      - setWeight: 40
      - pause: {duration: 10}
      - setWeight: 60
      - pause: {duration: 10}
      - setWeight: 80
      - pause: {duration: 10}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: saken-manual
  template:
    metadata:
      labels:
        app:  saken-manual
    spec:
      containers:
      - name:  saken-manual
        image:  ccr.ccs.tencentyun.com/shovel/shovel-rollouts:0.0.2
        resources:
          requests:
            cpu: 10m
            memory: 32Mi
        ports:
        - containerPort:  9099
          name:  saken-manual
# service
apiVersion: v1
kind: Service
metadata:
  name: saken-manual-lb
  namespace: default
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/scrape: "true"
spec:
  selector:
    app: saken-manual
  type: ClusterIP
  sessionAffinity: None
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  ports:
  - name: saken-manual
    protocol: TCP
    port: 80
    targetPort: 9099
# kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: staging
resources:
- base-rollout.yaml
- base-service.yaml
images:
- name: ccr.ccs.tencentyun.com/shovel/shovel-rollouts
  newTag: 0.0.3         

这里我们kustomization配置了一个Rollout与对应的Service,Rollout使用Canary部署策略,将20%的流量部署到Canary后,Rollout进入Paused状态, 进行手动promote后,每间隔10s进行逐步地升级。

通过如下命令进行部署,注意所有的Rollout的初始创建,因为没用任何升级,所以会立即将副本扩展到100%(跳过canary、analyse等)。

kubectl apply -k .kubernetes/overlays/manual
kubectl argo rollouts get rollout saken-manual -n staging --watch

image

  • 更新Rollout

使用kustomize edit命令更新或者对kustomization文件进行更新,通过如下命令进行部署,可以看到canary部署策略已经生效了,此时已经将20%的流量部署到cancary,并进入了Paused状态。

kubectl apply -k .kubernetes/overlays/manual
kubectl argo rollouts get rollout saken-manual -n staging --watch

image

  • Promote/Abort Rollout

Rollout处于Paused状态后,可以手动进行Promote/Abort操作,如下命令所示:

# Promoting a Rollout
kubectl argo rollouts promote saken-manual -n staging
# Aborting a Rollout
# kubectl argo rollouts abort saken-manual -n staging
kubectl argo rollouts get rollout saken-manual -n staging --watch

image

Argo Rollout Analysis

Argo Rollouts 提供了多种执行分析(Analysis)的方法来推动渐进式交付,为此也定义了如下CRD:

CRD Description
AnalysisTemplate Analysis模板,定义了如何执行canary分析,如执行的指标、频率、成功或失败的阈值。AnalysisTemplates还支持通过输入值进行参数化。
ClusterAnalysisTemplate 类似于AnalysisTemplate,但不限于其namespace,可以被集群中的任何Rollout使用。
AnalysisRun 实例化AnalysisTemplate。AnalysisRuns类似于Jobs,最终都会完成。已完成的运行被视为成功、失败或不确定,运行结果会影响Rollout的推进是否继续、中止或暂停。
Experiment 允许用户临时运行一个或多个ReplicaSet。除此之外,Experiment还可以与ReplicaSet一起启动AnalysisRuns。通常,这些AnalysisRun用于确认新的ReplicaSet是否按预期运行。

Argo Rollout Analysis 支持多种分析模式:

  • Background Analysis:后台分析,canary部署步骤执行时,可以在后台进行AnalysisRun分析,以分析结果决定canary rollout的后续行为(推进/终止)。
  • Inline Analysis:内联分析,canary部署步骤执行时,在到达某一阶段时启动AnalysisRun,并在运行完成之前阻止其推进,以分析结果决定canary rollout的后续行为(推进/终止)。
  • Analysis with Multiple Templates:多模板分析,在构建AnalysisRun时,Rollout可以引用多个 AnalysisTemplate,允许从多个 AnalysisTemplate 进行分析。
  • Analysis Template Arguments:AnalysisTemplates可以声明一组可以由Rollouts传递的参数。
  • BlueGreen Pre Promotion Analysis/BlueGreen Post Promotion Analysis:使用BlueGreen策略的Rollout可以在流量切换到新版本前后启动AnalysisRun进行分析,根据分析结果决定是否切换流量。

更多内容可以参考官方文档:https://argoproj.github.io/argo-rollouts/features/analysis/

Canary-Analysis(金丝雀分析)

这里使用Background Analysis结合Canary部署,如下所示,配置了一个Rollout,与对应的AnalysisTemplate,Service。rollout每10分钟逐渐增加canary权重20%,直到达到100%。后台,AnalysisRun基于AnalysisTemplate,该分析模板以一下方式运行:

  • 从Prometheus服务获取指标信息,以五分钟为间隔,测量五分钟内对应服务的HTTP成功率,根据给定的度量阈值(successCondition=90%)以及失败次数的阈值(failureLimit=2)计算得出测量结果,如测量成功率为小于90%并且存在两个这样的测量结果,则被认为分析失败。
  • 失败的分析结果会使Rollout中止,将Canary权重设置回零,并且Rollout将被视为Degraded(降级)。
  • 如果Rollout完成其所有Canary步骤,则认为Rollout成功部署,控制器将停止运行分析。
  • 这里使用了Rollout的analysis-startingStep配置为2,即会延迟执行AnalysisRun,直到Canary达到40%。
# Rollout
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: saken-canary
spec:
  replicas: 5
  strategy:
    canary:
      analysis:
        templates:
        - templateName: canary-success-rate
        startingStep: 2
        args:
        - name: service-name
          value: saken-canary-lb
      steps:
      - setWeight: 20
      - pause: {duration: 10m}
      - setWeight: 40
      - pause: {duration: 10m}
      - setWeight: 60
      - pause: {duration: 10m}
      - setWeight: 80
      - pause: {duration: 10m}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: saken-canary
  template:
    metadata:
      labels:
        app:  saken-canary
    spec:
      containers:
      - name:  saken-canary
        image:  ccr.ccs.tencentyun.com/shovel/shovel-rollouts:0.0.2
        resources:
          requests:
            cpu: 10m
            memory: 32Mi
        ports:
        - containerPort:  9099
          name:  saken-canary
# Service
apiVersion: v1
kind: Service
metadata:
  name: saken-canary-lb
  namespace: default
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/scrape: "true"
spec:
  selector:
    app: saken-canary
  type: ClusterIP
  sessionAffinity: None
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  ports:
  - name: saken-canary
    protocol: TCP
    port: 80
    targetPort: 9099
# AnalysisTemplate
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: canary-success-rate
spec:
  args:
  - name: service-name
  metrics:
  - name: canary-success-rate
    interval: 5m
    successCondition: result[0] >= 0.9
    failureLimit: 2
    provider:
      prometheus:
        address: http://prometheus.kube-ops.svc.cluster.local
        query: |
          sum(irate(gin_metric_handler_http_responses_status_total{code = "200",kubernetes_name = "{{args.service-name}}"}[5m])) /
          sum(irate(gin_metric_handler_http_responses_status_total{kubernetes_name = "{{args.service-name}}"}[5m]))
# Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- canary-rollout.yaml
- canary-service.yaml
- canary-success-rate.yaml

namespace: staging
images:
- name: ccr.ccs.tencentyun.com/shovel/shovel-rollouts
  newTag: 0.0.2

Blue-Green-Analysis(蓝绿分析)

这里我们使用BlueGreen Pre Promotion结合BlueGreen部署,如下所示,配置了一个Rollout,与对应的AnalysisTemplate,Service与Preview-Service。Rollout对新版本应用进行BlueGreen部署时,会创建一个新的ReplicaSet部署新版本应用,并修改Preview-Service使其selector的目标为我们创建的新版本应用。此时,AnalysisRun基于AnalysisTemplate,对Preview-Service进行冒烟测试并阻塞Blue-Green切换,直到AnalysisRun完成测试,AnalysisRun的测试结果决定了Rollout 是切换流量或者还是完全中止Rollout。

# Rollout
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: saken-bluegreen
spec:
  replicas: 3
  revisionHistoryLimit: 1
  strategy:
    blueGreen:
      activeService: saken-bluegreen-lb
      previewService: saken-bluegreen-preview-lb
      prePromotionAnalysis:
        templates:
        - templateName: bluegreen-smoke-test
        args:
        - name: service-name
          value: saken-bluegreen-preview-lb
  selector:
    matchLabels:
      app: saken-bluegreen
  template:
    metadata:
      labels:
        app:  saken-bluegreen
    spec:
      containers:
      - name:  saken-bluegreen
        image:  ccr.ccs.tencentyun.com/shovel/shovel-rollouts:0.0.2
        resources:
          requests:
            cpu: 10m
            memory: 32Mi
        ports:
        - containerPort:  9099
          name:  saken-bluegreen
# Service
apiVersion: v1
kind: Service
metadata:
  name: saken-bluegreen-lb
  namespace: default
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/scrape: "true"
spec:
  selector:
    app: saken-bluegreen
  type: ClusterIP
  sessionAffinity: None
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  ports:
  - name: saken-bluegreen
    protocol: TCP
    port: 80
    targetPort: 9099
# Preview-Service
apiVersion: v1
kind: Service
metadata:
  name: saken-bluegreen-preview-lb
  namespace: default
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/scrape: "true"
spec:
  selector:
    app: saken-bluegreen
  type: ClusterIP
  sessionAffinity: None
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  ports:
  - name: saken-bluegreen
    protocol: TCP
    port: 80
    targetPort: 9099
# AnalysisTemplate
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: bluegreen-smoke-test
spec:
  args:
  - name: service-name
  metrics:
  - name: bluegreen-smoke-test
    provider:
      job:
        spec:
          backoffLimit: 1
          template:
            spec:
              containers:
              - name: bluegreen-smoke-test
                image: alpine
                command: 
                - /bin/sh
                - -c
                - |
                - echo "mock smoke test to serivce ..."
              restartPolicy: Never
# Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- bluegreen-rollout.yaml
- bluegreen-service.yaml
- bluegreen-service-preview.yaml
- bluegreen-smoke-test.yaml

namespace: staging
images:
- name: ccr.ccs.tencentyun.com/shovel/shovel-rollouts
  newTag: 0.0.2

项目源码

可以在Github上获取到此项目的源码:https://github.com/ChinaLHR/shovel-progressive-delivery-argo-rollouts

参考