流量镜像

在前面的章节中,我们介绍了流量镜像的概念以及它的一些实现原理,明确了流量镜像能帮助我们解决什么问题,具体参考流量镜像概念介绍。本章节则介绍下实践中如何配置流量镜像。

我们使用httpbin服务的两个版本v1,v2来对流量镜像功能进行测试。v1版本用来接收原始流量,v2版本用来接收镜像流量,这两个版本的服务使用相同的镜像名称,只在编写 Kubernetes 的 Deployment 资源时使用version:v1version:v2这两个不同的 Labels 来区分。

我们的测试流程是:先将两个版本的服务都部署起来,并设置它的默认路由策略将所有流量转发到v1版本中,测试正常后我们通过配置 v1 版本服务的 VirtualService 来设置将流量镜像到v2版本的服务中,测试流量镜像成功后,再修改流量镜像的采样百分比来观察实际的表现效果。

部署测试服务

首先,通过以下命令来部署两个版本的httpbin服务并设置它的默认路由策略将所有流量转发到v1版本中。

  • 创建v1版本httpbin服务的Deployment
$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
        ports:
        - containerPort: 80
EOF
  • 创建v2版本httpbin服务的Deployment
$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v2
  template:
    metadata:
      labels:
        app: httpbin
        version: v2
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
        ports:
        - containerPort: 80
EOF
  • 创建httpbin服务的Service
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
EOF
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - '*'
  gateways:
    - istio-system/ingressgateway
  http:
  - match:
    - uri:
        prefix: /httpbin
    rewrite:
      uri: /
    route:
    - destination:
        host: httpbin
        subset: v1
      weight: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
EOF

部署好以上服务之后,我们来验证下服务是否正常启动,资源是否全部创建完成。

$ kubectl get svc,po,vs,dr
NAME              TYPE         CLUSTER-IP       EXTERNAL-IP  PORT(S)   AGE
service/httpbin   ClusterIP    172.20.227.164   <none>       8000/TCP  72s

NAME                              READY   STATUS        RESTARTS   AGE
pod/httpbin-v1-595647cbcc-h6s5m   2/2     Running       0          85s
pod/httpbin-v2-96486cc9d-jcz9f    2/2     Running       0          79s

NAME                                          GATEWAYS  HOSTS         AGE
virtualservice.networking.istio.io/httpbin              [httpbin]     34s

NAME                                              HOST          AGE
destinationrule.networking.istio.io/httpbin       httpbin       34s

接下来我们使用如下命令向httpbin服务发送一些流量请求。

# 将 istio-gateway 的 service 地址导入到环境变量中去。
$ export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

# 对 httpbin 服务发送请求。
$ curl http://${GATEWAY_URL}/httpbin/headers
{
  "headers": {
    "Accept": "*/*",
    "Content-Length": "0",
    "Host": "abcc68c3b126c11eab2630a8eb933271-1419444084.us-west-2.elb.amazonaws.com",
    "User-Agent": "curl/7.29.0",
    "X-B3-Parentspanid": "1a8b1eced190c3a2",
    "X-B3-Sampled": "0",
    "X-B3-Spanid": "f2212d5df93bd3da",
    "X-B3-Traceid": "3bbee4b27efecd5d1a8b1eced190c3a2",
    "X-Envoy-Internal": "true",
    "X-Envoy-Original-Path": "/httpbin/headers",
    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=855455e7c4d742bb7ce76df7c3c3c9ecf4b978e2421097ae74f6b2deec91af9c;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
  }
}

分别查看v1v2版本httpbin服务的日志信息发现,v1版本接收到了请求并打印了日志,v2版本则没有打印日志信息。

# 将 v1 版本 httpbin 服务的 pod 名称导入到环境变量中去。
$ export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})

# 查看 v1 版本 httpbin 服务对应 Pod 的应用日志信息。
$ kubectl logs -f $V1_POD -c httpbin
127.0.0.1 - - [30/Apr/2020:02:21:39 +0000] "GET //headers HTTP/1.1" 200 694 "-" "curl/7.29.0"
# 将 v2 版本 httpbin 服务的 pod 名称导入到环境变量中去。
$ export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})

# 查看 v2 版本 httpbin 服务对应 Pod 的应用日志信息。
$ kubectl logs -f $V2_POD -c httpbin
<none>

流量镜像测试

流量镜像最常用的场景是在一个集群内,将流量从服务的一个版本镜像到同源服务的另外一个版本中,比如在这个示例中,把访问v1版本httpbin服务的流量镜像到v2版本的httpbin服务中。它的场景模型如下图所示:

场景模型:一个集群中不同版本服务间流量镜像
图 3.2.1.7.1:场景模型:一个集群中不同版本服务间流量镜像

在以上配置下,使用一样的kubectl apply -f <file> 命令修改 Virtual Service 资源。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - '*'
  gateways:
    - istio-system/ingressgateway
  http:
  - match:
    - uri:
        prefix: /httpbin
    rewrite:
      uri: /
    route:
    - destination:
        host: httpbin
        subset: v1
      weight: 100
    mirror:
      host: httpbin
      subset: v2
    mirror_percent: 100
EOF

流量镜像相关的配置具体如下:

    ...
    mirror:
      host: httpbin
      subset: v2
    mirror_percent: 100

