Traffic Management

Overview

这篇介绍在Istio中流量管理如何工作,包括流量管理原则的优势。假设你已经读过What is Istio并熟悉Istio的高级架构。

Pilot and Envoy

在Istio中,流量管理的核心组件使Pilot,它管理和配置部署在Istio服务网格中的所有Envoy代理实例。它让你指定Envoy代理之间路由流量所使用的规则,配置故障恢复功能,如超时、重试、熔断。它也维护了一个在网格中所有服务的标准模型,并使用它来让Envoy通过其发现服务了解网格中的其他实例。
每个Envoy实例基于从Pilot获得的信息来维护负载均衡信息,并定期对它的负载均衡池中的其他实例进行健康检查,使它按照指定的路由规则在目标实例间智能分配流量。

Traffic management benefits

使用Istio流量管理模型本质上解耦了流量和基础架构扩展,让运维通过Pilot指定它们希望流量遵循哪些规则,而不是哪些特定的pods/VMs接受流量——Pilot 和 智能Envoy代理负责监控其余部分。例如,你可以通过Pilot指定你希望某个特定服务的5%流量进入金丝雀版本,而无需考虑金丝雀部署的大小;或者根据请求的内容将流量发送到特定版本。
这里写图片描述
像这样从基础架构解耦流量允许Istio在应用代码外部提供流量管理功能。除了A/B测试,滚动发布和金丝雀部署的动态请求路由,它还使用超时、重试、熔断来处理故障恢复,最后进行故障注入以测试跨服务的故障恢复策略的兼容性。这些功能都是通过在服务网格中部署Envoy sidecar/proxies来实现的。

Pilot

Pilot负责跨Istio服务网格部署的Envoy实例的生命周期。
这里写图片描述
如上图,Pilot维护独立与底层平台的网格中的服务的规范表示。Pilot中的特定平台适配器负责适当填充此规范模型。比如,Pilot中的k8s适配器实现必要的控制器,以观察k8s API server中更改的pod注册信息、入口资源和存储流量管理规则的第三方资源。这些数据被转换为规范表示。指定Envoy的配置是基于规范表示生成的。

Pilot公开了用于服务发现、动态更新负载均衡池和路由表的API。这些API将Envoy和特定平台的细微差别解耦,简化了设计并提高了跨平台的可移植性。

运维可以通过Pilot的规则API指定高级流量管理规则。这些规则被转换为低级配置,并通过发现API分发给Envoy实例。

Request Routing

这篇介绍Istio服务网格中的服务间请求如何路由。

Service model and service versions

如在Pilot中的描述,Pilot维护特定网格中服务的规范表示。服务的Istio模型与其所在底层平台(Kubernetes, Mesos, Cloud Foundry, etc.)中的表现方式无关。特定平台适配器负责使用在平台中找到的元数据中的各个字段来填充内部模型表示。
Istio引入了服务版本的概念,它按版本 (v1, v2) 或环境(staging, prod)细分服务实例的实现细粒度划分。这些变体不一定是不同的API版本:他们可能是对同一服务进行迭代更改,然后部署在不同环境中(prod, staging, dev, etc.)。常用场景包括A/B测试或金丝雀发布。Istio的流量路由规则可以指定服务版本,以提供对服务间流量的额外控制。

Communication between services

这里写图片描述
如上图,服务客户端不需知道服务的不同版本。 它们可以通过服务的hostname/IP地址持续访问服务。Envoy sidecar/proxy拦截并转发客户端和服务之间的所有请求/响应。

Envoy根据运维使用Pilot指定的路由规则动态选择实际服务的版本。该模型使应用代码与其相关服务的演进解耦,同时提供了其他好处(见Mixer)。路由规则使Envoy根据标准选择版本,这些标准形如headers、与源/目标相关的标签、分配给每个版本的权重。

Istio同时为流量提供相同服务版本的多个实例的负载均衡。在Discovery and Load-Balancing产看更多。

扫描二维码关注公众号,回复: 890736 查看本文章

Istio不提供DNS解析。应用可以尝试使用依赖平台使用的DNS服务 (kube-dns, mesos-dns, etc.)解析FQDN。

Ingress and egress

Istio假设所有进出服务网格的流量都通过Envoy进行中转。通过在服务之前部署Envoy代理,运维可以进行A/B测试,金丝雀部署等面向用户的服务。同时,依托Envoy sidecar,通过路由外部web服务(如一个Maps API或video API),运维可以加入故障恢复功能,如超时、重试、熔断等,并通过这些服务的通信获取具体的metrics。
这里写图片描述

