美文网首页
Service Mesh - Istio流量控制篇(上)

Service Mesh - Istio流量控制篇(上)

作者: 端碗吹水 | 来源:发表于2020-12-23 08:47 被阅读0次

    动态路由:用Virtual Service和Destination Rule设置路由规则

    路由这个功能是流量控制里面非常重要,也是最常用的一个功能。在Istio里一般通过Virtual Service(虚拟服务)以及Destination Rule(目标规则)这两个API资源进行动态路由的设置。

    基本概念

    虚拟服务(Virtual Service):

    • 定义路由规则,匹配请求
    • 描述满足条件的请求去哪里

    目标规则(Destination Rule):

    • 定义子集、策略
    • 描述到达目标的请求怎么处理
    image.png

    实践动态路由

    在上一篇Service Mesh - Istio安装与部署文章中,我们演示了BookInfo这个Demo应用的部署,并且可以发现其中的 reviews 服务共有三个不同的版本。现在我们的需求是将请求路由到 reviews 服务的指定版本上。例如,路由到版本1上,如下图:

    image.png

    实现该需求很简单,官方已经提供了配置清单文件,我们只需执行如下命令应用路由规则即可:

    [root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/bookinfo/networking/virtual-service-all-v1.yaml    # 创建虚拟服务
    virtualservice.networking.istio.io/productpage created
    virtualservice.networking.istio.io/reviews created
    virtualservice.networking.istio.io/ratings created
    virtualservice.networking.istio.io/details created
    [root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/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
    [root@m1 ~]# 
    

    然后回到应用页面上测试下规则是否生效,正常情况下,此时无论怎么刷新页面,访问的都是版本1的 reviews 服务:


    image.png

    那么配置是如何生效的呢?我们先来看看这两个API资源它们的一些具体配置项:


    image.png
    • Virtual Service
      • hosts:对应 DestinationRule 所配置的host,可配置多个
      • gateways:用来和配置的网关进行匹配使用的,如果是服务网关内部的虚拟服务就不需要配置这一项
      • http:配置http请求的路由规则与 HTTPRoute 对应
      • tls:配置tls请求的路由规则
      • tcp:配置tcp请求的路由规则
      • exportTo:给虚拟服务设置它的可见性,例如设置为所有的Namspace都可见
    • HTTPRoute:
      • match:设置匹配满足什么样条件的请求,对应 HTTPMatchRequest 对象配置项
      • route:匹配到的请求经过route配置的规则进行路由
    • HTTPRouteDestination:
      • destination:通过该字段将虚拟服务与目标规则进行绑定
    • Destination Rule
      • host:最终路由到的具体目标地址
      • subset:子集,一般主要是给服务限定版本

    virtual-service-all-v1.yaml 文件中针对 reviews 服务创建的虚拟服务配置内容如下:

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService  # 资源类型
    metadata:
      name: reviews   # 虚拟服务的名称
    spec:
      hosts:
      - reviews   # 虚拟服务的主机名,可定义多个
      http:  # 没有定义match代表匹配任意的请求
      - route:
        - destination:
            host: reviews   # 服务名称,与目标规则中的host配置对应
            subset: v1      # 通过子集限定了服务版本
    

    destination-rule-all.yaml 文件中针对 reviews 服务创建的目标规则配置内容如下:

    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule  # 资源类型
    metadata:
      name: reviews   # 目标规则的名称
    spec:
      host: reviews   # 指向k8s中的服务名称,k8s平台的dns机制可以解析出具体的服务地址
      subsets:   # 定义子集,对应该服务的三个版本,虚拟服务就是根据在这里定义的子集来路由对应的版本的
      - name: v1
        labels:
          version: v1
      - name: v2
        labels:
          version: v2
      - name: v3
        labels:
          version: v3
    

    Virtual Service 和 Destination Rule 的应用场景:

    • 按服务版本路由
    • 按比例切分流量
    • 根据匹配规则进行路由
    • 定义各种策略(负载均衡、连接池等)

    网关:用Gateway管理进入网格的流量

    什么是网关(Gateway)

    • 一个运行在网格边缘的负载均衡器
    • 接收外部请求,转发给网格内的服务
    • 配置对外的端口、协议与内部服务的映射关系
    • Istio中的Ingress网关控制入口流量,Egress网关控制出口流量,在网关只定义入口点不定义具体的路由
    • 与k8s中的Ingress一样,Istio中的Gateway也只是一种资源,需要配合一个真正工作的组件使用,在k8s中通常是ingress-nginx,在Istio中则是基于envoy的istio-ingressgateway / istio-egressgateway


      image.png
    • 官方文档

    实践创建网关

    我们来创建一个入口网关,配合虚拟服务对外暴露一些服务接口:


    image.png

    Gateway资源的一些配置选项

    image.png
    • Gateway:
      • servers:定义入口点列表
      • selector:选择器,用于通过label选择集群中Istio网关的Pod
    • Server:
      • port:暴露给外部访问的端口信息,包括端口号、名称、协议
      • hosts:暴露给外部可访问的host列表
    • VirtualService:
      • gateways:用于配置关联哪些Gateway资源,通过名称指定

    创建 test-gateway.yaml 文件,内容如下:

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway   # 资源类型为网关
    metadata:
      name: test-gateway   # 网关的名称
    spec:
      selector:   # 配置选择器,指向istio-ingressgateway的pod
        istio: ingressgateway
      servers:  # 定义对外暴露的入口点,主要配置哪些host和端口允许访问
      - port:
          number: 80
          name: http
          protocol: HTTP
        hosts:
        - "*"
    
    ---
    
    # 由于在网关只定义入口点不定义具体的路由,所以我们需要定义虚拟服务来配置路由规则
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService   # 资源类型为虚拟服务
    metadata:
      name: test-gateway    # 虚拟服务的名称
    spec:
      hosts:
      - "*"
      gateways:   # 配置gateway的名称,用于关联具体的gateway
      - test-gateway
      http:
      - match:   # 定义路由匹配规则,即暴露哪些接口
        - uri:
            prefix: /details
        - uri:
            exact: /health
        route:
        - destination:   # 将请求转发到哪些目标
            host: details
            port:
              number: 9080
    

    在应用该配置文件之前,我们先到浏览器上访问 details 接口,此时返回的状态码将会是404:

    image.png

    然后应用配置文件,创建我们所定义的网关和虚拟服务:

    [root@m1 ~]# kubectl apply -f service-mash/test-gateway.yaml
    gateway.networking.istio.io/test-gateway created
    virtualservice.networking.istio.io/test-gateway created
    [root@m1 ~]# 
    

    通过网关和虚拟服务暴露接口后,再重新访问一下该接口,可以看到能正常访问了:


    image.png

    同样,health 接口也可以被访问到:

    image.png

    Gateway 的应用场景:

    • 暴露网格内服务给外界访问
    • 访问安全(HTTPS、mTLS 等)
    • 统一应用入口,API 聚合

    服务入口:用Service Entry扩展你的网格服务

    官方文档:

    什么是服务入口(ServiceEntry):

    • 服务入口与网关正好是相反的概念,服务入口是用于添加外部服务到网格内
    • 管理到外部服务的请求,对外部服务进行抽象,使得可以像访问内部服务一样访问外部服务
    • 扩展网格,例如需要给多个集群共享同一个Mesh的场景


      image.png

    接下来我们实践一下,将 httpbin.org 注册为网格内部的服务,并配置流控策略。httpbin.org 这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法,对 web 开发和测试很有帮助,我们可以将它作为一个外部的服务进行测试。

    由于我们之前部署的Bookinfo应用的所有服务中都没有 curl 命令,因此我们想要模拟内部服务调用外部服务,就需要额外添加一个 sleep 服务。官方已经给我们准备好了配置文件,使用如下命令应用一下即可:

    [root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml
    serviceaccount/sleep created
    service/sleep created
    deployment.apps/sleep created
    [root@m1 ~]# 
    

    通过 sleep 服务来访问一下 httpbin 这个外部服务:

    [root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
    {
      "headers": {
        "Accept": "*/*", 
        "Content-Length": "0", 
        "Host": "httpbin.org", 
        "User-Agent": "curl/7.69.1", 
        "X-Amzn-Trace-Id": "Root=1-5fe0b2c6-0940d09657bb9faf42e9f49e", 
        "X-B3-Sampled": "1", 
        "X-B3-Spanid": "c8f80494e0358f1f", 
        "X-B3-Traceid": "46b7447441931824c8f80494e0358f1f", 
        "X-Envoy-Attempt-Count": "1", 
        "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwoYCg1JU1RJT19WRVJTSU9OEgcaBTEuOC4xCt8BCgZMQUJFTFMS1AEq0QEKDgoDYXBwEgcaBXNsZWVwChkKDGlzdGlvLmlvL3JldhIJGgdkZWZhdWx0CiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjg1NDU2NWNiNzkKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKIAoETkFNRRIYGhZzbGVlcC04NTQ1NjVjYjc5LWdtNmhqChYKCU5BTUVTUEFDRRIJGgdkZWZhdWx0CkkKBU9XTkVSEkAaPmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9kZWZhdWx0L2RlcGxveW1lbnRzL3NsZWVwChcKEVBMQVRGT1JNX01FVEFEQVRBEgIqAAoYCg1XT1JLTE9BRF9OQU1FEgcaBXNsZWVw", 
        "X-Envoy-Peer-Metadata-Id": "sidecar~172.22.152.249~sleep-854565cb79-gm6hj.default~default.svc.cluster.local"
      }
    }
    [root@m1 ~]# 
    

    从输出结果可以看到,从 sleep 服务的内部能够直接访问外部的 httpbin 服务,原因是Istio里默认允许所有的网格内的服务直接访问外部服务。所以为了测试服务入口这个API资源,我们需要把这种允许访问外部服务的行为给关掉,使得只有注册过的服务才能访问外部服务。

    使用如下命令可关闭出流量可访问权限(outboundTrafficPolicy = REGISTRY_ONLY):

    [root@m1 ~]# istioctl install --set profile=demo -y --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY
    

    此时再通过 sleep 访问 httpbin 就访问不到了,没有任何输出:

    [root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
    [root@m1 ~]# 
    

    现在我们来为外部服务(httpbin)配置 ServiceEntry ,让 sleep 服务通过服务入口来访问外部服务。具体命令如下:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry   # 资源类型为服务入口
    metadata:
      name: httpbin-ext   # 服务入口的名称 
    spec:
      hosts:   # 外部服务的访问地址列表,域名或ip
      - httpbin.org
      ports:   # 配置外部服务的访问端口信息
      - number: 80
        name: http
        protocol: HTTP
      resolution: DNS   # 指定服务发现机制
      location: MESH_EXTERNAL  # 定义该服务位于网格内部还是网格外部
    EOF
    

    通过如下命令可查看已创建的服务入口:

    [root@m1 ~]# kubectl get se
    NAME          HOSTS             LOCATION        RESOLUTION   AGE
    httpbin-ext   ["httpbin.org"]   MESH_EXTERNAL   DNS          97s
    [root@m1 ~]# 
    

    测试从 sleep 服务访问 httpbin :

    [root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
    {
      "headers": {
        "Accept": "*/*", 
        "Content-Length": "0", 
        "Host": "httpbin.org", 
        "User-Agent": "curl/7.69.1", 
        "X-Amzn-Trace-Id": "Root=1-5fe0bd82-51a06204013c7006305ac384", 
        "X-B3-Sampled": "1", 
        "X-B3-Spanid": "3cdd9036d7815b35", 
        "X-B3-Traceid": "08d1682631d190293cdd9036d7815b35", 
        "X-Envoy-Attempt-Count": "1", 
        "X-Envoy-Decorator-Operation": "httpbin.org:80/*", 
        "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwoYCg1JU1RJT19WRVJTSU9OEgcaBTEuOC4xCt8BCgZMQUJFTFMS1AEq0QEKDgoDYXBwEgcaBXNsZWVwChkKDGlzdGlvLmlvL3JldhIJGgdkZWZhdWx0CiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjg1NDU2NWNiNzkKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKIAoETkFNRRIYGhZzbGVlcC04NTQ1NjVjYjc5LWdtNmhqChYKCU5BTUVTUEFDRRIJGgdkZWZhdWx0CkkKBU9XTkVSEkAaPmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9kZWZhdWx0L2RlcGxveW1lbnRzL3NsZWVwChcKEVBMQVRGT1JNX01FVEFEQVRBEgIqAAoYCg1XT1JLTE9BRF9OQU1FEgcaBXNsZWVw", 
        "X-Envoy-Peer-Metadata-Id": "sidecar~172.22.152.249~sleep-854565cb79-gm6hj.default~default.svc.cluster.local"
      }
    }
    [root@m1 ~]# 
    

    服务入口的配置选项

    image.png

    流量转移:灰度发布是如何实现的?

    官方文档:

    蓝绿部署

    image.png

    蓝绿部署简单理解就是当需要部署新版本时,不会去动老版本的服务,而是在另一个环境部署相同数量的新服务,然后当新的服务测试确认 OK 后,把流量切到新的服务这边来。

    其实这种部署方式,就是我们说的预发环境。生产线上有两套相同的集群,一套是 Prod 是真实服务的,另一套是 Stage 是预发环境,发布发 Stage,然后把流量切到 Stage 这边,于是 Stage 就成了 Prod,而之前的 Prod 则成了 Stage。有点像换页似的。

    这种方式的优点是没有停机,实时发布和升级,也避免有新旧版本同时在线的问题。但这种部署的问题就是有点浪费,因为需要使用双倍的资源(不过,这只是在物理机时代,在云计算时代没事,因为虚拟机部署完就可以释放了)。

    另外,如果我们的服务中有状态,比如一些缓存什么的,停机部署和蓝绿部署都会有问题。

    灰度发布(金丝雀发布)

    image.png

    金丝雀部署又叫灰度部署,其得名来源于矿井中的金丝雀。17 世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱。而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为 ” 瓦斯检测指标 “,以便在危险状况下紧急撤离。

    灰度部署是指逐渐将生产环境流量从老版本切换到新版本。通常流量是按比例分配的。例如 90% 的请求流向老版本,10% 的请求流向新版本。然后没有发现问题,就逐步扩大新版本上的流量,减少老版本上的流量。

    除了切流量外,对于多租户的平台,例如云计算平台,灰度部署也可以将一些新的版本先部署到一些用户上,如果没有问题,扩大部署,直到全部用户。一般的策略是,从内部用户开始,然后是一般用户,最后是大客户。

    这个技术大多数用于缺少足够测试,或者缺少可靠测试,或者对新版本的稳定性缺乏信心的情况下。把一部分用户切到新版上来,然后看一下有没有问题。如果没有问题就继续扩大升级,直到全部升级完成。

    A/B 测试

    image.png

    AB 测试和蓝绿部署或是金丝雀灰度部署完全是不一样的。AB 测试是同时上线两个版本,然后做相关的比较。它是用来测试应用功能表现的方法,例如可用性、受欢迎程度、可见性等。

    蓝绿部署是为了不停机,灰度部署是对新版本的质量没信心。而 AB 测试是对新版的功能没信心。注意,一个是质量,一个是功能。

    比如,网站 UI 大改版,推荐算法的更新,流程的改变,我们不知道新的版本否会得到用户青睐或是能得到更好的用户体验,我们需要收集一定的用户数据才能知道。

    于是我们需要在生产线上发布两个版本,拉一部分用户过来当小白鼠,然后通过科学的观测得出来相关的结论。AB 测试旨在通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量时可信。

    我们可以看到 AB 测试,其包含了灰度发布的功能。也就是说,我们的观测如果只是观测有没有 bug,那就是灰度发布了。当然,如果我们复杂一点,要观测用户的一些数据指标,这完全也可能做成自动化的,如果新版本数据好,就自动化地切一点流量过来,如果不行,就换一批用户(样本)再试试。

    对于灰度发布或是 AB 测试可以使用下面的技术来选择用户:

    • 浏览器 cookie、查询参数、地理位置。技术支持,如浏览器版本、屏幕尺寸、操作系统等。客户端语言

    实践基于权重的路由

    在Istio中我们可以配置基于权重的路由,将请求按比例路由到对应的服务版本来实现灰度发布的效果。接下来我们利用 reviews 服务的多版本,模拟灰度发布。官方demo已经提供了该配置文件,执行如下命令应用即可:

    [root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml
    virtualservice.networking.istio.io/reviews configured
    [root@m1 ~]# 
    

    virtual-service-reviews-50-v3.yaml 文件的内容如下,就是通过 Virtual Service 配置了权重:

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: reviews
    spec:
      hosts:
        - reviews
      http:
      - route:
        - destination:
            host: reviews
            subset: v1
          weight: 50   # 权重配置,表示50%的流量转发到v1版本
        - destination:
            host: reviews
            subset: v3
          weight: 50   # 表示50%的流量转发到v3版本
    

    此时到应用页面上进行一下测试,可以发现请求基本按照50%的比例转发到v1和v3版本:


    image.png
    image.png

    下篇:

    相关文章

      网友评论

          本文标题:Service Mesh - Istio流量控制篇(上)

          本文链接:https://www.haomeiwen.com/subject/fcfenktx.html