侧边栏壁纸
博主头像
船长博主等级

专注于云原生运维,致敬每个爱学习的你

  • 累计撰写 35 篇文章
  • 累计创建 10 个标签
  • 累计收到 10 条评论

Istio 东西向流量管理

船长
2022-02-11 / 0 评论 / 2 点赞 / 723 阅读 / 10,473 字
温馨提示:
本文最后更新于 2022-02-11,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

背景

Istio的流量管理本质上是将流量与基础设施解耦,让运维人员可以通过Pilot指定流量遵循什么规则,而不是指定哪些pod虚拟机应该接收流量,这样通过Pilot和智能Envoy代理就可以进行流量控制。例如,你可以通过Pilot指定特定服务的5%流量转到金丝雀版本,而不必考虑金丝雀部署的大小,或根据请求的内容将流量发送到特定版本。

很明显,将流量管理与基础设施扩缩分离开来,使得系统可提供独立于应用代码的多种功能,例如A/B测试、金丝雀发布等机制所依赖的动态请求路由。此外,Istio还使用超时、重试和熔断器来处理故障恢复,并使用故障注入来跨服务测试故障恢复政策的兼容性。这些功能都是通过在服务网格中部署的Sidecar代理Envoy来实现的。

概念

  • Sidecar(边车):Sidecar自定义资源描述了Sidecar代理的配置,该代理协调与其连接的工作负载实例的入站和出站通信。默认情况下,Istio将为网格中的所有Sidecar代理进行配置,使其具有到达网格中每个工作负载实例所需的必要配置,并接受与工作负载关联的所有端口上的流量。Sidecar资源提供了一种细粒度调整端口、协议的方法,使得代理能接受向工作负载转发流量或从工作负载转发流量。此外,可以限制代理在从工作负载实例转发出站流量时可以达到的服务集。
  • 服务(Service):绑定到服务注册表中唯一名称的应用程序行为单位。服务由运行在pod、容器、虚拟机上的工作负载实例实现的多个网络端点组成。
  • 服务版本(Service versions):也称为子集(subsets),在持续部署场景中,对于给定服务,可能会存在运行着应用程序二进制文件的不同变种的不同实例子集。这些变种不一定是不同的API版本,也可以是对同一服务的迭代更改,部署在不同的环境中,如生产环境、预发环境或者开发测试环境等。
  • 源(Source):调用目标服务的下游客户端。
  • 主机(Host):客户端在尝试连接服务时使用的地址
  • 访问模型(Access model):应用程序在不知道各个服务版本(子集)的情况下仅对目标服务(主机)进行寻址。版本的实际选择由Sidecar代理确定,使应用程序代码能够脱离依赖服务的演变。
  • 虚拟服务(Virtual Service):一个虚拟服务定义了一系列针对指定服务的流量路由规则。每个路由规则都针对特定协议定义流量匹配规则。如果流量符合这些特征,就会根据规则发送到服务注册表中的目标服务(或者目标服务的子集或版本)。
  • 目标规则(Destination Rule):目标规则定义了在路由发生后应用于服务的流量的策略。这些规则指定负载均衡的配置,来自Sidecar代理的连接池大小以及异常检测设置,以便从负载均衡池中检测和驱逐不健康的主机。

DestinationRule 配置

DestinationRule 定义服务的版本和路由发生后的流量策略,包括负载均衡、连接池大小、健康检查(从负载均衡后端中剔除不健康的 hosts)等流量策略。服务与 DestinationRule 是一对一的绑定关系。