Discovery & Load Balancing

这篇介绍Istio如何在服务网格中负载均衡服务实例间的流量。

Service registration: Istio假定服务注册表存在,以追踪pods/VMs上的应用中的服务。它还假定服务的新实例会自动注册,不健康实例会自动删除。如k8s、Mesos等平台已经为基于容器的应用提供了这样的功能。基于VM的应用存在大量解决方案。

Service Discovery: Pilot使用来自服务注册中心的信息,并提供平台无关的服务发现界面。网格中Envoy实例执行服务发现,并相应地动态更新其负载均衡池。
这里写图片描述
如上图,网格中的服务通过使用它们的DNS名访问彼此。绑定到服务的所有HTTP流量都会通过Envoy自动重新路由。Envoy在负载均衡池中的实例间分配流量。尽管Envoy支持多种复杂的负载均衡算法,但是Istio目前只支持三种:轮询,随机和加权最少请求。

除了负载均衡,Envoy还会定期检查池中每个实例的健康状况。Envoy遵循断路器模式,根据健康检查API调用的故障率划分实例为不健康或健康的。换句话说,当给定实例的运行检查失败次数超过预先设定的阈值时,它将从负载均衡池中被清除。同样,当传递的运行状态健康检查数超过预先设定阈值时,它将被添加回负载均衡池。可以在Handling Failures了解更多Envoy故障处理能力。

服务可以通过HTTP 503的响应健康检查来积极地减轻负载。在这种情况下,服务实例将立即从调用者的负载均衡池中移除。

Handling Failures

Envoy提供一套开箱即用的选择性故障恢复功能,可以在应用中更好的使用服务。功能包括:
1. 超时
2. 带有超时预算的有界重试和重试间变量抖动
3. 并发连接数和上游服务请求的限制
4. 对负载均衡池中的每个成员主动(定期)健康检查
5. 细粒度熔断(被动健康检查)—— 针对负载均衡池中的每个实例

这些功能可以通过Istio的traffic management rules在运行期动态配置。

重试间的抖动最小化了重试对上游服务重载的影响,而超时预算确保呼叫服务在可预测时间范围内获得响应(成功/失败)。

主动和被动的健康检查组合(上述4和5)可最大限度减少访问负载均衡池中不健康实例的机会。当与平台级健康检查结合时(如k8s或Mesos支持的),应用可以确保不健康的pods/容器/VMs能快速从服务网格中清除,最大限度减少请求失败和延迟的影响。

这些功能一同使服务网格能够容忍失败节点,防止由于级联不稳定导致的本地失败影响到其他节点。

Fine tuning

Istio流量管理规则使运维为每个服务/版本的故障恢复设置全局默认值。然而,自定义服务可以通过特定HTTP 请求头中提供请求级覆盖去覆写超时和重试默认值。Envoy代理中,请求头分别时是 “x-envoy-upstream-rq-timeout-ms” 和“x-envoy-max-retries”。

FAQ

  1. 运行在Istio中的应用还需要处理故障吗?
    当然。Istio在网格中提供高可用服务。但是,应用需要处理故障,并采取恰当的fallback方法。例如,当负载均衡池中的所有实例失败了,Envoy会返回HTTP 503.这表示应用需要在上游服务有处理HTTP 503错误码的fallback逻辑。

  2. Envoy的故障恢复功能会破坏应用已有的容灾库吗(如Hystrix)?
    不。Envoy完全对应用透明。由Envoy返回的故障响应与进行调用的上游服务返回的故障响应没什么不同。

  3. 当同时使用Envoy和应用级库处理故障时会发生什么?
    为同一目标服务提供两个故障恢复策略(如两个超时——一个是Envoy,另一个是应用级库),当故障发生时,会触发二者中更严格的那个。例如,如果应用对一个服务的API调用设置5s超时,运维配置10s超时,则应用的超时首先启动。同样,如果Envoy的熔断在应用的熔断之前触发,服务的API调用从Envoy返回503.

Fault Injection

尽管Envoy sidecar/proxy为运行在Istio上的服务提供了很多故障恢复机制,测试整个应用的端到端故障恢复能力仍然是必要的。错误配置的故障恢复策略(如跨服务调用的不兼容/限制性超时)可能导致应用中的关键服务不可用,从而导致用户体验极差。

