TH小破站

  • 首页
  • 容器
    • 认识Docker
    • DockerFile详解
  • k8s
    • 认识Kubernetes
    • Kubernetes部署--(kubeadm方式)
    • kubeasz部署k8s集群
    • Ansible-binary部署k8s集群-1.31.2
  • Ceph
    • 认识分布式存储
    • CPEH部署
  • CI/CD
    • DevOps认识
    • 部署--Gitlab
  • ELK
    • ELK简介
    • Elasticsearch
    • Kibana部署
    • Logstash
    • ELK--收集 tomcat 日志(实战)
  • HD
    • 认识--大数据
    • Hadoop-单机部署
    • Hadoop-伪分布部署
    • Hadoop-完全分布部署
    • Hadoop-(HA)完全分布部署
    • Ambari自动部署--Hadoop
  • Nginx
    • Nginx功能介绍
    • Nginx安全选项
    • Nginx配置文件简单介绍
    • 网站服务器部署
  • 监控
    • ZABBIX-部署(yum)
    • ZABBIX-编译安装
  • 云计算
    • KVM 概述
    • OpenStack企业级部署(T版)
    • OpenStack之dashboard部署
    • OpenStack-controller的高可用
    • openstack(Caracal版_ubuntu版本)部署
  • 数据库
    • mariadb与mysql
    • mysql主从
    • MySQL--冷备(实战)
    • MySQL--热备(dump表)
    • MySQL--热备(dump库)
    • 数据恢复实战案例
    • Xtrabackup实现MySql8.x全量备份
    • MyCat实现数据库读写分离
    • MHA实现数据库高可用
    • Percona XtraDB(PXC)实现数据库集群高可用
    • 数据库压力测试
    • PostgreSQL 基操
    • PostgreSQL备份恢复
    • PostgreSQL高可用
    • MongDB安装配置基础使用
    • MongoDB实现复制集
    • MongoDB备份还原
  • 自动化
    • ssh远程服务原理及配置
    • Ansible剧本文件应用
    • Ansible管理服务介绍
    • Ansible自动化部署node_exporter(包含升级,回滚)
    • Saltstack-部署安装
  • 脚本
    • 一键查看服务器资源利用率
    • 一键生成SSL证书
    • 保存指定数量的文件/目录
    • 初始化服务器
    • 扫描可用IP
    • 批量分发SSH密钥
    • 监控多主机磁盘脚本
    • 一键安装MySQL8.x脚本
    • Python脚本监控ES集群
    • 资源巡检脚本
    • 查看各用户使用的cpu占比
热衷于分享
架构师成长之路 !!!
  1. 首页
  2. 云原生
  3. 正文

istio灰度发布(金丝雀/AB测试)

2024年8月16日 2087点热度 0人点赞 0条评论

lstio中使用Flagger实现金丝雀发布

灰度发布也叫金丝雀部署,是指通过控制流量的比例,实现新老版本的逐步替换。比如对于服务A有两个版本〈蓝和绿两个版本),当前两个版本同时部署,但是version1比例90%,version2比例10%,然后我们可以观察version2的实际运行效果,如果符合预期,则可以逐步调整流量占比,比如调整为80:20 -> 70:30 -> 10:90 -> 0:100 ,最终version1版本下线,全部替换成version2版本。如果验证失败,切换100%流量回v1版本(回滚)。灰度发布的特点是︰

在 lstio中要实现灰度发布有多种方案,比如Flagger .Argo Rollouts等。

Flagger

Flagger是一个渐进式交付的 Kubernetes Operator,它可以自动执行Kubernetes上运行的应用程序的发布过程。它通过在测量指标和运行一致性测试的同时逐渐将流量转移到新版本,降低了在生产中引入新软件版本的风险。

Flagger通过使用服务网格(App Mesh、Istio、Linkerd、Kuma、Open Service Mesh)或Ingress控制器(Contour、Gloo、NGINX、Skipper、Traefik、APISIX)用于流量路由。对于发布分析,Flagger可以查询Prometheus、InfluxDBDatadog、New Relic、CloudWatch、Stackdriver或 Graphite,并使用Slack、MS Teams、Discord和 Rocket来发出警报。

Flagger可以使用Kubernetes CRD进行配置,并且与任何为Kubernetes 制作的CI/CD解决方案兼容。由于Flagger是声明性的对Kubernetes事件做出反应,因此它可以与诸如此类的工具一起在Git0ps管道中使用。

