基于 Flagger Operator 的 Traefik 金丝雀部署

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
简介: 在日常的项目开发过程中时,我们时常会面临服务变更的挑战,为不影响用户体验,我们往往尽可能需要规避服务不可用风险。因此,持续交付便应运而生,其被接受为一种企业软件实践,并且是完善的持续集成原则的自然演变。然而,持续部署仍然非常罕见,这可能是由于管理的复杂性以及担心部署失败会影响系统可用性。在整个持续交付体系中,金丝雀发布,或许是最为经典的一个场景,基于此,我们能够很快发现不健康和“有问题”的服务,并且可以毫不费力地回滚到上一个的版本。

    在日常的项目开发过程中时,我们时常会面临服务变更的挑战,为不影响用户体验,我们往往尽可能需要规避服务不可用风险。因此,持续交付便应运而生,其被接受为一种企业软件实践,并且是完善的持续集成原则的自然演变。然而,持续部署仍然非常罕见,这可能是由于管理的复杂性以及担心部署失败会影响系统可用性。在整个持续交付体系中,金丝雀发布,或许是最为经典的一个场景,基于此,我们能够很快发现不健康和“有问题”的服务,并且可以毫不费力地回滚到上一个的版本。

金丝雀部署

    什么是金丝雀部署?也称“灰度部署”,通常来讲,在原有版本可用的情况下,同时部署一个新版本应用作为“金丝雀”,测试新版本的性能和表现,在保障整体系统稳定的前提下,尽早发现、及时调整。

    金丝雀部署,其并非黑即白的部署方式,它能够缓慢的将特定百分比的流量引导至一小部分用户,若验证没有问题后,再推广到全部用户,并逐步淘汰旧版本,以降低生产环境引入新功能带来的风险。针对金丝雀部署工作流,其往往主要涉及以下阶段:

    Step 1:将流量从待部署节点移出,更新该节点服务至待发布状态,此时,节点称为“金丝雀节点”。

    Step 2:依据不同的场景策略,将流量引入至金丝雀节点。流量引入策略可以依据实际的业务场景情况而定,例如,随机样本策略(随机引入)、狗粮策略(就是内部用户或员工先尝鲜)、区域策略(不同区域用户使用不同版本)、用户特征策略(这种比较复杂,需要根据用户个人资料和特征进行分流)。

    Step 3:金丝雀节点验证通过后,选取更多的节点作为金丝雀节点,然后重复步骤一和步骤二,直到所有节点全部更新至最新状态。

    在本篇文章中,笔者将选择 Traefik 与 Flagger 相结合使用,以探索应用程序/服务在发布新版本时所拥有的一些潜在可能性。

    我们先来介绍下 Flagger ,Flagger 是一个云原生计算基金会项目,是 GitOps 工具 Flux 系列的一部分。作为一种渐进式交付工具,Flagger 可自动执行在 Kubernetes 上运行的应用程序的发布过程。它通过在衡量指标和运行一致性测试的同时逐渐将流量转移到新版本来降低在生产中引入新软件版本的风险。

    Flagger 可以针对以下部署策略运行自动化的应用程序分析、升级和回滚:

  • Canary(渐进式流量转移)
  • A/B 测试(HTTP 标头和 cookie 流量路由)
  • 蓝/绿(流量开关或镜像)

   对于 Canary 部署和 A/B 测试,我们需要一个第 7 层流量管理解决方案,例如服务网格(Istio、Linkerd、App Mesh)或入口控制器(Contour、NGINX、Gloo)。而对于蓝/绿部署,则不需要服务网格或入口控制器。

    Flagger 使用服务网格(App Mesh、Istio、Linkerd、Open Service Mesh)或入口控制器(Contour、Gloo、NGINX、Skipper、Traefik)实现了多种部署策略(Canary 发布、A/B 测试、蓝/绿镜像) 用于流量路由。对于发布分析,Flagger 可以查询 Prometheus、Datadog、New Relic、CloudWatch 或 Graphite,并使用 Slack、MS Teams、Discord 和 Rocket 进行警报。

    Flagger 可以使用 Kubernetes 自定义资源进行配置,并且兼容任何为 Kubernetes 制作的 CI/CD 解决方案。由于 Flagger 是声明性的并对 Kubernetes 事件做出反应,因此它可以与 Flux、JenkinsX、Carvel、Argo 等工具一起用于 GitOps 管道中。

    除此之外,Flagger 同时也会跟踪 Kubernetes 部署引用的 ConfigMap 和 Secrets,并在这些对象中的任何一个发生更改时触发金丝雀分析。在生产中提升工作负载时,代码(容器映像)和配置(配置映射和机密)都会同步。

