基于istio的流量镜像构建真实流量的staging环境

本文涉及的产品
全局流量管理 GTM,标准版 1个月
日志服务 SLS,月写入数据量 50GB 1个月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: istio 作为一个强大的服务网格工具,除了常见的熔断,限流,故障注入,A/B发布之外还可以用来做流量镜像(traffic mirror)的任务,流量镜像对于调试,测试和数据采集都是非常重要的,这里介绍基于istio的流量镜像构建staging环境。

背景

流量镜像,也叫影子流量(Traffic shadowing),是一种通过复制生产环境的流量到非生产环境(一般是staging环境)进行测试开发的工作模式。

影子流量常用场景:

  • 线上流量模拟和测试,比如要用新系统替换掉老旧系统或者系统经历了大规模改造的时候,可以将线上流量导入新系统试运行;一些实验性的架构调整,也可以通过线上流量进行模拟测试。
  • 由于是全样本的模拟,影子流量可以应用于新服务的预上线演练,由于传统的手工测试本身是一种样本化的行为,通过导入真实流量形态,可以完整的模拟线上的所有情况,比如异常的特殊字符,带恶意攻击的token,可以探测预发布服务最真实的处理能力和对异常的处理能力。
  • 用于线上问题排查和临时的数据采集,比如对于一些线上突发性问题,在线下流量总是无法复现,这时候可以临时开启一个分支服务,导入影子流量进行调试和排查,而不比影响线上服务。
  • 用于日志行为采集,对于推荐系统和算法来说,样本和数据是非常核心的,传统的自动化测试在算法类的应用所面对的最大的挑战就是无法构建真实环境的用户行为数据,通过影子流量可以将用户行为以日志的形式保存起来,既可以为推荐系统和算法模型构建模拟测试样本数据,也可以作为后续大数据分析用户画像的数据来源再应用到推荐服务中。

这里给大家介绍基于 istio 服务网格做网络流量镜像的方法。

Envoy实现流量镜像的原理

envoy-mirror-setup.png

envoy 会将流量复制一份影子流量发到分支服务,和正常流量的区别是对于分支服务发送影子流量后不会处理其返回响应。同时在区分分支服务的影子流量和正常服务流量, envoy 是通过对请求头中的host值标识,envoy 会在原来流量的 host 上加上-shadow的后缀进行标识。

以上图为例,镜像流量的 host 是 http://myservice-test.mycompany.com,其将被修改为myservice-backend.company.com-shadow。(如果服务中有对请求头的host进行处理需要注意这点

案例

我们知道 istio 的数据面板是基于 envoy 构建的,包括网关部分的 ingressgateway 和服务部分的 sidecar,这样我们就可以通过 istio 做网关层流量镜像和服务层的流量镜像。
这里以一个 grpc 的应用为例分别讲述 istio 在网关层和服务层做流量镜像的应用。

本文案例代码见:
https://github.com/shikanon/privatecode/tree/master/traffic-shadowing

PS:基于http协议的集群内流量镜像可以参考istio官方文档:
https://istio.io/latest/zh/docs/tasks/traffic-management/mirroring/

基于服务层做流量镜像

在同一service发布分支服务为其引入影子流量,首先构建正常服务和分支服务(分支服务放在 testing 命名空间):

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: grpc-hello-world-v1
  name: grpc-hello-world-v1
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/instance: grpc-hello-world-v1
      app.kubernetes.io/name: grpc-hello-world
      version: v1
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: grpc-hello-world-v1
        app.kubernetes.io/name: grpc-hello-world
        version: v1
    spec:
      containers:
      - image: docker.io/shikanon096/grpc-helloworld
        imagePullPolicy: Always
        name: grpc-hello-world
        ports:
        - containerPort: 8000
        resources:
          limits:
            cpu: 50m
            memory: 128Mi
          requests:
            cpu: 50m
            memory: 128Mi
        env:
          - name: PODNAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: PODIP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP

安装sidecar:

$ istioctl kube-inject -f deploy-v1.yaml | kubectl apply -f -
$ istioctl kube-inject -f deploy-v2.yaml | kubectl apply -f -