安装Flagger

要使用Flagger ,需要先选择一个受支持的路由提供商(比如我们这里使用lstio),然后使用Helm 或 Kustomize安装 Flagger。

Flagger需要Kubernetes集群v1.16或更高版本以及lstio v1.5或更高版本。

首先当然需要安装lstio,并开启Prometheus插件:

 #demo 或者default都可以
 root@k8s-master01:~# istioctl manifest install --set profile=demo -y
 root@k8s-master01:~/istio# kubectl apply -f istio-1.22.2/samples/addons/prometheus.yaml 

部署flagger到k8s

 root@k8s-master01:~# git clone https://github.com/fluxcd/flagger.git
 root@k8s-master01:~# cd flagger/
 root@k8s-master01:~/flagger# kubectl apply -k kustomize/istio/
 root@k8s-master01:~/flagger# kubectl get pod -n istio-system 
 NAME                                   READY   STATUS    RESTARTS       AGE
 flagger-78f6f77854-wmnm5               1/1     Running   0              76s
 ....
 root@k8s-master01:~/flagger# kubectl get crd | grep fla
 alertproviders.flagger.app                 2024-08-14T13:52:15Z
 canaries.flagger.app                       2024-08-14T13:52:15Z
 metrictemplates.flagger.app                2024-08-14T13:52:15Z

测试应用

下面我们创建一个名为 test的命名空间,并为其启用Istio sidecar自动注入∶

 root@k8s-master01:~/flagger# kubectl create ns test
 root@k8s-master01:~/flagger# kubectl label ns test  istio-injection=enabled

接下来我们使用flagger官方提供的podinfo应用来进行测试:

 root@k8s-master01:~/flagger# kubectl apply -k kustomize/podinfo/
 root@k8s-master01:~/flagger# kubectl get deploy -n test 
 NAME      READY   UP-TO-DATE   AVAILABLE   AGE
 podinfo   2/2     2            1           58s
 root@k8s-master01:~/flagger# kubectl get hpa -n test 
 NAME      REFERENCE            TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
 podinfo   Deployment/podinfo   <unknown>/99%   2         4         2          72s
 root@k8s-master01:~/flagger# kubectl get po -n test 
 NAME                       READY   STATUS    RESTARTS   AGE
 podinfo-6cf8bdbd9f-c2rzw   2/2     Running   0          96s
 podinfo-6cf8bdbd9f-c46fm   2/2     Running   0          81s

接着我们再部署一个负载测试服务用于在金丝雀分析期间生成流量︰(主要用来验证金丝雀效果)

 root@k8s-master01:~/flagger# kubectl apply -k kustomize/tester/
 root@k8s-master01:~/flagger# kubectl get po -n test 
 NAME                                 READY   STATUS    RESTARTS   AGE
 flagger-loadtester-96b57bb8c-nckbt   2/2     Running   0          37s
 podinfo-6cf8bdbd9f-c2rzw             2/2     Running   0          5m43s
 podinfo-6cf8bdbd9f-c46fm             2/2     Running   0          5m28s

创建金丝雀

接下来我们就胡以创建一个Canary自定义资源来实现我们的金丝雀发布了。Canary对象是Flagger 的核心,它描述了金丝雀发布的目标。如下所示,我们为podinfo应用创建一个Canary对象:

 root@k8s-master01:~/flagger# cat podinfo-canary.yaml 
 apiVersion: flagger.app/v1beta1
 kind: Canary
 metadata:
   name: podinfo
   namespace: test
 spec:
   targetRef: # deployment 引用
     apiVersion: apps/v1
     kind: Deployment
     name: podinfo
   progressDeadlineSeconds: 60 # 金丝雀部署升级最大处理时间(以秒为单位)(默认600秒)
   autoscalerRef: # HPA 引用(可选)
     apiVersion: autoscaling/v2
     kind: HorizontalPodAutoscaler
     name: podinfo
   service:
     port: 9898
     targetPort: 9898
     gateways: # Istio 网关(可选)
       - istio-system/public-gateway
     hosts: # VirtualService 主机名 (optional)
       - podinfo.k8s.local
     trafficPolicy: # Istio 流量策略(可选)
       tls:
         # use ISTIO_MUTUAL when mTLS is enabled
         mode: DISABLE
     retries: # Istio 重试策略(可选)
       attempts: 3
       perTryTimeout: 1s
       retryOn: "gateway-error,connect-failure,refused-stream"
   analysis: # 金丝雀分析
     interval: 1m # 金丝雀分析间隔时间(默认 60s)
     threshold: 5 # 金丝雀分析失败阈值(默认 5)
     maxWeight: 50 # 金丝雀最大流量权重(默认 50)
     stepWeight: 10 # 金丝雀流量权重步长(默认 10)
     metrics:
       - name: request-success-rate
         # minimum req success rate (non 5xx responses)
         # percentage (0-100)
         thresholdRange:
           min: 99
         interval: 1m
       - name: request-duration
         # maximum req duration P99
         # milliseconds
         thresholdRange:
           max: 500
         interval: 30s
     # testing (optional)
     webhooks:
       - name: acceptance-test
         type: pre-rollout
         url: http://flagger-loadtester.test/
         timeout: 30s
         metadata:
           type: bash
           cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
       - name: load-test
         url: http://flagger-loadtester.test/
         timeout: 5s
         metadata:
           # 使用 hey 工具对 podinfo-canary 进行为期1分钟的负载测试,每秒发送10个请求,且测试过程中会维持2个并发连接。
           cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"