前置条件:Flagger 需要依赖 Kubernetes 集群 v1.16 或更高版本以及 Traefik v2.3 或更高版本。

    接下来,我们用 Helm (此处为 v3 版本)部署 Traefik ,具体如下所示:


[administrator@JavaLangOutOfMemory ~ ] % helm repo add traefik https://helm.traefik.io/traefik
[administrator@JavaLangOutOfMemory ~ ] % kubectl create ns traefik
[administrator@JavaLangOutOfMemory ~ ] % cat <<EOF | helm upgrade -i traefik traefik/traefik --namespace traefik -f -
deployment:
  podAnnotations:
    prometheus.io/port: "9100"
    prometheus.io/scrape: "true"
    prometheus.io/path: "/metrics"
metrics:
  prometheus:
    entryPoint: metrics
EOF

    在与 Traefik 相同的命名空间中安装 Flagger 和 Prometheus 等支撑组件,具体如下所示:


[administrator@JavaLangOutOfMemory ~ ]% helm repo add flagger https://flagger.app
[administrator@JavaLangOutOfMemory ~ ]% helm upgrade -i flagger flagger/flagger \
--namespace traefik \
--set prometheus.install=true \
--set meshProvider=traefik

    Flagger 采用 Kubernetes 部署和可选的水平 Pod 自动缩放器 (HPA),然后创建一系列对象(Kubernetes 部署、ClusterIP 服务和 TraefikService)。这些对象将应用程序暴露在集群之外并驱动金丝雀分析和推广。

    接下来,我们来创建一个测试使用的命名空间,具体如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl create ns test
[administrator@JavaLangOutOfMemory ~ ]% kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main

    然后,部署负载测试服务以在金丝雀分析期间生成流量:


[administrator@JavaLangOutOfMemory ~ ]% helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=test

    创建引用由 Flagger 生成的 TraefikService 的 Traefik IngressRoute(将 app.example.com 替换为自己的域),其 Demo 文件如下所示:


apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: podinfo
  namespace: test
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`app.example.com`)
      kind: Rule
      services:
        - name: podinfo
          kind: TraefikService
          port: 80

    将上述资源另存为 podinfo-ingressroute.yaml 然后执行资源配置操作,具体如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl apply -f ./podinfo-ingressroute.yaml

    此处,我们创建金丝雀自定义资源(将 app.example.com 替换为自己的域),具体如下所示:


apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: traefik
  # deployment reference
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # HPA reference (optional)
  autoscalerRef:
    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    name: podinfo
  # the maximum time in seconds for the canary deployment
  # to make progress before it is rollback (default 600s)
  progressDeadlineSeconds: 60
  service:
    # ClusterIP port number
    port: 80
    # container port number or name
    targetPort: 9898
  analysis:
    # schedule interval (default 60s)
    interval: 10s
    # max number of failed metric checks before rollback
    threshold: 10
    # max traffic percentage routed to canary
    # percentage (0-100)
    maxWeight: 50
    # canary increment step
    # percentage (0-100)
    stepWeight: 5
    # Traefik Prometheus checks
    metrics:
    - name: request-success-rate
      interval: 1m
      # minimum req success rate (non 5xx responses)
      # percentage (0-100)
      thresholdRange:
        min: 99
    - name: request-duration
      interval: 1m
      # maximum req duration P99
      # milliseconds
      thresholdRange:
        max: 500
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 10s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary.test/token | grep token"
      - name: load-test
        type: rollout
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          type: cmd
          cmd: "hey -z 10m -q 10 -c 2 -host app.example.com http://traefik.traefik"
          logCmdOutput: "true"

