数据包在 Istio 网格中的生命周期(上)


Istio 网关中的 Gateway 和 VirtualService 配置深度解析

数据包在 Istio 网格中的生命周期(上)

Istio 网关中的 Gateway 和 VirtualService 配置深度解析

  • Kubernetes 知识图谱
  • 云原生导航
  • 谷歌搜索
  • Raft 协议动画演示
  • 搬瓦工

    通过前几篇文章的学习与实践,我们对 Gateway、VirtualService 和 Destinationrule 的概念和原理有了初步的认知,本篇将对这几个对象资源的配置文件进行深度地解析,具体细节将会深入到每一个配置项与 Envoy 配置项的映射关系。

    在开始之前,需要先搞清楚我们创建的这些对象资源最后都交给谁来处理了,负责处理这些资源的就是 pilot。

    1. pilot总体架构


    https://hugo-picture.oss-cn-beijing.aliyuncs.com/pilot.svg

    首先我们回顾一下 pilot 总体架构,上面是官方关于pilot的架构图,因为是 old_pilot_repo 目录下,可能与最新架构有出入,仅供参考。所谓的 pilot 包含两个组件:pilot-agentpilot-discovery。图里的 agent 对应 pilot-agent 二进制,proxy 对应 Envoy 二进制,它们两个在同一个容器中,discovery service 对应 pilot-discovery 二进制,在另外一个跟应用分开部署的单独的 Deployment 中。

    • discovery service : 从 Kubernetes apiserver list/watch serviceendpointpodnode 等资源信息,监听 istio 控制平面配置信息(如VirtualService、DestinationRule等), 翻译为 Envoy 可以直接理解的配置格式。
    • proxy : 也就是 Envoy,直接连接 discovery service,间接地从 Kubernetes 等服务注册中心获取集群中微服务的注册情况。
    • agent : 生成 Envoy 配置文件,管理 Envoy 生命周期。
    • service A/B : 使用了 Istio 的应用,如 Service A/B,的进出网络流量会被 proxy 接管。

    简单来说 Istio 做为管理面,集合了配置中心和服务中心两个功能,并把配置发现和服务发现以一组统一的 xDS 接口提供出来,数据面的 Envoy 通过 xDS 获取需要的信息来做服务间通信和服务治理。

    2. pilot-discovery 为 Envoy 提供的 xds 服务


    所谓 xds

    pilot-discovery 为数据面(运行在 sidecar 中的 Envoy 等 proxy 组件)提供控制信息服务,也就是所谓的 discovery service 或者 xds 服务。这里的 x 是一个代词,类似云计算里的 XaaS 可以指代 IaaS、PaaS、SaaS 等。在 Istio 中,xds 包括 cds(cluster discovery service)、lds(listener discovery service)、rds(route discovery service)、eds(endpoint discovery service),而 ads(aggregated discovery service) 是对这些服务的一个统一封装。

    以上 cluster、endpoint、route 等概念的详细介绍和实现细节可以参考 Envoy 在社区推广的 data plane api(github.com/envoyproxy/data-plane-api),这里只做简单介绍:

    • endpoint : 一个具体的“应用实例”,对应 ip 和端口号,类似 Kubernetes 中的一个 Pod。
    • cluster : 一个 cluster 是一个“应用集群”,它对应提供相同服务的一个或多个 endpoint。cluster 类似 Kubernetes 中 Service 的概念,即一个 Kubernetes Service 对应一个或多个用同一镜像启动,提供相同服务的 Pod。
    • route : 当我们做灰度发布、金丝雀发布时,同一个服务会同时运行多个版本,每个版本对应一个 cluster。这时需要通过 route 规则规定请求如何路由到其中的某个版本的 cluster 上。

    以上这些内容实际上都是对 Envoy 等 proxy 的配置信息,而所谓的 cluster discovery service、route discovery service 等 xxx discovery service 就是 Envoy 等从 pilot-discovery 动态获取 endpoint、cluster 等配置信息的协议和实现。为什么要做动态配置加载,自然是为了使用 istioctl 等工具统一、灵活地配置 service mesh。至于如何通过 istioctl 来查看 xds 信息,下文将会详细介绍。

    而为什么要用 ads 来“聚合”一系列 xds,并非仅为了在同一个 gRPC 连接上实现多种 xds 来省下几个网络连接,ads 还有一个非常重要的作用是解决 cdsrds 信息更新顺序依赖的问题,从而保证以一定的顺序同步各类配置信息,这方面的讨论可以详见 Envoy官网

    如何查看 xds

    pilot-discovery 在初始化阶段依次 init 了各种模块,其中 discovery service 就是 xDS 相关实现。envoy API reference 可以查到 v1 和 v2 两个版本的 API 文档。envoy control plane 给了 v2 grpc 接口相关的数据结构和接口。

    那么如何查看 xds 的信息呢?虽然 v2 是 grpc 的接口,但是 pilot 提供了 InitDebug,可以通过 debug 接口查询服务和 routes 等服务和配置信息。

    查看 eds

    首先找到 Service istio-pilot 的 Cluster IP

    $ export PILOT_SVC_IP=$(kubectl -n istio-system get svc istio-pilot -o go-template='{{.spec.clusterIP}}')
    

    然后查看 eds:

    $ curl http://$PILOT_SVC_IP:8080/debug/edsz
    
    [{
        "clusterName": "outbound|9080||reviews.nino.svc.cluster.local",
        "endpoints": [{
            "lbEndpoints": [{
                "endpoint": {
                    "address": {
                        "socketAddress": {
                            "address": "10.244.0.56",
                            "portValue": 9080
                        }
                    }
                }
            }, {
                "endpoint": {
                    "address": {
                        "socketAddress": {
                            "address": "10.244.0.58",
                            "portValue": 9080
                        }
                    }
                }
            }, {
                "endpoint": {
                    "address": {
                        "socketAddress": {
                            "address": "10.244.2.25",
                            "portValue": 9080
                        }
                    }
                }
            }]
        }]
    }, {
        "clusterName": "outbound|9080|v3|reviews.nino.svc.cluster.local",
        "endpoints": [{
            "lbEndpoints": [{
                "endpoint": {
                    "address": {
                        "socketAddress": {
                            "address": "10.244.0.58",
                            "portValue": 9080
                        }
                    }
                }
            }]
        }]
    }]
    

    查看 cds

    $ curl http://$PILOT_SVC_IP:8080/debug/cdsz
    
    [{"node": "sidecar~172.30.104.45~fortio-deploy-56dcc85457-b2pkc.default~default.svc.cluster.local-10", "addr": "172.30.104.45:43876", "connect": "2018-08-07 06:31:08.161483005 +0000 UTC m=+54.337448884","Clusters":[{
      "name": "outbound|9080||details.default.svc.cluster.local",
      "type": "EDS",
      "edsClusterConfig": {
        "edsConfig": {
          "ads": {
    
          }
        },
        "serviceName": "outbound|9080||details.default.svc.cluster.local"
      },
      "connectTimeout": "1.000s",
      "circuitBreakers": {
        "thresholds": [
          {
    
          }
        ]
      }
    },
    ...
    {
      "name": "outbound|9090||prometheus-k8s.monitoring.svc.cluster.local",
      "type": "EDS",
      "edsClusterConfig": {
        "edsConfig": {
          "ads": {
    
          }
        },
        "serviceName": "outbound|9090||prometheus-k8s.monitoring.svc.cluster.local"
      },
      "connectTimeout": "1.000s",
      "circuitBreakers": {
        "thresholds": [
          {
    
          }
        ]
      }
    },
    {
      "name": "BlackHoleCluster",
      "connectTimeout": "5.000s"
    }]}
    ]
    

    查看 ads

    $ curl http://$PILOT_SVC_IP:8080/debug/adsz
    

    3. Envoy 基本术语回顾


    为了让大家更容易理解后面所讲的内容,先来回顾一下 Envoy 的基本术语。

    • Listener : 监听器(listener)是服务(程序)监听者,就是真正干活的。 它是可以由下游客户端连接的命名网络位置(例如,端口、unix域套接字等)。Envoy 公开一个或多个下游主机连接的侦听器。一般是每台主机运行一个 Envoy,使用单进程运行,但是每个进程中可以启动任意数量的 Listener(监听器),目前只监听 TCP,每个监听器都独立配置一定数量的(L3/L4)网络过滤器。Listenter 也可以通过 Listener Discovery Service(LDS)动态获取。
    • Listener filter : Listener 使用 listener filter(监听器过滤器)来操作链接的元数据。它的作用是在不更改 Envoy 的核心功能的情况下添加更多的集成功能。Listener filter 的 API 相对简单,因为这些过滤器最终是在新接受的套接字上运行。在链中可以互相衔接以支持更复杂的场景,例如调用速率限制。Envoy 已经包含了多个监听器过滤器。
    • Http Route Table : HTTP 的路由规则,例如请求的域名,Path 符合什么规则,转发给哪个 Cluster。
    • Cluster : 集群(cluster)是 Envoy 连接到的一组逻辑上相似的上游主机。Envoy 通过服务发现发现集群中的成员。Envoy 可以通过主动运行状况检查来确定集群成员的健康状况。Envoy 如何将请求路由到集群成员由负载均衡策略确定。

    更多详细信息可以参考 Envoy 的架构与基本术语,本文重点突出 ListenerRouteCluster 这三个基本术语,同时需要注意流量经过这些术语的先后顺序,请求首先到达 Listener,然后通过 Http Route Table 转到具体的 Cluster,最后由具体的 Cluster 对请求做出响应。

    4. Gateway 和 VirtualService 配置解析


    还是拿之前 Istio 流量管理 这篇文章中的例子来解析吧,首先创建了一个 Gateway,配置文件如下:

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: bookinfo-gateway
    spec:
      selector:
        istio: ingressgateway # use istio default controller
      servers:
      - port:
          number: 80
          name: http
          protocol: HTTP
        hosts:
        - "*"
    

    然后又创建了一个 VirtualService

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: bookinfo
    spec:
      hosts:
      - "*"
      gateways:
      - bookinfo-gateway
      http:
      - match:
        - uri:
            exact: /productpage
        - uri:
            exact: /login
        - uri:
            exact: /logout
        - uri:
            prefix: /api/v1/products
        route:
        - destination:
            host: productpage
            port:
              number: 9080
    

    VirtualService 映射的就是 Envoy 中的 Http Route Table,大家可以注意到上面的 VirtualService 配置文件中有一个 gateways 字段,如果有这个字段,就表示这个 Http Route Table 是绑在 ingressgatewayListener 中的;如果没有这个字段,就表示这个 Http Route Table 是绑在 Istio 所管理的所有微服务应用的 Pod 上的。

    注意

    为了分清主次,我决定将本文拆分成两篇文章来讲解,本篇主要围绕 ingressgateway 来解析 Gateway 和 VirtualService,而微服务应用本身的 VirtualService 和 DestinationRule 解析放到下一篇文章再说。

    显而易见,上面这个 VirtualService 映射的 Http Route Table 是被绑在 ingressgateway 中的,可以通过 istioctl 来查看,istioctl 的具体用法请参考:调试 Envoy 和 Pilot

    首先查看 Listener 的配置项:

    $ istioctl -n istio-system pc listeners istio-ingressgateway-b6db8c46f-qcfks --port 80 -o json
    
    [
        {
            "name": "0.0.0.0_80",
            "address": {
                "socketAddress": {
                    "address": "0.0.0.0",
                    "portValue": 80
                }
            },
            "filterChains": [
                {
                    "filters": [
                        {
                            "name": "envoy.http_connection_manager",
                            "config": {
                                ...
                                "rds": {
                                    "config_source": {
                                        "ads": {}
                                    },
                                    "route_config_name": "http.80"
                                },
                                ...
                            }
                        }
                    ]
                }
            ]
        }
    ]
    

    通过 rds 配置项的 route_config_name 字段可以知道该 Listener 使用的 Http Route Table 的名字是 http.80

    查看 Http Route Table 配置项:

    $ istioctl -n istio-system pc routes istio-ingressgateway-b6db8c46f-qcfks --name http.80 -o json
    
    [
        {
            "name": "http.80",
            "virtualHosts": [
                {
                    "name": "bookinfo:80",
                    "domains": [
                        "*"
                    ],
                    "routes": [
                        {
                            "match": {
                                "path": "/productpage"
                            },
                            "route": {
                                "cluster": "outbound|9080||productpage.default.svc.cluster.local",
                                "timeout": "0.000s",
                                "maxGrpcTimeout": "0.000s"
                            },
                            ...
                        },
                        ...
                        {
                            "match": {
                                "prefix": "/api/v1/products"
                            },
                            "route": {
                                "cluster": "outbound|9080||productpage.default.svc.cluster.local",
                                "timeout": "0.000s",
                                "maxGrpcTimeout": "0.000s"
                            },
                            ...
                        },
                        ...
                    ]
                }
            ],
            "validateClusters": false
        }
    ]
    
    • VirtualService 中的 hosts 字段对应 Http Route Table 中 virtualHosts 配置项的 domains 字段。这里表示可以使用任何域名来通过 ingressgateway 访问服务(也可以直接通过 IP 来访问)。
    • VirtualService 中的 exact 字段对应 Http Route Table 中 routes.match 配置项的 path 字段。
    • VirtualService 中的 prefix 字段对应 Http Route Table 中 routes.match 配置项的 prefix 字段。
    • VirtualService 中的 route.destination 配置项对应 Http Route Table 中 routes.route 配置项的 cluster 字段。

    关于 Envoy 中的 HTTP 路由解析可以参考我之前的文章:HTTP 路由解析

    查看 Cluster 配置项:

    $ istioctl -n istio-system pc clusters istio-ingressgateway-b6db8c46f-qcfks --fqdn productpage.default.svc.cluster.local --port 9080 -o json
    
    [
        {
            "name": "outbound|9080||productpage.default.svc.cluster.local",
            "type": "EDS",
            "edsClusterConfig": {
                "edsConfig": {
                    "ads": {}
                },
                "serviceName": "outbound|9080||productpage.default.svc.cluster.local"
            },
            "connectTimeout": "1.000s",
            "circuitBreakers": {
                "thresholds": [
                    {}
                ]
            }
        }
    ]
    

    可以看到,Cluster 最终将集群外通过 ingressgateway 发起的请求转发给实际的 endpoint,也就是 Kubernetes 集群中的 Service productpage 下面的 Pod(由 serviceName 字段指定)。

    注意

    实际上 istioctl 正是通过 pilot 的 xds 接口来查看 Listener 、Route 和 Cluster 等信息的。

    好了,现在请求已经转交给 productpage 了,那么接下来这个请求将会如何走完整个旅程呢?请听下回分解!

    5. 参考



    https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/wechat.gif


    -------他日江湖相逢 再当杯酒言欢-------

    「真诚赞赏,手留余香」

    米开朗基杨

    真诚赞赏,手留余香

    使用微信扫描二维码完成支付


    相关推荐