上面的配置文件中,我们定义了 podinfo 应用的金丝雀发布策略,其中 targetRef 指定了要进行金丝雀发布的 Deployment 对象,service 指定了金丝雀发布的服务,analysis 指定了金丝雀分析策略,这里我们指定了两个内置的指标检查:request-success-rate 和 request-duration,其中 request-success-rate 指定了 HTTP 请求成功率,request-duration 指定了请求持续时间。对于每个指标,你可以使用 thresholdRange 和窗口大小或时间序列指定可接受的值范围和时间间隔。内置检查适用于每个服务网格/Ingress 控制器,并通过 Prometheus 查询实现。

在 service 中我们指定了 Istio 的 Gateway(istio-system/public-gateway)以及 VirtualService 要使用的主机名,首先我们可以为该应用创建一个 Gateway 对象:

 root@k8s-master01:~/flagger# cat public-gateway.yaml
 apiVersion: networking.istio.io/v1alpha3
 kind: Gateway
 metadata:
   name: public-gateway
   namespace: istio-system
 spec:
   selector:
     istio: ingressgateway
   servers:
     - port:
         number: 80
         name: http
         protocol: HTTP
       hosts:
         - "*"

另外我们在上面的对象中通过 webhooks 字段指定了金丝雀分析期间要执行的测试,其中 acceptance-test 用于在金丝雀分析开始之前执行,load-test 用于在金丝雀分析期间执行。

接下来我们可以直接创建 Canary 对象了:

 root@k8s-master01:~/flagger# kubectl apply -f public-gateway.yaml 
 root@k8s-master01:~/flagger# kubectl apply -f podinfo-canary.yaml 

当创建了 Canary 对象后,Flagger 会自动创建一个名为 pod-info-primary 的 Deployment 以及两个版本的 Service 对象:

 root@k8s-master01:~/flagger# kubectl get svc -n test
 NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
 flagger-loadtester   ClusterIP   173.96.10.128    <none>        80/TCP     16m
 podinfo              ClusterIP   173.96.197.37    <none>        9898/TCP   42s
 podinfo-canary       ClusterIP   173.96.203.207   <none>        9898/TCP   102s
 podinfo-primary      ClusterIP   173.96.147.150   <none>        9898/TCP   102s

可以看到我们原本的 podinfo 应用已经从 podinfo 这个 Deployment 迁移到了 podinfo-primary 这个 Deployment 之上。

此外还有 Istio 相关的对象:

 root@k8s-master01:~/flagger# kubectl get vs -ntest
 NAME      GATEWAYS                          HOSTS                             AGE
 podinfo   ["istio-system/public-gateway"]   ["podinfo.k8s.local","podinfo"]   5m34s
 root@k8s-master01:~/flagger# kubectl get dr -ntest
 NAME              HOST              AGE
 podinfo-canary    podinfo-canary    5m48s
 podinfo-primary   podinfo-primary   5m48s