Istio支持将特定协议故障注入网络,而不是杀死pods、延迟或破坏TCP层的数据包。我们的依据是应用层观察到的故障应该是相同的,和网络级的失败无关;并且可以在应用层注入更多有意义的失败(如HTTP错误码)来锻炼应用的恢复能力。

运维可以配置故障注入到符合特定标准的请求中。运维可以进一步限制应该发生故障的请求的百分比。可以注入两种类型故障:delays and aborts。Delays是定时失败、模仿网络延迟增加或上游服务超负荷。Aborts是模仿上游服务故障导致的崩溃。Aborts通常以HTTP错误码或TCP连接失败的形式出现。

在Istio的traffic management rules发现更多。

Rules Configuration

Istio提供一个简单的特定领域语言(DSL)去控制如何调用API及应用部署中跨不同服务的第四层流量。DSL允许运维配置服务级属性,如熔断,超时,重试,以及建立常见的持续部署任务,如金丝雀发布,A/B测试,基于%流量分组的分阶段发布等。更多详见here
例如,如下是一个使用DSL规则描述的将“reviews”服务的传入流量100%发送到“v1”版本的简单规则:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-default
spec:
  destination:
    name: reviews
  route:
  - labels:
      version: v1
    weight: 100

destination是被路由流量的服务名。route 的labels确定接受流量的特定服务实例。例如,Istio的k8s部署中,route的labels为“version:v1”表明只有包含标签“version:v1”的pods能接受流量。

规则可以使用istioctl CLI配置,或使用k8s部署的kubectl命令。查看configuring request routing task
在Istio中由三种流量管理策略:Route Rules, Destination Policies(和Mixer policies不同),及Egress Rules。三种规则都控制请求如何路由到目标服务。

Route Rules

Route Rules控制Istio服务网格如何路由请求。例如,一个路由规则可以路由请求到一个服务的不同版本。请求可以根据源和目标、HTTP请求头字段、各服务版本相关权重进行路由。编写路由规则时,必须牢记以下几个方面:

Qualify rules by destination

每个规则对应于规则中的destination字段标识的目标服务。例如,申请调用“reviews”服务的规则通常至少包含如下内容。

destination:
  name: reviews

destination值显示或隐式的指定一个全限定名(FQDN)。被用于Istio Pilot按规则匹配服务。

通常服务的全限定名由三部分组成:name, namespace, and domain

FQDN = name + "." + namespace + "." + domain

这些字段如下显示指定。

destination:
  name: reviews
  namespace: default
  domain: svc.cluster.local

更常见的是,为了简化和最大限度的重用规则(如在多个命名空间或域中使用相同规则),规则destination仅指定name字段,依赖于其他两个默认值。
namespace的默认值是规则自己所属命名空间,可能在规则的metadata字段中指定,或在使用命令istioctl -n <namespace> createkubectl -n <namespace> create 安装rule时指定。domain的默认值时特定实现的。如在k8s中,默认值是svc.cluster.local

有时候,比如在egress rules或在namespace和domain无意义的平台上涉及外部服务时,使用可替代字段service来明确指定目的地。

destination:
  service: my-service.com

当service字段被指定,其他字段的显示或隐式值将被忽略。

Qualify rules by source/headers

规则可选择性的限定为仅适用于匹配某些特定条件的请求,如下:
1. 限制特定调用者。例如,下述规则仅允许“reviews”服务调用。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-to-ratings
spec:
  destination:
    name: ratings
  match:
    source:
      name: reviews
  ...

source值,就像destination值,显示或隐式地指定一个服务的FQDN。
2. 限定调用者的特定版本。例如,下属规则细化之前的例子,仅允许“v2”版本的“reviews”服务调用。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-v2-to-ratings
spec:
  destination:
    name: ratings
  match:
    source:
      name: reviews
      labels:
        version: v2
  ...

3. 基于HTTP请求头选择规则。例如,下属规则仅允许cookie中包含“user=jason”的请求进入。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-jason
spec:
  destination:
    name: reviews
  match:
    request:
      headers:
        cookie:
          regex: "^(.*?;)?(user=jason)(;.*)?$"
  ...

如果提供了多个header,则必须匹配所有的headers,规则才能适用。
可以同时设置多个标准。这种情况下,应用AND语法。如下属规则仅允许源请求是 “reviews:v2” AND 当前cookie中包含“user=jason”。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-reviews-jason
spec:
  destination:
    name: ratings
  match:
    source:
      name: reviews
      labels:
        version: v2
    request:
      headers:
        cookie:
          regex: "^(.*?;)?(user=jason)(;.*)?$"
  ...
