k8s

通过Kruise Rollout实现基于Higress的全链路灰度

介绍

要想实现标题的功能,需要先了解什么是 Kruise Rollout 和什么是全链路灰度。

Kruise Rollout

官方文档:https://openkruise.io/zh/rollouts/introduction

Kruise Rollout 是一个 Bypass(旁路) 组件,提供 高级渐进式交付功能。支持金丝雀、蓝绿和A/B测试交付模式,同时它兼容 Gateway API 和各种Ingress 实现,以及发布过程能够基于 Prometheus Metrics 指标自动化分批与暂停,并提供旁路的无感对接、兼容已有的多种工作负载(Deployment、CloneSet、DaemonSet)。

file

主要特点

  • 丰富的发布策略

    • 用于 Deployment、CloneSet、StatefulSet、Advanced StatefulSet、Advanced DaemonSet 的多批次更新策略。
    • 用于 Deployment 的金丝雀(Canary)更新策略。
  • 丰富的流量路由管理策略

    • 在更新工作负载时进行流量细粒度、加权流量转移。
    • 流量A/B测试,基于HTTP头和Cookie进行流量转移。
    • 端到端流量灰度
  • 丰富的流量协议支持

    • Ingress 控制器集成:NGINX、ALB、Higress。
    • 通过 GatewayAPI 进行服务网格集成。
    • 可插拔的Lua脚本,以便轻松扩展到其他 Kubernetes 流量协议(甚至CRD)。

和其他组件对比

Kruise Rollouts 与 Argo Rollout 和 Flux Flagger 的对比。

file

全链路灰度

在发布应用的过程中,我们通常希望用少量特定流量来验证新版本的发布是否正常,以保障整体稳定性。这个过程被称为灰度发布。在微服务架构场景下,传统的灰度发布模式往往不能满足交付的复杂需求,全链路灰度发布的场景也就应运而生,此时每个微服务都会有灰度环境或分组来接受灰度流量。

一个微服务系统中的多个应用可以共用一个流量网关,上游应用的灰度副本会把流量传递到下游应用的灰度副本中, 从而保证一个请求的处理尽可能的保持在一个端到端灰度环境中。 这种灰度环境往往被叫做流量泳道, 在这样的泳道中,如果某个应用不存在灰度实例, 请求会引流到稳定版本的应用实例中。 但当下游应用又存在灰度实例的时候,发往下游的请求又会被导流到灰度实例上。 全链路灰度往往被用来在需要多个应用协同的场景下进行业务验证和灰度。示例图如下:

file

Istio全链路灰度

Istio 也可以实现全链路灰度。首先,需要创建灰度的 Deployment 并打上灰度的节点标识。其次,还需要配置 Istio 的流量路由 CRD,包括每一跳请求的 VirtualServiceDestinationRule 规则,并且这些流量规则还需要与请求标识相配合。

假设有2个服务A和B,流量路径如下:

流量-->Gateway-->VirtualService-A-->DestinationRule-A-->Service-A-->APP-A-->VirtualService-B-->DestinationRule-B-->Service-B-->APP-B

灰度时,在应用A和B上添加比如x-mse-tag=gray的 header,和version=canary的 label。然后包含指定 header 的流量会走灰度环境。这里可以利用 EnvoyFilter 在入口网关进行染色。对包含 system=ios 的流量染色,为其添加 x-mse-tag=gray,而其它流量则添加 x-mse-tag=base

综上所述,可以看到通过 Istio 来实现全链路灰度操作非常复杂且成本较高。如果在配置过程中出现错误,可能会导致生产流量出现问题,对业务造成重大影响。

header透传

在对微服务进行全链路灰度的过程中,一个最需要考虑的问题是流量中 header 透传的问题,一些微服务会仅保留特定的 header 进行透传,而去除其它 header。

分布式链路追踪技术对大型分布式系统中请求调用链路进行详细记录,核心思想就是通过一个全局唯一的 traceid 和每一条的 spanid 来记录请求链路所经过的节点以及请求耗时,其中 traceid 是需要整个链路传递的。当然我们也可以传递一些自定义信息,比如灰度标识。