我们可以查看下自动生成的 VirtualService 对象:

 root@k8s-master01:~/flagger#  kubectl get vs -ntest podinfo -oyaml
 apiVersion: networking.istio.io/v1
 kind: VirtualService
 metadata:
   annotations:
     helm.toolkit.fluxcd.io/driftDetection: disabled
     kustomize.toolkit.fluxcd.io/reconcile: disabled
   creationTimestamp: "2024-08-14T14:19:42Z"
   generation: 1
   name: podinfo
   namespace: test
   ownerReferences:
   - apiVersion: flagger.app/v1beta1
     blockOwnerDeletion: true
     controller: true
     kind: Canary
     name: podinfo
     uid: 3561d268-5ea9-452d-be8d-622469c18a6d
   resourceVersion: "1337334"
   uid: a29b933b-d21f-4730-a20e-a595c93a49b8
 spec:
   gateways:
   - istio-system/public-gateway
   hosts:
   - podinfo.k8s.local
   - podinfo
   http:
   - retries:
       attempts: 3
       perTryTimeout: 1s
       retryOn: gateway-error,connect-failure,refused-stream
     route:
     - destination:
         host: podinfo-primary
       weight: 100
     - destination:
         host: podinfo-canary
       weight: 0

从上面的配置中我们可以看到当前 podinfo 应用的流量全部被路由到了 podinfo-primary 对象上,而 podinfo-canary 对象的流量权重为 0,当然同样可以查看 DestinationRule 对象:

 root@k8s-master01:~/flagger# kubectl get dr podinfo-primary -ntest -oyaml
 apiVersion: networking.istio.io/v1
 kind: DestinationRule
 metadata:
   creationTimestamp: "2024-08-14T14:19:42Z"
   generation: 1
   name: podinfo-primary
   namespace: test
   ownerReferences:
   - apiVersion: flagger.app/v1beta1
     blockOwnerDeletion: true
     controller: true
     kind: Canary
     name: podinfo
     uid: 3561d268-5ea9-452d-be8d-622469c18a6d
   resourceVersion: "1337320"
   uid: 68eba0f3-504d-4943-b441-1b35254e2749
 spec:
   host: podinfo-primary
   trafficPolicy:
     tls:
       mode: DISABLE

所以默认情况下现在我们访问到的就是 podinfo-primary 这个 Deployment 对象,也就是目前的默认版本。我们可以在浏览器中访问 podinfo 来查看当前的版本:

自动金丝雀发布

我们可以看到现在的版本是 podinfo v6.0.0,接下来我们来升级应用触发金丝雀发布。要触发金丝雀发布,可以由以下任何对象的更改来触发:

  • Deployment PodSpec(容器镜像、命令、端口、环境变量、资源等)

  • 作为卷挂载或映射到环境变量的 ConfigMaps

  • 作为卷挂载或映射到环境变量的 Secrets

比如我们可以直接修改 Deployment 对象的镜像版本来触发自动化的金丝雀发布:

 root@k8s-master01:~/flagger# kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.1

 root@k8s-master01:~/flagger# kubectl describe canaries podinfo -ntest
   Normal   Synced  2m12s              flagger  Advance podinfo.test canary weight 20
   Normal   Synced  72s                flagger  Advance podinfo.test canary weight 30
   Normal   Synced  12s                flagger  Advance podinfo.test canary weight 40

需要注意在金丝雀分析期间对 Deployment 应用新的更改,Flagger 将重新启动分析。第一步是先会去扩容 podinfo 应用:

 root@k8s-master01:~/flagger# kubectl get pods -ntest
 NAME                                 READY   STATUS    RESTARTS   AGE
 flagger-loadtester-96b57bb8c-nckbt   2/2     Running   0          32m
 podinfo-77495b7f4-4fg9v              2/2     Running   0          5m55s
 podinfo-77495b7f4-whqfr              2/2     Running   0          5m55s
 podinfo-primary-86f5c66454-4sg77     2/2     Running   0          16m
 podinfo-primary-86f5c66454-72x7k     2/2     Running   0          16m

然后就会根据我们在 Canary 对象中定义的金丝雀分析策略来进行分析,并一步步将金丝雀版本的权重提高。


自动回滚

在金丝雀分析期间,我们可以生成 HTTP 500 错误和高延迟来测试 Flagger 是否暂停发布。