[administrator@JavaLangOutOfMemory~ ]% kubectl apply -f ./podinfo-canary.yaml

    几秒钟后,Flager 将创建金丝雀对象:


# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated 
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
traefikservice.traefik.containo.us/podinfo

    Flagger 实施了一个控制循环,在测量 HTTP 请求成功率、请求平均持续时间和 Pod 健康状况等关键性能指标的同时,逐渐将流量转移至金丝雀。根据对相关指标的分析,发布或中止金丝雀部署,并将分析结果发布到相关平台。

    通过更新容器镜像触发金丝雀部署,具体操作方法如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6

    Flagger 检测到部署内容已更改并开始新的部署:


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n test describe canary/podinfo
Status:
  Canary Weight:         0
  Failed Checks:         0
  Phase:                 Succeeded
Events:
 New revision detected! Scaling up podinfo.test
 Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
 Pre-rollout check acceptance-test passed
 Advance podinfo.test canary weight 5
 Advance podinfo.test canary weight 10
 Advance podinfo.test canary weight 15
 Advance podinfo.test canary weight 20
 Advance podinfo.test canary weight 25
 Advance podinfo.test canary weight 30
 Advance podinfo.test canary weight 35
 Advance podinfo.test canary weight 40
 Advance podinfo.test canary weight 45
 Advance podinfo.test canary weight 50
 Copying podinfo.test template spec to podinfo-primary.test
 Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
 Routing all traffic to primary
 Promotion completed! Scaling down podinfo.test

    备注:如果在 Canary 分析期间对部署应用新更改,Flager 将重新启动分析。

    我们可以通过以下方式监控所有金丝雀部署信息,具体如下所示:


[administrator@JavaLangOutOfMemory ~ ]% watch kubectl get canaries --all-namespaces
NAMESPACE   NAME        STATUS        WEIGHT   LASTTRANSITIONTIME
test        podinfo-2   Progressing   30       2020-08-14T12:32:12Z
test        podinfo     Succeeded     0        2020-08-14T11:23:88Z

    上述我们简要介绍了金丝雀发布的相关理论基础,接下来,我们来看一下基于此场景下的自动回滚。

    在金丝雀分析过程中,我们可以生成 HTTP 500 错误来测试 Flagger 是否暂停并回滚有故障的版本。

    此时,我们进行另一个金丝雀部署,具体操作如下:


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6

    进入 Pod 内,执行相关命令,具体操作如下:


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n test exec -it deploy/flagger-loadtester bash

[administrator@JavaLangOutOfMemory ~ ]% hey -z 1m -c 5 -q 5 http://app.example.com/status/500
[administrator@JavaLangOutOfMemory ~ ]% watch -n 1 curl http://app.example.com/delay/1

    当失败的检查次数达到金丝雀分析所设定的阈值时,流量将路由回主节点,金丝雀缩放为零,并将推出标记为失败。


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n traefik logs deploy/flagger -f | jq .msg
New revision detected! Scaling up podinfo.test
Canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 updated replicas are available
Starting canary analysis for podinfo.test
Pre-rollout check acceptance-test passed
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Advance podinfo.test canary weight 20
Halt podinfo.test advancement success rate 53.42% < 99%
Halt podinfo.test advancement success rate 53.19% < 99%
Halt podinfo.test advancement success rate 48.05% < 99%
Rolling back podinfo.test failed checks threshold reached 3
Canary failed! Scaling down podinfo.test

    Canary 分析可以通过 Prometheus 查询进行扩展。我们创建指标模板并将其应用于集群,具体如下所示:


apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: test
spec:
  provider:
    type: prometheus
    address: http://flagger-prometheus.traefik:9090
  query: |
    sum(
      rate(
        traefik_service_request_duration_seconds_bucket{
          service=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+@kubernetescrd",
          code!="404",
        }[{{ interval }}]
      )
    )
    /
    sum(
      rate(
        traefik_service_request_duration_seconds_bucket{
          service=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+@kubernetescrd",
        }[{{ interval }}]
      )
    ) * 100

    编辑金丝雀分析并添加未找到错误率检查:


analysis:
    metrics:
      - name: "404s percentage"
        templateRef:
          name: not-found-percentage
        thresholdRange:
          max: 5
        interval: 1m

    上述配置通过检查 HTTP 404 req/sec 百分比是否低于总流量的 5% 来验证金丝雀。如果 404s 率达到 5% 阈值,则金丝雀失败。通过更新容器镜像触发金丝雀部署,如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6

    生成 HTTP 500 错误及产生延迟相关命令如下所示:


[administrator@JavaLangOutOfMemory ~ ]%hey -z 1m -c 5 -q 5 http://app.example.com/status/500

[administrator@JavaLangOutOfMemory ~ ]% watch curl http://app.example.com/status/400


[administrator@JavaLangOutOfMemory ~ ]% kubectl -n traefik logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement 404s percentage 6.20 > 5
Halt podinfo.test advancement 404s percentage 6.45 > 5
Halt podinfo.test advancement 404s percentage 7.60 > 5
Halt podinfo.test advancement 404s percentage 8.69 > 5
Halt podinfo.test advancement 404s percentage 9.70 > 5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.test

    如果配置了告警功能,Flagger 将会发送一条通知,描述金丝雀失败的具体原因。以上为相关内容详情,针对 Flagger 技术的深入分析,大家有兴趣的话,可阅读 Flagger 官方文档以获得更为详尽的答案。


# 参考资料

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
3月前
|
Kubernetes 监控 容器
Istio安装及Bookinfo环境部署
文章详细介绍了如何在Kubernetes集群上安装和配置Istio服务网格,并通过部署Bookinfo示例应用来演示Istio的核心功能,如流量管理、服务监控和故障注入等。
64 1
Istio安装及Bookinfo环境部署
|
4月前
|
Kubernetes 负载均衡 C++
Istio的部署模型介绍
Istio的部署模型介绍
63 1
|
4月前
|
Kubernetes Java Shell
Kubernetes(K8S) Deployment 升级和回滚
Kubernetes(K8S) Deployment 升级和回滚
159 0
|
7月前
OpenKruise金丝雀发布过程中,创建出了canary service
【1月更文挑战第11天】【1月更文挑战第51篇】OpenKruise金丝雀发布过程中,创建出了canary service
34 1
|
Kubernetes 监控 Go
在Kubernetes上安装和配置Istio:逐步指南,展示如何在Kubernetes集群中安装和配置Istio服务网格
在Kubernetes上安装和配置Istio:逐步指南,展示如何在Kubernetes集群中安装和配置Istio服务网格
290 0
|
运维 Kubernetes 负载均衡
kubernetes 灰度发布
kubernetes 灰度发布
531 1
|
Kubernetes 负载均衡 监控
Kubernetes 实现灰度和蓝绿发布
Kubernetes 实现灰度和蓝绿发布
1203 1
|
设计模式 Kubernetes 测试技术
干货分享|使用 Istio 实现灰度发布
Kubernetes 作为基础平台,提供了强大的容器编排能力。但是在其上部署业务和服务治理上,仍然会面对一些复杂性和局限性。在服务治理上,已经有许多成熟的 ServiceMesh 框架用于扩充其能力,如 Istio、Linkerd、Dapr 等。本文将主要介绍如何使用 Istio 扩充 Kubernetes 灰度发布的能力。
干货分享|使用 Istio 实现灰度发布
|
运维 Kubernetes 负载均衡
从docker到istio之四 - istio分流应用
这是第四篇,istio分流应用。
528 0
从docker到istio之四 - istio分流应用
|
Kubernetes 监控 网络协议
Istio:灰度发布与技术实现
Istio:灰度发布与技术实现
827 0
Istio:灰度发布与技术实现
下一篇
DataWorks