构建istio virtualservice:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: grpc-hello-world
spec:
  hosts:
    - 'grpc-hello-world'
  http:
  - route:
    - destination:
        host: grpc-hello-world.default.svc.cluster.local
        subset: v1
      weight: 100
    mirror:
      host: grpc-hello-world.default.svc.cluster.local
      subset: v2
    mirror_percent: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: grpc-hello-world
spec:
  host: grpc-hello-world.default.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1 #通过pod的label来区分
  - name: v2
    labels:
      version: v2
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: grpc-hello-world
  name: grpc-hello-world
  namespace: default
spec:
  ports:
  - name: grpc
    port: 8000
    targetPort: 8000
  selector:
    app.kubernetes.io/name: grpc-hello-world # 共用一个selector
  type: ClusterIP

mirror 参数说明:

  • host: istio的Destination,目标host地址
  • mirror_percent: 镜像流量百分比

集群内同service影子流量.png

用grpcurl工具测试:

$ grpcurl --plaintext -d '{"name":"test01"}' grpc-hello-world:8000 helloworld.Greeter.SayHello
{
  "message": "Hello test01 ! \n Pod name is grpc-hello-world-v1-548f845bf6-mwj48 \n Pod IP is 10.0.2.65 \n"
}

查看两个 pod 的日志:

$ kubectl logs -f grpc-hello-world-v1-548f845bf6-mwj48 grpc-hello-world
...
Received: Hello test01 !
Pod name is grpc-hello-world-v1-548f845bf6-mwj48
Pod IP is 10.0.2.65
$ kubectl logs -f grpc-hello-world-v2-6b9fc86c5d-sfwht grpc-hello-world
...
Received: Hello test01 !
Pod name is grpc-hello-world-v2-6b9fc86c5d-sfwht
Pod IP is 10.0.2.67

可以看到两个 pod 都收到请求了,但只有 v1 的 response 被接收了

基于网格层做跨集群流量镜像

基于网关层做流量镜像一般多是用于为预发布环境导入线上真实流量,所以多是跨集群中使用到。
这里以 staging 集群(clusterA)和 test 集群(clusterB)命名,主体请求在 clusterA,由 clusterA 网关将流量镜像拷贝 clusterB,如下图:

跨集群影子流量.png

在 clusterA 我们需要创建 virtualservice 实现路由策略和流量镜像配置,这里和集群内调用是类似的:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: grpc-hello-world
spec:
  gateways:
  - istio-system/internal-gateway
  hosts:
    - 'grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn'
  http:
  - route:
    - destination:
        host: grpc-hello-world.default.svc.cluster.local
        port:
          number: 8000
    mirror:
      host: grpc-hello-mirror.cn-bj.rcmd-testing.skyengine.net.cn
      port:
        number: 8000
    mirror_percent: 100

我们的 mirror host 是一个外部域名,所以我们这里需要添加一个 ServiceEntry 对 hosts 的 DNS 解析方式进行指定:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-cluster-b
spec:
  hosts:
  - grpc-hello-mirror.cn-bj.rcmd-testing.skyengine.net.cn
  location: MESH_EXTERNAL
  ports:
  - number: 8000
    name: http80
    protocol: HTTP
  resolution: DNS

这里的解析方式 resolution 可以使用外部 DNS,也可以直接指定,可以参考官方设定:
https://istio.io/latest/zh/docs/reference/config/networking/service-entry/

设置好 clusterA 的路由策略,我们可以设置 clusterB 路由接受影子流量,这里需要注意 clusterB 的路由规则设置并不是grpc-hello-mirror.cn-bj.rcmd-testing.skyengine.net.cn,如果我们设置成mirror的目标路由是无法匹配的,日志如下:

$ kubectl logs  -nistio-system -f istio-ingressgateway-xxxxx
...
2021-03-03T10:44:32.588499Z    debug    envoy http    [external/envoy/source/common/http/conn_manager_impl.cc:782] [C543840][S3999381319208092814] request headers complete (end_stream=true):
':authority', 'grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn-shadow:8000'
':path', '/'
':method', 'GET'
'user-agent', 'curl/7.29.0'
'accept', '*/*'
'x-forwarded-for', 'xxxxx'
'x-forwarded-proto', 'http'
'x-envoy-external-address', 'xxxxx'
'x-request-id', 'd75bea03-c046-43a3-a49e-a6b1fcfb8eff'
'x-envoy-decorator-operation', 'httpbin.default.svc.cluster.local:8000/*'
'x-envoy-peer-metadata', 'ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwodCgxJTlNUQU5DRV9JUFMSDRoLMTcyLjI2LjIuNTQKlQIKBkxBQkVMUxKKAiqHAgodCgNhcHASFhoUaXN0aW8taW5ncmVzc2dhdGV3YXkKEwoFY2hhcnQSChoIZ2F0ZXdheXMKFAoIaGVyaXRhZ2USCBoGVGlsbGVyChkKBWlzdGlvEhAaDmluZ3Jlc3NnYXRld2F5CiAKEXBvZC10ZW1wbGF0ZS1oYXNoEgsaCWY3NjRmOTZjNQoSCgdyZWxlYXNlEgcaBWlzdGlvCjkKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSFhoUaXN0aW8taW5ncmVzc2dhdGV3YXkKLwojc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtcmV2aXNpb24SCBoGbGF0ZXN0ChoKB01FU0hfSUQSDxoNY2x1c3Rlci5sb2NhbAouCgROQU1FEiYaJGlzdGlvLWluZ3Jlc3NnYXRld2F5LWY3NjRmOTZjNS05eHNtOAobCglOQU1FU1BBQ0USDhoMaXN0aW8tc3lzdGVtCl0KBU9XTkVSElQaUmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9pc3Rpby1zeXN0ZW0vZGVwbG95bWVudHMvaXN0aW8taW5ncmVzc2dhdGV3YXkKOQoPU0VSVklDRV9BQ0NPVU5UEiYaJGlzdGlvLWluZ3Jlc3NnYXRld2F5LXNlcnZpY2UtYWNjb3VudAonCg1XT1JLTE9BRF9OQU1FEhYaFGlzdGlvLWluZ3Jlc3NnYXRld2F5'
'x-envoy-peer-metadata-id', 'router~xxxxx~istio-ingressgateway-xxxxx.istio-system~istio-system.svc.cluster.local'
'x-b3-traceid', '3d9b3060d620e2bc37c7f60957d91f28'
'x-b3-spanid', 'b6f30f3edd63ee7e'
'x-b3-parentspanid', '37c7f60957d91f28'
'x-b3-sampled', '0'
'x-envoy-internal', 'true'
'content-length', '0'

2021-03-03T10:44:32.588508Z    debug    envoy http    [external/envoy/source/common/http/conn_manager_impl.cc:1337] [C543840][S3999381319208092814] request end stream
2021-03-03T10:44:32.588598Z    debug    envoy router    [external/envoy/source/common/router/router.cc:415] [C543840][S3999381319208092814] no cluster match for URL '/'

这里的:authority:path:method就是 http 协议的 hosts, path, method,其影子流量使用的是clusterA 的 host 后面加-shadow,而不是目标host地址,比如上面的影子流量网关接受到的是grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn-shadow,而不是grpc-hello-mirror.cn-bj.rcmd-testing.skyengine.net.cn

这里主要是因为 istio-ingressgateway 的 envoy 对目标请求做了转换,所以在设置cluster B 的路由策略时应该设置为grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn-shadow

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: grpc-hello-world-clusterb
  namespace: default
spec:
  gateways:
  - istio-system/internal-gateway
  hosts:
  - grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn-shadow
  http:
  - route:
    - destination:
        host: grpc-hello-world.default.svc.cluster.local
        port:
          number: 8000

测试:

grpcurl --plaintext -d '{"name":"shikanon"}' grpc-hello-shikanon.cn-bj.rcmd-staging.skyengine.net.cn:8000 helloworld.Greeter.SayHello
{
  "message": "Hello shikanon ! \n Pod name is grpc-hello-world-cluster-a-b79b794d4-kdq2n \n; Pod IP is 172.26.1.174 \n;"
}

我们接到的是clusterA 的请求,同时查看 clusterB 中的服务日志,可以看到请求已经到达,完整示例代码:https://github.com/shikanon/privatecode/tree/master/traffic-shadowing/k8sconfig/cross-cluster

总结