比如我们触发另一个金丝雀发布:

 root@k8s-master01:~/flagger# kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.2

 root@k8s-master01:~/flagger# kubectl describe canaries podinfo -ntest
 Warning  Synced  3m2s (x2 over 13m)  flagger  Halt advancement no values found for istio metric request-success-rate probably podinfo.test is not receiving traffic: running query failed: no values found
 Normal   Synced  2m2s (x2 over 12m)  flagger  Advance podinfo.test canary weight 20
 Warning  Synced  62s                 flagger  Halt advancement no values found for istio metric request-duration probably podinfo.test is not receiving traffic
 Normal   Synced  2s (x5 over 9m2s)   flagger  (combined from similar events): Advance podinfo.test canary weight 30

然后进入 loadtester 容器:

 kubectl -n test exec -it flagger-loadtester-xx-xx sh

使用下面的命令来生成 HTTP 500 错误:

 watch curl http://podinfo-canary:9898/status/500
 #也可以添加延迟:
 watch curl http://podinfo-canary:9898/delay/1

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

 root@k8s-master01:~/flagger# kubectl describe canaries podinfo -ntest  
   Normal   Synced  7m53s (x6 over 17m)  flagger  (combined from similar events): Advance podinfo.test canary weight 40
   Warning  Synced  6m53s                flagger  Halt podinfo.test advancement success rate 98.02% < 99%
   Warning  Synced  5m53s                flagger  Halt podinfo.test advancement success rate 22.22% < 99%
   Warning  Synced  4m53s                flagger  Halt podinfo.test advancement success rate 97.65% < 99%
   Warning  Synced  3m53s                flagger  Rolling back podinfo.test failed checks threshold reached 5
   Warning  Synced  3m53s                flagger  Canary failed! Scaling down podinfo.test

会话亲和性

虽然 Flagger 可以单独执行加权路由和 A/B 测试,但通过 Istio,它可以将两者结合起来,从而形成具有会话关联性的 Canary 版本。这种部署策略将金丝雀发布与 A/B 测试相结合,当我们尝试逐步向用户推出新功能时,金丝雀发布是很有帮助的,但由于其路由的特性(基于权重),即使用户之前已经被路由到新版本,他们仍然还有路由到应用程序的旧版本上,这种情况可能不符合我们的预期。

由于 A/B 测试对于需要会话关联的应用程序特别有用,因此我们将基于 cookie 的路由与常规的基于权重的路由集成在一起,这意味着一旦用户接触到我们应用程序的新版本(基于流量权重),他们总是会被路由到该版本,不会被路由回我们应用程序的旧版本。

我们可以通过在 Canary 对象中指定 .spec.anasyis.sessionAffinity 来启用此功能:

 analysis:
   # schedule interval (default 60s)
   interval: 1m
   # 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: 2
   # session affinity config
   sessionAffinity:
     # name of the cookie used
     cookieName: flagger-cookie
     # max age of the cookie (in seconds)
     # optional; defaults to 86400
     maxAge: 21600

其中 .spec.analysis.sessionAffinity.cookieName 是存储的 Cookie 的名称,cookie 的值是随机生成的字符串,充当唯一标识符,对于上述配置,在 Canary 运行期间路由到 Canary 版本的请求的响应头将如下所示:

 Set-Cookie: flagger-cookie=LpsIaLdoNZ; Max-Age=21600

Canary 运行结束并且所有流量都转移回主应用后,所有响应都将具有以下 Header:

 Set-Cookie: flagger-cookie=LpsIaLdoNZ; Max-Age=-1

这告诉客户端删除 cookie,确保用户系统中没有垃圾 cookie。

如果触发新的 Canary 运行,响应标头将被路由到 Canary 版本的所有请求设置一个新的 cookie:

 Set-Cookie: flagger-cookie=McxKdLQoIN; Max-Age=21600