在OpenTelemetry、Skywalking、Jaeger 等分布式链路追踪产品都支持链路传递用户自定义的数据。通过 Tracing Baggage 机制在全链路中传递对应染色标识。首先添加x-mse-tag=gray的 key 到 Baggage 中,然后在 outbound 出口处读取 Baggage 中的 key 并注入到 header 中,继续流量路由。

实例

下面来实际操作一下 Kruise Rollout 和 Higress 的全链路灰度。

安装Kruise Rollout

安装文档:https://openkruise.io/zh/rollouts/installation

这里使用helm安装。

helm repo add openkruise https://openkruise.github.io/charts/
helm repo update
helm install kruise-rollout openkruise/kruise-rollout --version 0.5.0 --set image.repository=openkruise-registry.cn-shanghai.cr.aliyuncs.com/openkruise/kruise-rollout

file

安装kubectl-kruise

kubectl krew install kruise
# 或
wget https://github.com/openkruise/kruise-tools/releases/download/v1.1.3/kubectl-kruise-linux-amd64.tar.gz
tar xvf kubectl-kruise-linux-amd64.tar.gz
mv linux-amd64/kubectl-kruise /usr/local/bin/
rm -rf linux-amd64/

file

部署demo

demo的流量如图所示:红色代表灰度流量。

file

首先部署应用。

# a service
apiVersion: v1
kind: Service
metadata:
  name: spring-cloud-a
spec:
  ports:
    - name: http
      port: 20001
      protocol: TCP
      targetPort: 20001
  selector:
    app: spring-cloud-a
# a application
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-a
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-a
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: spring-cloud-a
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.0
          imagePullPolicy: Always
          name: spring-cloud-a
          ports:
            - containerPort: 20001
# b application
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-b
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-b
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-b
      labels:
        app: spring-cloud-b
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-b:mse-2.0.0
          imagePullPolicy: Always
          name: spring-cloud-b
# c application
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-c
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-c
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: spring-cloud-c
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-c:mse-2.0.0
          imagePullPolicy: Always
          name: spring-cloud-c
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nacos-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nacos-server
  template:
    metadata:
      labels:
        app: nacos-server
    spec:
      containers:
      - env:
        - name: MODE
          value: standalone
        image: nacos/nacos-server:v2.2.0
        imagePullPolicy: Always
        name: nacos-server
      dnsPolicy: ClusterFirst
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: nacos-server
spec:
  type: ClusterIP
  ports:
    - name: nacos-server-8848-8848
      port: 8848
      protocol: TCP
      targetPort: 8848
    - name: nacos-server-9848-9848
      port: 9848
      protocol: TCP
      targetPort: 9848
  selector:
    app: nacos-server
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: demo-mysql
  name: demo-mysql
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: demo-mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-mysql
spec:
  selector:
    matchLabels:
      app: demo-mysql
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: demo-mysql
    spec:
      containers:
        - args:
            - --character-set-server=utf8mb4
            - --collation-server=utf8mb4_unicode_ci
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: root
          image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/demo-mysql:3.0.1
          name: demo-mysql
          ports:
            - containerPort: 3306

file

然后创建ingress。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-cloud-a
spec:
  ingressClassName: higress
  rules:
    - http:
        paths:
          - backend:
              service:
                name: spring-cloud-a
                port:
                  number: 20001
            path: /
            pathType: Prefix
      host: springa.com

登录nacos可以看到服务列表。用户名密码都是nacos。

file

测试访问。这里用higress-gateway的nodePort访问。有LoadBalancer的用EXTERNAL_IP。

curl http://ip:31779/a -H 'host: springa.com'

file

定义灰度规则

定义 Kruise Rollout 的灰度发布规则。

# 用于圈定全链路的应用
# a rollout configuration
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
  # 其它微服务应用类似配置,例如:rollout-b, rollout-c
  name: rollout-a
  annotations:
    rollouts.kruise.io/trafficrouting: higress-traffic