mirror表示配置一个 Destination 类型的对象。表示将该请求的流量镜像到别的服务中去。Destination 类型可配置以下子属性:

  • host:必配项,是一个 string 类型的值。表示上游资源的地址,host 可配置的资源为所有在集群中注册的所有的 service 域名或者资源, 可以是一个 Kubernetes 的 service,也可以是一个 Consul 的 services, 或者是一个 ServiceEntry 类型的资源。同样,这里配置的域名可以是一个 FQDN(Fully Qualified Domain Name - 全限定域名),也可以是一个短域名。注意,如果您是 Kubernetes 用户,那么这里的资源地址补全同样依赖于该 VirtualService 所在的命名空间,比如在 prod 命名空间下配置了 destination.host=reviews,那么 host 最终将被解释并补全为 reviews.prod.svc.cluster.local。
  • subset:非必配项,是一个 string 类型的值。表示访问上游工作负载组中指定版本的工作负载,如果需要使用 subset 配置,必须先在 DestinationRule 中定义一个 subset 组,而这里的配置值即为 subset 组中某一个 subset 的 name 值。
  • port:非必配项,是一个 PortSelector 类型的对象。指定上游工作负载的端口号,如果上游工作负载只公开了一个端口,那么这个配置将可以不用配置。

mirror_percent配置表示将100%的流量镜像到v2版本的httpbin服务上。

现在我们给v1版本httpbin服务发送一些流量请求,发送完成后,查看v2版本httpbin服务的日志信息,查看它是否接收到了镜像过来的流量。

# 对 httpbin 服务发送请求。
$ curl http://${GATEWAY_URL}/httpbin/headers
# 查看 v2 版本 httpbin 服务对应 Pod 的应用日志信息。
$ kubectl logs -f $V2_POD -c httpbin
127.0.0.1 - - [30/Apr/2020:03:10:56 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"

观察后发现,v2版本的httpbin服务中打印了日志信息,这表示我们的流量镜像配置已经生效。

接下来我们发送5次请求给v1版本的httpbin服务并查看v2版本httpbin服务的日志信息,观察是否每一个请求都会被镜像。

# 查看 v2 版本 httpbin 服务对应 Pod 的最新5行应用日志信息。
$ kubectl logs -f $V2_POD -c httpbin --tail 5
127.0.0.1 - - [30/Apr/2020:03:18:44 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"
127.0.0.1 - - [30/Apr/2020:03:18:44 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"
127.0.0.1 - - [30/Apr/2020:03:18:45 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"
127.0.0.1 - - [30/Apr/2020:03:18:47 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"
127.0.0.1 - - [30/Apr/2020:03:18:48 +0000] "GET //headers HTTP/1.1" 200 668 "-" "curl/7.29.0"

可以看到新增了5条日志信息,所有流量都被复制了一份,这说明我们的mirror_percent: 100配置生效了。

接下来,我们设置镜像到v2版本服务的流量采样率为50%。一样使用kubectl apply -f <file>命令修改mirror_percent属性值为50

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - '*'
  gateways:
    - istio-system/ingressgateway
  http:
  - match:
    - uri:
        prefix: /httpbin
    rewrite:
      uri: /
    route:
    - destination:
        host: httpbin
        subset: v1
      weight: 100
    mirror:
      host: httpbin
      subset: v2
    mirror_percent: 50
EOF

此时再连续发送20次请求给v1版本的httpbin服务,同时观察v2版本httpbin服务的日志信息变化。我们发现v1版本的httpbin服务在被调用20次时,只有9次请求镜像到了v2版本的服务当中,并不是准确的50%。实际上,Istio 允许这种误差的存在,该属性值只能表示它大概的一个百分比,读者可以再尝试发送更多的请求来观察效果。

除了上面的场景,我们经常也需要将某个服务的流量从一个 service mesh 网格中镜像到另外一个 service mesh 网格中,比如将生产环境的流量镜像到测试环境中去。它的场景模型如下图所示:

场景模型:将一个集群的流量镜像到另外一个集群当中
图 3.2.1.7.2:场景模型:将一个集群的流量镜像到另外一个集群当中

这种场景的实现方式与前面介绍的第一种场景实现方式一样,也是修改v1版本httpbin服务的 VirtualService 资源。如下只需要替换${OTHER_MESH_GATEWAY_URL}为测试环境的真实主机地址即可:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - '*'
  gateways:
    - istio-system/ingressgateway
  http:
  - match:
    - uri:
        prefix: /httpbin
    rewrite:
      uri: /
    route:
    - destination:
        host: httpbin
        subset: v1
      weight: 100
    mirror:
      host: ${OTHER_MESH_GATEWAY_URL}
    mirror_percent: 100
EOF

至此,我们就可以完成将一个集群中的流量镜像转发到另外一个集群的操作。当然,这只是简单的在不同集群中流量镜像的示例,在实际操作过程中,我们可能需要在业务集群中使用 ServiceEntry 将对测试环境的调用交给 Envoy 托管,也可能需要配置 TLS 实现安全访问等,这些功能这里不再一一展开说明。

实际上,对于生产环境中一些比较有代表性的流量,我们使用流量镜像复制到其他集群之后,可以将这些流量通过文件或者日志的形式收集起来。而这些数据既可以作为自动化测试脚本的数据源,也可以作为大数据分析客户画像等功能的部分数据源,通过对这些数据的提取以及二次开采,分析客户的使用习惯,行为等特征信息,将加工后的数据应用到推荐服务当中,可以有效帮助实现系统的千人千面,定向推荐等功能。

环境清除

使用以下命令清除上面创建的所有资源。

$ kubectl delete virtualservice httpbin
$ kubectl delete destinationrule httpbin
$ kubectl delete deploy httpbin-v1 httpbin-v2
$ kubectl delete svc httpbin

参考

  1. VirtualService 介绍
  2. DestinationRule 介绍
Copyright © servicemesher.com 2018-2020 all right reserved,powered by Gitbook Updated at 2020-05-18 14:11:07

results matching ""

    No results matching ""