比如我们这里的 podinfo 这个金丝雀对象如果想要启用会话亲和性,我们可以这样配置:

 root@k8s-master01:~/istio# vim  podinfo-canary-session-affinity.yaml
 apiVersion: flagger.app/v1beta1
 kind: Canary
 metadata:
   name: podinfo
   namespace: test
 spec:
   targetRef:
     apiVersion: apps/v1
     kind: Deployment
     name: podinfo
   progressDeadlineSeconds: 60
   autoscalerRef:
     apiVersion: autoscaling/v2
     kind: HorizontalPodAutoscaler
     name: podinfo
   service:
     port: 9898
     targetPort: 9898
     gateways:
       - istio-system/public-gateway
     hosts:
       - podinfo.k8s.local
     trafficPolicy:
       tls:
         mode: DISABLE
     retries:
       attempts: 3
       perTryTimeout: 1s
       retryOn: "gateway-error,connect-failure,refused-stream"
   analysis:
     interval: 1m
     threshold: 5
     maxWeight: 50
     stepWeight: 10
     sessionAffinity:             # session 亲和性配置
       cookieName: flagger-cookie # cookie 名称
       maxAge: 21600              # cookie 最大存活时间(秒),默认为 86400
     metrics:
       - name: request-success-rate
         thresholdRange:
           min: 99
         interval: 1m
       - name: request-duration
         thresholdRange:
           max: 500
         interval: 30s
     webhooks:
       - name: acceptance-test
         type: pre-rollout
         url: http://flagger-loadtester.test/
         timeout: 30s
         metadata:
           type: bash
           cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
       - name: load-test
         url: http://flagger-loadtester.test/
         timeout: 5s
         metadata:
           cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"
 root@k8s-master01:~/istio# kubectl apply -f podinfo-canary-session-affinity.yaml

更新后我们可以重新触发金丝雀发布:

 root@k8s-master01:~/istio# kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.0

这个时候我们查看 VirtualService 对象可以发现里面就包含了会话亲和性的相关配置:

root@k8s-master01:~/istio#  kubectl get vs -ntest podinfo -oyaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: podinfo
  namespace: test
spec:
  gateways:
  - istio-system/public-gateway
  hosts:
  - podinfo.k8s.local
  - podinfo
  http:
  - match:
    - headers:
        Cookie:
          regex: .*flagger-cookie.*tmVCwNFKaj.*
    name: sticky-route
    retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: gateway-error,connect-failure,refused-stream
    route:
    - destination:
        host: podinfo-primary
      weight: 0
    - destination:
        host: podinfo-canary
      weight: 100
  - retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: gateway-error,connect-failure,refused-stream
    route:
    - destination:
        host: podinfo-primary
      weight: 50
    - destination:
        host: podinfo-canary
      headers:
        response:
          add:
            Set-Cookie: flagger-cookie=tmVCwNFKaj; Max-Age=21600
      weight: 50

通过 VirtualService 对象将请求头中添加上 Cookie 信息,然后根据 Cookie 信息来进行路由,这样就可以实现会话亲和性了。

流量镜像

我们可以通过用迭代替换 stepWeight/maxWeight 并将 analysis.mirror 设置为 true 来启用流量镜像:

root@k8s-master01:~/istio# vim podinfo-morr.yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  progressDeadlineSeconds: 60
  autoscalerRef:
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    name: podinfo
  service:
    port: 9898
    targetPort: 9898
    gateways:
      - istio-system/public-gateway
    hosts:
      - podinfo.k8s.local
    trafficPolicy:
      tls:
        mode: DISABLE
    retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: "gateway-error,connect-failure,refused-stream"
  analysis:
    interval: 1m
    threshold: 5
    iterations: 10       # 迭代总数
    mirror: true         # 启用流量镜像
    mirrorWeight: 100    # 将流量镜像到金丝雀版本的权重(默认为100%)
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        thresholdRange:
          max: 500
        interval: 1m
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 30s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"

在上面的对象中我们增加了一个 match 字段,用于指定金丝雀匹配条件,这里我们指定了两个匹配条件,一个是 user-agent,另一个是 cookie,表示将针对 Chrome 用户和拥有 type=insider cookie 的用户运行 10 分钟的金丝雀分析。

通过上述配置,Flagger将通过以下步骤运行金丝雀版本:

  • 检测新版本

  • 金丝雀版本从零开始扩展

  • 等待HPA设置金丝雀最小副本数检查Canary pod的健康状况运行验收测试

  • 如果测试失败则中止金丝雀发布开始负载测试

  • 将100%的流量从主版本镜像到金丝雀版本

  • 每分钟检查请求成功率和请求持续时间

  • 如果达到指标检查失败阈值,则中止金丝雀发布达到迭代次数后停止流量镜像

  • 将实时流量路由到Canary Pod

  • 升级金丝雀

  • 等待主要部署完成

  • 等待HPA设置主最小副本数

  • 检查主Pod的运行状况

  • 将实时流量切换回主版本

  • 将金丝雀规模归零

我们可以直接更新 Canary 对象:

root@k8s-master01:~/istio# kubectl apply -f podinfo-morr.yaml

更新后我们可以重新触发金丝雀发布:

root@k8s-master01:~/istio# kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.1

然后就会开始金丝雀发布过程了:

root@k8s-master01:~/istio# kubectl describe canary podinfo -ntest

Events:
  Type    Reason  Age                  From     Message
  ----    ------  ----                 ----     -------
  Normal  Synced  39m                  flagger  Pre-rollout check acceptance-test passed
  Normal  Synced  39m                  flagger  Advance podinfo.test canary weight 10
  Normal  Synced  38m                  flagger  Advance podinfo.test canary weight 20
  Normal  Synced  37m                  flagger  Advance podinfo.test canary weight 30
  Normal  Synced  36m                  flagger  Advance podinfo.test canary weight 40
  Normal  Synced  35m                  flagger  Advance podinfo.test canary weight 50
  Normal  Synced  34m                  flagger  Copying podinfo.test template spec to podinfo-primary.test
  Normal  Synced  32m (x2 over 33m)    flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test
  Normal  Synced  2m14s (x2 over 40m)  flagger  New revision detected! Scaling up podinfo.test
  Normal  Synced  74s (x2 over 39m)    flagger  Starting canary analysis for podinfo.test
  Normal  Synced  74s                  flagger  Advance podinfo.test canary iteration 1/10
  Normal  Synced  14s                  flagger  Advance podinfo.test canary iteration 2/10
  root@k8s-master01:~/istio# kubectl get pod -n test
NAME                                 READY   STATUS    RESTARTS   AGE
flagger-loadtester-96b57bb8c-nckbt   2/2     Running   0          24h
podinfo-77495b7f4-djhsb              2/2     Running   0          2m53s
podinfo-77495b7f4-qjhzl              2/2     Running   0          2m53s
podinfo-primary-67b54c7-kmxsj        2/2     Running   0          16m
podinfo-primary-67b54c7-vrmwm        2/2     Running   0          16m

A/B 测试

接下来我们了解下如何使用 Istio 和 Flagger 进行 A/B 测试。除了加权路由之外,Flagger 还可以配置为根据 HTTP 匹配条件将流量路由到金丝雀版本,在 A/B 测试场景中,我们会使用 HTTP Header 或 cookie 来定位特定的用户群体,这对于需要会话关联的前端应用程序特别有用。

A/B 测试实验一般有 2 个目的:

  • 判断哪个更好:比如在 APP 界面上做了一个新的修改,究竟效果会不会更新,需要数据来判定

  • 计算收益:例如最近新上线了一个直播功能,那么直播功能究竟给平台带了来多少额外的 DAU,多少额外的使用时长,多少直播以外的视频观看时长等

我们一般比较熟知的是上述第 1 个目的,对于第 2 个目的,对于收益的量化,计算 ROI,往往对数据分析师和管理者非常重要。对于一般的 A/B 测试,其实本质上就是把平台的流量均匀分为几个组,每个组添加不同的策略,然后根据这几个组的用户数据指标,例如:留存、人均观看时长、基础互动率等等核心指标,最终选择一个最好的组上线。

所以 A/B 测试其实没有一个固定的标准,一般都是根据业务场景来定制的,比如我们可以根据用户的地域、设备、版本、渠道、用户行为等等来进行分组,然后针对不同的分组进行不同的策略,最后根据不同的指标来选择最好的组。

同样这里我们对上面的 podinfo 应用来进行 A/B 测试,创建一个如下所示的 Canary 对象:

root@k8s-master01:~/istio# vim podinfo-ab.yaml 
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  progressDeadlineSeconds: 60
  autoscalerRef:
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    name: podinfo
  service:
    port: 9898
    gateways:
      - istio-system/public-gateway
    hosts:
      - podinfo.k8s.local
    trafficPolicy:
      tls:
        mode: DISABLE
  analysis:
    interval: 1m
    iterations: 10
    # 回滚前的最大失败迭代次数
    threshold: 2
    # 金丝雀匹配条件
    match:
      - headers:
          user-agent:
            regex: ".*Chrome.*"
      - headers:
          cookie:
            regex: "^(.*?;)?(type=insider)(;.*)?$"
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        thresholdRange:
          max: 500
        interval: 30s
    webhooks: # 金丝雀分析期间生成流量
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 15s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: type=insider' http://podinfo.test:9898/"