Split traffic between service versions

每条路由规则标识一个或多个加权后端,以便在规则激活时进行调用。每个后端对应于目标服务的特定版本,其中可以使用labels表示版本。
如果有多个具有指定标签的注册实例时,它们将根据为服务配置的负载均衡策略进行路由,或者使用默认循环策略。
例如,如下规则路由25%的流量到“v2”标签的“reviews”服务实例,剩下的流量路由到“v1”。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-v2-rollout
spec:
  destination:
    name: reviews
  route:
  - labels:
      version: v2
    weight: 25
  - labels:
      version: v1
    weight: 75
Timeouts and retries

http请求的默认超时为15s,但可以被如下路由规则覆盖:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-timeout
spec:
  destination:
    name: ratings
  route:
  - labels:
      version: v1
  httpReqTimeout:
    simpleTimeout:
      timeout: 10s

给定http请求的重试次数也可以在路由规则中指定。最大尝试次数,或者在默认或重写的超时时限内尽可能多的尝试,可以像如下设置:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-retry
spec:
  destination:
    name: ratings
  route:
  - labels:
      version: v1
  httpReqRetries:
    simpleRetry:
      attempts: 3

注意请求超时或重试也可以被每个请求重写。
request timeouts task有超时控制的示范。

Injecting faults in the request path

路由规则可以指定一个或多个故障注入,同时将http请求转发到规则对应的请求目标。故障可能是延迟或中止。
以下例子将在“reviews”微服务的”v1“版本中的10%请求引入5s延迟。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-delay
spec:
  destination:
    name: reviews
  route:
  - labels:
      version: v1
  httpFault:
    delay:
      percent: 10
      fixedDelay: 5s

另一种故障,中止,可用于过早终止请求,例如模拟故障。

下述例子将在”ratings“服务的“v1”版本中的10%请求返回HTTP 400错误码。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-abort
spec:
   destination:
     name: ratings
   route:
   - labels:
       version: v1
   httpFault:
     abort:
       percent: 10
       httpStatus: 400

有时延迟和中止同时使用。如,下面例子将对从“reviews”的“v2”服务到“ratings”的“”v1服务的所有请求延迟5s,然后终止其中10%的请求:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: ratings-delay-abort
spec:
  destination:
    name: ratings
  match:
    source:
      name: reviews
      labels:
        version: v2
  route:
  - labels:
      version: v1
  httpFault:
    delay:
      fixedDelay: 5s
    abort:
      percent: 10
      httpStatus: 400

查看action中的失败注入,查看 fault injection task

Rules have precedence

多重路由规则可以应用到相同目标。通过设定规则precedence字段,可以指定目标对应的多个规则的评估顺序。

destination:
  name: reviews
precedence: 1

precedence字段是一个可选整数值,默认为0。precedence值越高,规则的评估越靠前。如果多个规则有相同的precedence,那么评估的顺序不定。

什么时候优先级有用?每当某个特定服务的路由纯粹基于权重时,就可以在每个规则中指定它,就像前面的例子。另一方面,当其他方面(如来自特殊用户的请求)用于路由流量时,需要多个规则来指定路由。这时就必须设置precedence来确保规则能够按正确顺序被评估。

通用路由规范的一个常见模式是提供一个或多个更高优先级的规则,将规则的source/headers限定到特定目标,然后以最低优先级提供没有匹配标准的单个基于权重的规则,来为所有其他情况提供流量的加权分布。
例如,如下两个规则一起指定header中包含键值对Foo: bar所有请求发送到“reviews”的“v2”版本实例,剩下的请求发送到“v1”中。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-foo-bar
spec:
  destination:
    name: reviews
  precedence: 2
  match:
    request:
      headers:
        Foo: bar
  route:
  - labels:
      version: v2
---
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-default
spec:
  destination:
    name: reviews
  precedence: 1
  route:
  - labels:
      version: v1
    weight: 100

注意到基于header的规则有更高的优先级(2 vs 1).如果它的优先级更低,则这些规则由于基于权重,没有特定匹配条件,将不能按预期工作,由于“v1”先被评估,所以所有流量都将路由到“v1”中,即使请求中包含匹配的“Foo” header。一旦规则能适用进入的请求,它将会执行,规则评估过程将会终止。这也是为何超过一个规则时,仔细的考虑规则优先级时非常重要的。

Destination policies