[root@k8s-master01 istio-1.10.6]# cat samples/bookinfo/networking/destination-rule-all.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: productpage
spec:
  host: productpage   # 关联Service Name
  subsets:   # 定义服务的版本
  - name: v1   # 定义版本名称
    labels:
      version: v1 # 版本V1通过标签version: v1来匹配该服务的endpoint
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: ratings
spec:
  host: ratings
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v2-mysql
    labels:
      version: v2-mysql
  - name: v2-mysql-vm
    labels:
      version: v2-mysql-vm
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: details
spec:
  host: details
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---
 
 
[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
destinationrule.networking.istio.io/productpage created
destinationrule.networking.istio.io/reviews created
destinationrule.networking.istio.io/ratings created
destinationrule.networking.istio.io/details created

DestinationRule 字段说明:

  • spec.host:关联 DestinationRule 配置的服务名称,可以是自动发现的服务(例如 Kubernetes service name),或通过 ServiceEntry 声明的 hosts。如填写的服务名无法在上述源中找到,则该 DestinationRule 中定义的规则无效
  • spec.subsets:定义服务的版本(subsets),版本可通过标签键值对匹配匹配服务中的endpoints。可以在 subsets 级覆盖流量策略配置
  • spec.trafficPolicy:定义流量策略,包括负载均衡、连接池、健康检查、TLS 策略
  • spec.trafficPolicy.loadBalancer:配置负载均衡算法,可配置:简单负载均衡算法(round robin, least conn, random...),一致性哈希(会话保持,支持按 header name,cookie,IP,query parameter 哈希),地域感知负载均衡算法
  • spec.trafficPolicy.connectionPool:配置与上游服务的连接量,可设置 TCP/HTTP 的连接池
  • spec.trafficPolicy.outlierDetection:配置从负载均衡池中驱逐不健康的 hosts
  • spec.trafficPolicy.tls:连接上游服务的 client 端 TLS 相关配置,与 PeerAuthentication 策略(server 端 TLS 模式配置)配合使用
  • spec.trafficPolicy.portLevelSettings:配置端口级别的流量策略,端口级别的流量策略会覆盖服务 / subsets 级别的流量策略配置

基于请求内容headler路由流量

最初我们将应用的所有流量路由到微服务V1版本的,现在将来自特定用户的所有流量路由到特定服务版本。在这种情况下,来自名为 Jason 的用户的所有流量将被路由到服务 reviews:v2。事实上,productpage 服务在所有到 reviews 服务的 HTTP 请求中都增加了一个自定义的 end-user 请求头。详情如下图

PS: 此场景大多用情况用于灰度验证

[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
virtualservice.networking.istio.io/reviews configured
[root@k8s-master01 istio-1.10.6]# cat samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - match:
    - headers:   # 定义header头
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v1

在 Bookinfo 应用程序的 /productpage 上,以用户 jason 身份登录,可以看到星级评分显示在每个评论旁边

以其他用户身份登录,现在星星消失了。这是因为除了 Jason 之外,所有用户的流量都被路由到 reviews:v1

基于权重路由流量

将 50% 的流量发送到 reviews:v1,另外,50% 的流量发送到 reviews:v3

[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml
virtualservice.networking.istio.io/reviews configured
[root@k8s-master01 istio-1.10.6]# cat samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews # 服务名称
        subset: v1   # 版本
      weight: 50   # 权重
    - destination:
        host: reviews
        subset: v3
      weight: 50

刷新浏览器中的 /productpage 页面,大约有 50% 的几率会看到页面中带 红色 星级的评价内容:

把 100% 的流量发送到 reviews:v3 来完成升级。

[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-v3.yaml
virtualservice.networking.istio.io/reviews configured
[root@k8s-master01 istio-1.10.6]# cat samples/bookinfo/networking/virtual-service-reviews-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v3

当您刷新 /productpage 时,您将始终看到带有 红色 星级评分的书评

流量镜像

流量镜像,也称为影子流量,是一个以尽可能低的风险为生产带来变化的强大的功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。

在非生产或者测试环境中,尝试访问一个服务所有可能的测试用例组合是个非常不现实的任务。在某些情况下,编写这些用例的所有工作也可能与实际生产所需的用例不匹配。在理想情况下,可以使用实时的生产用例和流量来帮助完善在测试环境中错过的功能区域。

注意:

  • 当流量镜像到不同的服务时,会发生在请求的关键路径之外。
  • 忽略对任何镜像流量的响应,镜像流量被视为“即发即忘”

首先把流量全部路由到测试服务的 v1 版本。然后,执行规则将一部分流量镜像到 v2 版本。

[root@k8s-master01 istio-1.10.6]# cat httpbin-v1.yml
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
[root@k8s-master01 istio-1.10.6]# kubectl apply -f httpbin-v1.yml
deployment.apps/httpbin-v1 created
 
 
[root@k8s-master01 istio-1.10.6]# cat httpbin-v2.yml
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
[root@k8s-master01 istio-1.10.6]# kubectl apply -f httpbin-v2.yml
deployment.apps/httpbin-v2 created
 
 
[root@k8s-master01 istio-1.10.6]# cat httpbin-svc.yml
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
[root@k8s-master01 istio-1.10.6]# kubectl apply -f httpbin-svc.yml
service/httpbin created
 
 
[root@k8s-master01 istio-1.10.6]# cat sleep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - name: sleep
        image: tutum/curl
        command: ["/bin/sleep","infinity"]
        imagePullPolicy: IfNotPresent
[root@k8s-master01 istio-1.10.6]# kubectl apply -f sleep.yml
deployment.apps/sleep created

创建一个默认路由规则,将所有流量路由到服务的 v1 版本:

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin
  http:
  - 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

向服务发送一部分流量:

[root@k8s-master01 istio-1.10.6]# curl -sS http://10.96.2.83:8000/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "10.96.2.83:8000",
    "User-Agent": "curl/7.29.0",
    "X-B3-Sampled": "0",
    "X-B3-Spanid": "cf562ab90684184a",
    "X-B3-Traceid": "75473676102457f0cf562ab90684184a"
  }
}

分别查看 httpbin Pod的 v1v2 两个版本的日志。您可以看到 v1 版本的访问日志条目,而 v2 版本没有日志:

[root@k8s-master01 ~]# export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})
[root@k8s-master01 ~]# kubectl logs "$V1_POD" -c httpbin
[2022-02-11 09:02:43 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-02-11 09:02:43 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-02-11 09:02:43 +0000] [1] [INFO] Using worker: sync
[2022-02-11 09:02:43 +0000] [8] [INFO] Booting worker with pid: 8
127.0.0.6 - - [11/Feb/2022:09:19:43 +0000] "GET /headers HTTP/1.1" 200 232 "-" "curl/7.29.0"
 
[root@k8s-master01 ~]# export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})
[root@k8s-master01 ~]# kubectl logs "$V2_POD" -c httpbin
[2022-02-11 09:03:46 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-02-11 09:03:46 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-02-11 09:03:46 +0000] [1] [INFO] Using worker: sync
[2022-02-11 09:03:46 +0000] [8] [INFO] Booting worker with pid: 8

镜像流量到 v2:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin
  http:
  - route:
    - destination:
        host: httpbin
        subset: v1
      weight: 100
    mirror:
      host: httpbin
      subset: v2
    mirrorPercent: 100
EOF

这个路由规则发送 100% 流量到 v1 版本。同时将 100% 的相同流量镜像(即发送)到 httpbin:v2 服务。当流量被镜像时,请求将发送到镜像服务中,并在 headers 中的 Host/Authority 属性值上追加 -shadow。例如 cluster-1 变为 cluster-1-shadow

发送流量:

[root@k8s-master01 istio-1.10.6]# curl -sS http://10.96.2.83:8000/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "10.96.2.83:8000",
    "User-Agent": "curl/7.29.0",
    "X-B3-Sampled": "0",
    "X-B3-Spanid": "165125706251a125",
    "X-B3-Traceid": "cb308415eed070eb165125706251a125"
  }
}