在上面的对象中我们增加了一个 match 字段,用于指定金丝雀匹配条件,这里我们指定了两个匹配条件,一个是 user-agent,另一个是 cookie,表示将针对 Chrome 用户和拥有 type=insider cookie 的用户运行 10 分钟的金丝雀分析。

我们可以直接更新 Canary 对象:

root@k8s-master01:~/istio# kubectl apply -f  podinfo-ab.yaml

更新后我们可以重新触发金丝雀发布:

root@k8s-master01:~/istio# kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.2

然后就会开始金丝雀发布过程了:

root@k8s-master01:~/istio# kubectl describe canary podinfo -ntest
  Normal   Synced  10m                  flagger  Routing all traffic to primary
  Normal   Synced  2m50s (x5 over 24h)  flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  110s (x10 over 23h)  flagger  (combined from similar events): Starting canary analysis for podinfo.test
  Normal   Synced  110s (x2 over 16m)   flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  50s (x2 over 14m)    flagger  Advance podinfo.test canary iteration 2/10

image-20240815223031845

自定义指标

作为分析过程的一部分,Flagger 可以验证服务级别目标 (SLO),例如可用性、错误率百分比、平均响应时间以及基于应用程序特定指标的任何其他目标。如果在 SLO 分析过程中发现性能下降,版本将自动回滚,将对最终用户的影响降到最低。

Flagger 附带两个内置指标检查:HTTP 请求成功率和持续时间。

analysis:
  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

默认情况下,Flagger 使用 Prometheus 查询来测量请求成功率和持续时间。

在 Istio 中 HTTP 请求成功率对应的 PromQL 语句如下所示:

sum(
    rate(
        istio_requests_total{
          reporter="destination",
          destination_workload_namespace=~"{{ namespace }}",
          destination_workload=~"{{ target }}",
          response_code!~"5.*"
        }[{{ interval }}]
    )
)
/
sum(
    rate(
        istio_requests_total{
          reporter="destination",
          destination_workload_namespace=~"{{ namespace }}",
          destination_workload=~"{{ target }}"
        }[{{ interval }}]
    )
)

同样 Istio 中 HTTP 请求的持续时间对应的 PromQL 语句为:

histogram_quantile(0.99,
  sum(
    irate(
      istio_request_duration_milliseconds_bucket{
        reporter="destination",
        destination_workload=~"{{ target }}",
        destination_workload_namespace=~"{{ namespace }}"
      }[{{ interval }}]
    )
  ) by (le)
)

istio_requests_total 以及 istio_request_duration_milliseconds_bucket 这两个指标都是 Istio 自带的,前面可观测性章节中我们已经介绍过了。

如果两个内置的指标检查不足以满足需求,Flagger 还支持自定义指标检查进行扩展。使用 MetricTemplate 自定义资源,可以将 Flagger 配置为连接到指标提供程序并运行返回 float64 值的查询,查询结果用于根据指定的阈值范围验证金丝雀。

比如我们想要自定义一个 Prometheus 的指标,那么可以通过将提供程序类型设置为 prometheus 并在 PromQL 中编写查询来创建针对 Prometheus 服务器的自定义指标检查。比如定义一个如下所示的指标模板:

apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: istio-system
spec:
  provider:
    type: prometheus
    address: http://prometheus.istio-system:9090
  query: |
    100 - sum(
        rate(
            istio_requests_total{
              reporter="destination",
              destination_workload_namespace="{{ namespace }}",
              destination_workload="{{ target }}",
              response_code!="404"
            }[{{ interval }}]
        )
    )
    /
    sum(
        rate(
            istio_requests_total{
              reporter="destination",
              destination_workload_namespace="{{ namespace }}",
              destination_workload="{{ target }}"
            }[{{ interval }}]
        )
    ) * 100

然后在 Canary 对象中引用这个指标模板即可:

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

上述配置通过检查 HTTP 404 请求/秒百分比是否低于总流量的 5% 来验证金丝雀,如果 404 率达到 5% 阈值,则金丝雀失败。

标签: 暂无
最后更新:2024年8月16日

tanghao946

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复
文章目录
  • lstio中使用Flagger实现金丝雀发布
  • Flagger
    • 安装Flagger
    • 测试应用
    • 创建金丝雀
    • 会话亲和性
    • 流量镜像
    • A/B 测试
    • 自定义指标

COPYRIGHT © 2025 TH小破站. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

渝ICP备2023013789号

渝公网安备50011702500926号