前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Service Mesh - Istio流量控制篇(上)

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

作者头像
端碗吹水
发布2020-12-23 11:47:05
1.5K0
发布2020-12-23 11:47:05
举报
文章被收录于专栏:程序猿的大杂烩

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

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

基本概念

虚拟服务(Virtual Service):

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

目标规则(Destination Rule):

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

实践动态路由

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

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

代码语言:javascript
复制
[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 服务:

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

  • 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 服务创建的虚拟服务配置内容如下:

代码语言:javascript
复制
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 服务创建的目标规则配置内容如下:

代码语言:javascript
复制
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

实践创建网关

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

Gateway资源的一些配置选项

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

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

代码语言:javascript
复制
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:

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

代码语言:javascript
复制
[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 ~]# 

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

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

Gateway 的应用场景:

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

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

官方文档:

什么是服务入口(ServiceEntry):

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

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

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

代码语言:javascript
复制
[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 这个外部服务:

代码语言:javascript
复制
[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):

代码语言:javascript
复制
[root@m1 ~]# istioctl install --set profile=demo -y --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

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

代码语言:javascript
复制
[root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
[root@m1 ~]# 

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

代码语言:javascript
复制
$ 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

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

代码语言:javascript
复制
[root@m1 ~]# kubectl get se
NAME          HOSTS             LOCATION        RESOLUTION   AGE
httpbin-ext   ["httpbin.org"]   MESH_EXTERNAL   DNS          97s
[root@m1 ~]# 

测试从 sleep 服务访问 httpbin :

代码语言:javascript
复制
[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 ~]# 

服务入口的配置选项


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

官方文档:

蓝绿部署

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

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

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

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

灰度发布(金丝雀发布)

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

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

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

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

A/B 测试

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

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

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

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

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

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

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

实践基于权重的路由

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

代码语言:javascript
复制
[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 配置了权重:

代码语言:javascript
复制
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版本:


下篇:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/12/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态路由:用Virtual Service和Destination Rule设置路由规则
    • 基本概念
      • 实践动态路由
      • 网关:用Gateway管理进入网格的流量
        • 什么是网关(Gateway)
          • 实践创建网关
          • 服务入口:用Service Entry扩展你的网格服务
          • 流量转移:灰度发布是如何实现的?
            • 蓝绿部署
              • 灰度发布(金丝雀发布)
                • A/B 测试
                  • 实践基于权重的路由
                  相关产品与服务
                  服务网格
                  服务网格(Tencent Cloud Mesh, TCM),一致、可靠、透明的云原生应用通信网络管控基础平台。全面兼容 Istio,集成腾讯云基础设施,提供全托管服务化的支撑能力保障网格生命周期管理。IaaS 组网与监控组件开箱即用,跨集群、异构应用一致发现管理加速云原生迁移。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档