现在就可以看到 v1v2 版本中都有了访问日志。v2 版本中的访问日志就是由镜像流量产生的,这些请求的实际目标是 v1 版本。

[root@k8s-master01 ~]# kubectl logs "$V1_POD" -c httpbin
[2022-02-11 09:02:43 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-02-11 09:02:43 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-02-11 09:02:43 +0000] [1] [INFO] Using worker: sync
[2022-02-11 09:02:43 +0000] [8] [INFO] Booting worker with pid: 8
127.0.0.6 - - [11/Feb/2022:09:19:43 +0000] "GET /headers HTTP/1.1" 200 232 "-" "curl/7.29.0"
 
[root@k8s-master01 ~]# kubectl logs "$V1_POD" -c httpbin
[2022-02-11 09:02:43 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-02-11 09:02:43 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-02-11 09:02:43 +0000] [1] [INFO] Using worker: sync
[2022-02-11 09:02:43 +0000] [8] [INFO] Booting worker with pid: 8
127.0.0.6 - - [11/Feb/2022:09:19:43 +0000] "GET /headers HTTP/1.1" 200 232 "-" "curl/7.29.0"
127.0.0.6 - - [11/Feb/2022:09:22:05 +0000] "GET /headers HTTP/1.1" 200 232 "-" "curl/7.29.0"

参考:https://istio.io/

2

评论区