目标策略描述了与特定服务或版本相关的各种路由相关策略,如负载均衡算法,熔断配置,健康检查等。
不同于路由规则,目标策略只能由呼叫服务的请求的属性来限定,但它们可以被限制为适用于使用特定标签路由到目标后端的请求。例如,下面的负载均衡策略仅适用被“reviews”的“v2”版本调用,目标为“ratings”的“v1”版本的请求。

apiVersion: config.istio.io/v1alpha2
kind: DestinationPolicy
metadata:
  name: ratings-lb-policy
spec:
  source:
    name: reviews
    labels:
      version: v2
  destination:
    name: ratings
    labels:
      version: v1
  loadBalancing:
    name: ROUND_ROBIN
Circuit breakers

一个简单的熔断可以根据很多标准设置,如连接和请求限制。
如下目标策略设置了“reviews”的“v1”后端服务最大连接为100.

apiVersion: config.istio.io/v1alpha2
kind: DestinationPolicy
metadata:
  name: reviews-v1-cb
spec:
  destination:
    name: reviews
    labels:
      version: v1
  circuitBreaker:
    simpleCb:
       maxConnections: 100

完整的简单熔断字段在 here

Destination policy evaluation

和路由规则类似,目标策略也和特定destination有关,但如果它们也包含labels,则其激活取决于路由规则的评估结果。
规则评估过程的第一步是评估destination的路由规则(如果有定义),以确定当前请求将路由到目标服务的标签(即特定版本)。然后,该组目标策略(如果有)会被评估以确定它们是否适用。
注意:记住算法的一个微妙之处在于,只有在相应的标记实例被明确路由到时,才会应用为特定标记的目标策略。如下,作为“reviews”服务定义的唯一规则。

apiVersion: config.istio.io/v1alpha2
kind: DestinationPolicy
metadata:
  name: reviews-v1-cb
spec:
  destination:
    name: reviews
    labels:
      version: v1
  circuitBreaker:
    simpleCb:
      maxConnections: 100

因为这里没有为“reviews”服务定义特定的路由规则,所以会适用默认的循环路由行为,在某些情况下将会调用“v1”实例,甚至“v1”总是唯一运行的版本。不过,由于默认路由是在较低级别完成的,因此不会调用上述策略。规则评估引擎不知道最终目标,因此无法将目标策略与请求匹配。

你可以通过如下两种方式修复上述例子。如果“v1”是唯一实例,你可以从规则中移除 labels:;或者更好的方式是,为服务定义适当的路由规则,比如简单的为 “reviews:v1”加一个路由规则。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-default
spec:
  destination:
    name: reviews
  route:
  - labels:
      version: v1

虽然默认的Istio行为方便地将来自所有版本地源服务流量发送到目标服务地所有版本,并无需设置任何规则,但只要希望区分版本时,那么就需要规则。因此,一开始就为每项服务设置默认规则,通常被认为是Istio地最佳做法。

Egress Rules

出口规则用于适用Istio服务网格外的服务请求。如下规则,允许外部服务调用托管在 *.foo.com 域名下的服务。

apiVersion: config.istio.io/v1alpha2
kind: EgressRule
metadata:
  name: foo-egress-rule
spec:
  destination:
    service: *.foo.com
  ports:
    - port: 80
      protocol: http
    - port: 443
      protocol: https

出口规则的目标是适用service字段指定,该字段可能是完全限定的或通配的域名。它表示白名单中列出一个或多个外部服务允许访问网格中的服务。在 here查看通配符语法。
目前,只有基于HTTP的服务可以使用出口规则,然而,源于sidecar的TLS可以通过将关联的服务端口协议设置为“https”来实现,如上例所示。该服务必须通过HTTP访问(e.g., http://secure-service.foo.com:443, instead of https://secure-service.foo.com),不过,这种情况下,sidecar将升级为TLS连接。
只要它们使用与对应的出口规则完全相同的目标服务规范来引用外部服务,出口规则就可以很好的与路由规则和目标策略结合使用。例如,下述规则可以与上述出口规则结合使用,以设置对外服务调用的10s超时时间。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: foo-timeout-rule
spec:
  destination:
    service: *.foo.com
  httpReqTimeout:
    simpleTimeout:
      timeout: 10s

用于重定向和转发流量的目标策略和路由规则,来定义支持外部目标的重试、超时和故障注入策略。然而,由于没有多个外部服务版本的改变,加权(基于版本)路由不可能实现。

猜你喜欢

转载自blog.csdn.net/ybt_c_index/article/details/80249810