spec:
  objectRef:
    workloadRef:
      apiVersion: apps/v1
      kind: Deployment
      name: spring-cloud-a
  strategy:
    canary:
      steps:
      - pause: {}
        replicas: 1
        # 针对灰度发布中的应用,Kruise 会自动给他标识 gray 版本
      patchPodTemplateMetadata:
        labels:
          alicloud.service.tag: gray
          opensergo.io/canary-gray: gray
---
# b rollout configuration
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
  name: rollout-b
  annotations:
    rollouts.kruise.io/trafficrouting: higress-traffic
spec:
  objectRef:
    workloadRef:
      apiVersion: apps/v1
      kind: Deployment
      name: spring-cloud-b
  strategy:
    canary:
      steps:
        - pause: {}
          replicas: 1
      patchPodTemplateMetadata:
        labels:
          alicloud.service.tag: gray
          opensergo.io/canary-gray: gray
---
# c rollout configuration
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
  name: rollout-c
  annotations:
    rollouts.kruise.io/trafficrouting: higress-traffic
spec:
  objectRef:
    workloadRef:
      apiVersion: apps/v1
      kind: Deployment
      name: spring-cloud-c
  strategy:
    canary:
      steps:
        - pause: {}
          replicas: 1
      patchPodTemplateMetadata:
        labels:
          alicloud.service.tag: gray
          opensergo.io/canary-gray: gray
---
# 全链路泳道配置。在入口服务即spring-cloud-a上配置灰度流量规则,标记灰度流量和线上流量。
apiVersion: rollouts.kruise.io/v1alpha1
kind: TrafficRouting
metadata:
  name: higress-traffic
spec:
  objectRef:
  - service: spring-cloud-a
    ingress:
      classType: higress
      name: spring-cloud-a
  strategy:
    matches:
    # A/B Testing发布,基于header的流量灰度方式。
    - headers:
      - type: Exact
        name: User-Agent
        value: xiaoming
        # 此批次灰度的流量比例50%。
    #weight: 30 , 灰度30%流量
    requestHeaderModifier:
      set:
      - name: x-mse-tag
        value: gray

上面定义的灰度发布规则发布分为三批:

  1. A/B Testing发布,具有header[x-user-id]=100的流量将导入到新版本,其它则为老版本。
  2. 按照流量比例进行灰度,此批次将灰度50%的实例及流量。
  3. 将灰度完成所有的实例。

将该Rollout资源下发到K8s集群。

kubectl apply -f rollout.yaml

查看rollout。

file

STATUS=Healthy,表明 Rollout 资源正常工作。

灰度发布

Kruise Rollouts 是一个常态化的配置,将其下发到集群后,后续业务版本发布只需调整Deployment配置,无需再对 Kruise Rollouts 进行额外操作。将spring-cloud-a服务和spring-cloud-c镜像版本升级到mse-2.0.1

k set image deploy spring-cloud-a spring-cloud-a=registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.1
k set image deploy spring-cloud-c spring-cloud-c=registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.1
# 或者直接修改deployment

查看应用。

file

可以看到 Kruise Rollout 并没有直接修改原先的 deployment,而是创建了两个灰度应用 spring-cloud-a-2h5kcspring-cloud-c-btk88。自动添加了tag=gray的label。

file

查看rollout。

file

输出STATUS=Progressing:表明已经在灰度发布过程中。
输出CANARY_STATE=StepPaused:表明当前批次已经完成,是否需要继续,可通过人工确认。

测试流量访问。经过多次测试,发现流量并没有打到灰度版本中,还是轮询的方式。

file

发布。

kubectl-kruise rollout approve rollouts/rollout-a
kubectl-kruise rollout approve rollouts/rollout-c

如何回滚

只需要把镜像修改回去即可,灰度出来的deployment会自动删除。

file

为什么没有实现?

查看官方文档,当前只支持MSE,而MSE云原生网关是Higress的商业托管版本。具体差别参考文档:https://help.aliyun.com/zh/mse/product-overview/comparison-between-mse-cloud-native-gateways-and-open-source-higress-gateways?spm=a2c4g.11186623.help-menu-123350.d_0_1_6_1.4f486ca2dUxNFZ

file

我这里没有阿里云ACK和MSE环境,没法进行测试。

分类: k8s
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x