isito 提供了一个基于七层负载的影子流量,不管是在集群内创建镜像副本,还是跨集群实现流量复制都可以轻松创建。通过流量镜像我们可以创建一个更接近真实的实验环境,在这个环境下可以进行真实流量下的调试,测试,数据采集和流量回放,这让线上工作作业变成一件更可控的事情,不管是服务迁移还是新旧服务升级都可以提前验证。而且通过 istio 来统一管理网格策略可以统一技术栈,将团队从复杂的技术栈解放出来,极大地降低团队心智负担。

参考文献

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
1月前
|
Kubernetes 监控 容器
Istio安装及Bookinfo环境部署
文章详细介绍了如何在Kubernetes集群上安装和配置Istio服务网格,并通过部署Bookinfo示例应用来演示Istio的核心功能,如流量管理、服务监控和故障注入等。
42 1
Istio安装及Bookinfo环境部署
|
Kubernetes JavaScript API
如何理解 Istio Ingress, 它与 API Gateway 有什么区别?东西流量?南北流量?
这三者都和流量治理密切相关,那么流量治理在过去和现在有什么区别呢?都是如何做的呢? 在学习istio的时候对流量管理加深了理解。什么是东西流量?什么是南北流量?
261 0
|
Kubernetes 负载均衡 网络协议
全网最细,深度解析 Istio Ambient Mesh 流量路径
本文旨在对 Istio Ambient Mesh 的流量路径进行详细解读,力求尽可能清晰地呈现细节,以帮助读者完全理解 Istio Ambient Mesh 中最为关键的部分。
862 10
|
Prometheus Kubernetes Cloud Native
一文带你了解 Istio 流量路由
当我们尝试在 Kubernetes 中使用 NodePort 或 LoadBalancer 类型的服务设施配置进行通信时,Istio 或许是一个非常流行、新兴的开源服务网格产品,其能够用于通信管理、可观察性、错误处理及安全性等。作为微服务架构体系的一部分,为了无需过多地使用重复的逻辑填充每个微服务代码,我们可以利用 Istio 服务网格在一个地方完成所有这些事情。
160 0
|
Kubernetes Shell iOS开发
Istio 网络:深入了解流量和架构
像 Istio 这样的服务网格项目为我们的架构引入了许多功能和优势,包括更安全地管理集群微服务之间的流量、服务发现、请求路由以及服务之间的可靠通信。
238 0
在Istio中实现Redis集群的数据分片读写分离和流量镜像
Redis 是一个高性能的 key-value 存储系统,被广泛用于微服务架构中。如果我们想要使用 Redis 集群模式提供的高级特性,则需要对客户端代码进行改动,这带来了应用升级和维护的一些困难。利用 Istio 和 Envoy ,我们可以在不修改客户端代码的前提下实现客户端无感知的 Redis Cluster 数据分片,并提供读写分离、流量镜像等高级流量管理功能。
|
监控 应用服务中间件 nginx
5个 Istio 访问外部服务流量控制最常用的例子,你知道几个?
5 个 Istio 访问外部服务的流量控制常用例子,强烈建议收藏起来,以备不时之需。
302 0
5个 Istio 访问外部服务流量控制最常用的例子,你知道几个?
|
机器学习/深度学习 JSON 网络协议
Istio IngressGateway 根据流量特征打标并据此路由
在阿里云 ASM 服务网格 Istio ingressgateway上实现根据流量特征(如来源于内外网、用户认证信息)等进行流量打标(染色),并根据流量标签进行路由和灰度发布。
1376 0
|
网络协议 应用服务中间件 容器
Istio流量管理实践之(1): 通过Istio规则来实现TCP入口流量路由的统一管理
本文通过一个示例来讲述如何通过一个简单且标准的Istio规则来控制TCP入口流量的路由,从而实现TCP入口流量路由的统一管理。
5252 0
|
网络协议 测试技术 应用服务中间件
Istio流量管理实践之(3): 基于Istio实现流量对比分析
流量镜像提供一种尽可能低的风险为生产带来变化的强大功能。镜像会将实时流量的副本发送到镜像服务,镜像流量发生在主服务的关键请求路径之外。一旦我们能够可靠地镜像流量,就可以开始做一些有价值的事情,例如通过请求流量对比工具Diffy,可以将引入测试集群的流量与生产集群中的预期行为进行比较。
2182 0