「 Istio 」流量管理 — 整体概述

「 Istio 」流量管理 — 整体概述


based on 1.15.0

Istio 的流量路由规则可以很容易的控制服务之间的流量和 API 调用。Istio 简化了服务级别属性的配置,比如熔断器、超时和重试,并且能轻松的设置重要的任务,如 A/B 测试、金丝雀发布、基于流量百分比切分的概率发布等。它还提供了开箱即用的故障恢复特性,有助于增强应用的健壮性,从而更好地应对被依赖的服务或网络发生故障的情况。

Istio 的流量管理模型源于和服务一起部署的 Envoy 代理。网格内服务发送和接收的所有流量(data plane 流量)都经由 Envoy 代理,这让控制网格内的流量变得异常简单,而且不需要对服务做任何的更改。

整体概述

为了在网格中导流,Istio 需要知道所有的 endpoint 在哪和属于哪个服务。为了定位到 Service Registry(服务注册中心),Istio 会连接到一个服务发现系统。例如,如果在 Kubernetes 集群上安装了 Istio,那么它将自动检测该集群中的服务和 endpoint。

使用此服务注册中心,Envoy 代理可以将流量定向到相关服务。大多数基于微服务的应用程序,每个服务的工作负载都有多个实例来处理流量,称为负载均衡池。默认情况下,Envoy 代理基于轮询调度模型在服务的负载均衡池内分发流量,按顺序将请求发送给池中每个成员,一旦所有服务实例均接收过一次请求后,重新回到第一个池成员。

Istio 基本的服务发现和负载均衡能力提供了一个可用的服务网格,但它能做到的远比这多的多。在许多情况下,可能希望对网格的流量情况进行更细粒度的控制。作为 A/B 测试的一部分,可能想将特定百分比的流量定向到新版本的服务,或者为特定的服务实例子集应用不同的负载均衡策略。可能还想对进出网格的流量应用特殊的规则,或者将网格的外部依赖项添加到服务注册中心。通过使用 Istio 的流量管理 API 将流量配置添加到 Istio,就可以完成所有这些甚至更多的工作。

和其他 Istio 配置一样,这些 API 也使用 Kubernetes CRD 来声明,可以像示例中看到的那样使用 YAML 进行配置。

核心 API 包括:Virtual Services,Destination Rules,Gateways,Service Entries 和 Sidecars。

Virtual Service

Virtual Service 和 Destination Rule 是 Istio 流量路由功能的关键拼图。Virtual Service 配置如何在服务网格内将请求路由到服务,这基于 Istio 和平台提供的基本的连通性和服务发现能力。每个 Virtual Service 包含一组路由规则,Istio 按顺序评估它们,Istio 将每个给定的请求匹配到 Virtual Service 指定的实际目标地址。网格可以有多个 Virtual Service,也可以没有,取决于使用场景。

为什么要使用 Virtual Service

Virtual Service 在增强 Istio 流量管理的灵活性和有效性方面,发挥着至关重要的作用,通过对客户端请求的目标地址与真实响应请求的目标工作负载进行解耦来实现。Virtual Service 同时提供了丰富的方式,为发送至这些工作负载的流量指定不同的路由规则。

如果没有 Virtual Service,Envoy 会在所有的服务实例中使用轮询的负载均衡策略分发请求,而有些场景下有着不同的分发需求。例如,有些可能代表不同的版本(A/B 测试),希望在其中配置基于不同服务版本的流量百分比路由,或者路由内部用户到特定实例集的流量。

使用 Virtual Service,可以为一个或多个主机名指定流量行为。在 Virtual Service 中使用路由规则,告诉 Envoy 如何发送 Virtual Service 的流量到适当的目标。路由目标地址可以是同一服务的不同版本,也可以是完全不同的服务。

一个典型的用例是将流量发送到指定服务子集的不同版本。客户端将 Virtual Service 视为一个单一实体,将请求发送至 Virtual Service 主机,然后 Envoy 根据 Virtual Service 规则把流量路由到不同的版本。例如,将 20% 的调用转到新版本或者将这些用户的调用转到版本 2。例如,创建一个金丝雀发布,逐步增加发送到新版本服务的流量百分比。流量路由完全独立于实例部署,这意味着实现新版本服务的实例可以根据流量的负载来伸缩,完全不影响流量路由。

Virtual Service 可以

  • 通过单个 Virtual Service 处理多个应用程序服务。如果网格使用 Kubernetes,可以配置一个 Virtual Service 处理特定命名空间中的所有服务。映射单一的 Virtual Service 到多个真实 Service,可以在不需要客户适应转换的情况下,将单体应用转换为微服务构建的复合应用系统
  • 和 Gateway 整合并配置流量规则来控制出入流量

在某些情况下,还需要配置 Destination Rule(用于指定服务子集)来使用这些特性。在一个单独的对象中指定服务子集和其它特定目标策略,有利于在 Virtual Service 之间更简洁地重用这些规则。

Virtual Service 示例

下面的 Virtual Service 根据请求是否来自特定的用户,把它们路由到服务的不同版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v3

hosts

使用 hosts 字段列举 Virtual Service 的主机——即用户指定的目标或是路由规则设定的目标。这是客户端向服务发送请求时使用的一个或多个地址。

1
2
hosts:
- reviews

Virtual Service 主机名可以是 IP 地址、DNS 名称,或者依赖于平台的一个简称(例如 Kubernetes 服务的短名称),隐式或显式地指向一个完全限定域名(FQDN)。也可以使用通配符(“*”)前缀,创建一组匹配所有服务的路由规则。Virtual Service 的 hosts 字段实际上不必是 Istio 服务注册的一部分,它只是虚拟的目标地址。可以为没有路由到网格内部的虚拟主机建模。

路由规则

在 http 字段包含了 Virtual Service 的路由规则,用来描述匹配条件和路由行为,它们把 HTTP/1.1、HTTP2 和 gRPC 等流量发送到 hosts 字段指定的目标(也可以用 tcp 和 tls 为 TCP 和未终止的 TLS 流量设置路由规则)。一个路由规则包含了指定的请求要流向哪个目标地址,具有 0 或多个匹配条件,取决于使用场景。

匹配条件

示例中的第一个路由规则有一个条件,因此以 match 字段开始。在本例中,希望此路由应用于来自 jason 用户的所有请求,所以使用 headers、end-user 和 exact 字段匹配相关请求。

1
2
3
4
- match:
- headers:
end-user:
exact: jason

目的地

route 部分的 destination 字段指定了符合此条件的流量的实际目标地址。与 Virtual Service 的 hosts 不同,destination 的 host 必须是存在于 Istio 服务注册中心的实际目标地址,否则 Envoy 不知道该将请求发送到哪里。可以是一个有代理的服务网格,或者是一个通过 Service Entry 被添加进来的非网格服务。

1
2
3
4
route:
- destination:
host: reviews
subset: v2

在示例中,为了简单,使用 Kubernetes 的短名称设置 destination 的 host。在评估此规则时,Istio 会添加一个基于 Virtual Service 命名空间的域后缀,这个 Virtual Service 包含要获取主机的完全限定名的路由规则。在示例中使用短名称也意味着可以复制并在任何命名空间中尝试它们。

只有在目标主机和 Virtual Service 位于相同的 Kubernetes 命名空间时才可以使用这样的短名称。因为使用 Kubernetes 的短名称容易导致配置出错,建议在生产环境中指定完全限定的主机名

destination 还指定了 Kubernetes 服务的子集,将符合此规则条件的请求转入其中。在本例中子集名称是 v2。

路由规则优先级

路由规则按从上到下的顺序选择,Virtual Service 中定义的第一条规则有最高优先级。本示例中,不满足第一个路由规则的流量均流向一个默认的目标(即 v2 子集),该目标在第二条规则中指定。因此,第二条规则没有 match 条件,直接将流量导向 v3 子集。

1
2
3
4
- route:
- destination:
host: reviews
subset: v3

建议提供一个默认的”无条件”或基于权重的规则作为每一个 Virtual Service 的最后一条规则,从而确保流经 Virtual Service 的流量至少能够匹配一条路由规则。

路由规则的更多内容

路由规则是将特定流量子集路由到指定目标地址的强大工具。可以在流量端口、header 字段、URI 等内容上设置匹配条件。例如,这个 Virtual Service 让用户发送请求到两个独立的服务:ratings 和 reviews。Virtual Service 规则根据请求的 URI 和指向适当服务的请求匹配流量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
http:
- match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
- match:
- uri:
prefix: /ratings
route:
- destination:
host: ratings
...

http:
- match:
sourceLabels:
app: reviews
route:
...

有些匹配条件可以使用精确的值,如前缀或正则。

可以使用 AND 向同一个 match 块添加多个匹配条件,或者使用 OR 向同一个规则添加多个 match 块。对于任何给定的 Virtual Service 也可以有多个路由规则。

另外,使用匹配条件可以按百分比”权重“分发请求。这在 A/B 测试和金丝雀发布中非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 75
- destination:
host: reviews
subset: v2
weight: 25

也可以使用路由规则在流量上执行一些操作,例如:

  • 添加或删除 header
  • 重写 URL
  • 为调用这一目标地址的请求设置重试策略

Destination Rule

与 Virtual Service 一样,Destination Rule 也是 Istio 流量路由功能的关键部分。可以将 Virtual Service 视为将流量如何路由到给定目标地址,然后使用 Destination Rule 来配置该目标的流量。在评估 Virtual Service 路由规则之后,Destination Rule 将应用于流量的真实目标地址。

可以使用 Destination Rule 来指定命名的服务子集,例如按版本为所有给定服务的实例分组。然后可以在 Virtual Service 的路由规则中使用这些服务子集来控制到服务不同实例的流量。

Destination Rule 还允许在调用整个目的地服务或特定服务子集时定制 Envoy 的流量策略,比如负载均衡模型、TLS 安全模式或熔断器设置。

负载均衡选项

默认情况下,Istio 使用轮询的负载均衡策略,实例池中的每个实例依次获取请求。Istio 同时支持如下的负载均衡模型,可以在 DestinationRule 中为流向某个特定服务或服务子集的流量指定这些模型。

  • Random:请求以随机的方式转到池中的实例
  • Weighted:请求根据指定的百分比转到实例
  • Least requests:请求被转到最少被访问的实例

Destination Rule 示例

下面的示例为 my-svc 目标服务配置了 3 个具有不同负载均衡策略的子集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-destination-rule
spec:
host: my-svc
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
- name: v3
labels:
version: v3

每个子集都是基于一个或多个 labels 定义的,在 Kubernetes 中它是附加到像 Pod 这种对象上的键/值对。这些标签应用于 Kubernetes 服务的 Deployment 并作为 metadata 来识别不同的版本。

除了定义子集之外,Destination Rule 对于所有子集都有默认的流量策略,而对于该子集,则有特定于子集的策略覆盖它。定义在 subsets 上的默认策略,为 v1 和 v3 子集设置了一个简单的随机负载均衡器。为 v2 子集设置了一个轮询负载均衡器。

Gateway

使用 Gateway 为网格来管理入站和出站流量,可以指定要进入或离开网格的流量。网关配置被用于运行在网格边界的独立 Envoy 代理,而不是服务工作负载的 sidecar 代理。

与 Kubernetes Ingress API 这种控制进入系统流量的其他机制不同,Istio Gateway 充分利用流量路由的强大能力和灵活性。可以这么做的原因是 Istio 的 Gateway 资源可以配置 4-6 层的负载均衡属性,如对外暴露的端口、TLS 设置等。作为替代应用层流量路由(L7)到相同的 API 资源,绑定了一个常规的 Istio Virtual Service 到 Gateway 。可以像管理网格中其他数据平面的流量一样去管理网关流量。

Gateway 主要用于管理进入的流量,但也可以配置出口 Gateway 。出口 Gateway 为离开网格的流量配置一个专用的出口节点,这可以限制哪些服务可以或应该访问外部网络,或者启用出口流量安全控制为网格添加安全性。也可以使用 Gateway 配置一个纯粹的内部代理。

Istio 提供了一些预先配置好的 Gateway 代理服务(istio-ingressgateway 和 istio-egressgateway)。

Gateway 示例

下面的示例展示了一个外部 HTTPS 入口流量的网关配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ext-host-gwy
spec:
selector:
app: my-gateway-controller
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- ext-host.example.com
tls:
mode: SIMPLE
serverCertificate: /tmp/tls.crt
privateKey: /tmp/tls.key

这个 Gateway 配置让 HTTPS 流量从 ext-host.example.com 通过 443 端口流入网格,但没有为请求指定任何路由规则。

为想要生效的 Gateway 指定路由,必须把 Gateway 绑定到 Virtual Service 上。然后就可以为出口流量配置带有路由规则的 Virtual Service。

1
2
3
4
5
6
7
8
9
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: virtual-svc
spec:
hosts:
- ext-host.example.com
gateways:
- ext-host-gwy

Service Entry

使用 Service Entry 来添加一个入口到 Istio 内部维护的服务注册中心。添加了 Service Entry 后,Envoy 代理可以向服务发送流量,就好像它是网格内部的服务一样。配置 Service Entry 允许管理运行在网格外的服务的流量,它包括以下几种能力:

  • 为外部目标 redirect 和转发请求,例如来自 web 端的 API 调用,或者流向遗留老系统的服务
  • 为外部目标定义重试、超时和故障注入策略
  • 添加一个运行在虚拟机的服务来扩展您的网格

不需要为网格服务要使用的每个外部服务都添加 Service Entry。默认情况下,Istio 配置 Envoy 代理将请求传递给未知服务。但是,不能使用 Istio 的特性来控制没有在网格中注册的目标流量。

Service Entry 示例

下面的 Service Entry 将外部服务 ext-svc.example.com 添加到 Istio 的服务注册中心:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: svc-entry
spec:
hosts:
- ext-svc.example.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS

指定的外部资源使用 hosts 字段。可以使用完全限定名或通配符作为前缀域名。

可以配置 Virtual Service 和 Destination Rule,以更细粒度的方式控制到 Service Entry 的流量,这与网格中的任何其他服务配置流量的方式相同。

例如,下面的 Destination Rule 调整了使用 Service Entry 配置的 ext-svc.example.com 外部服务的连接超时:

1
2
3
4
5
6
7
8
9
10
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ext-res-dr
spec:
host: ext-svc.example.com
trafficPolicy:
connectionPool:
tcp:
connectTimeout: 1s

Sidecar

默认情况下,Istio 让每个 Envoy 代理都可以访问来自和它关联的工作负载的所有端口的请求,然后转发到对应的工作负载。可以使用 Sidecar 配置去做下面的事情:

  • 微调 Envoy 代理接受的端口和协议集
  • 限制 Envoy 代理可以访问的服务集合

可能希望在较庞大的应用程序中限制这样的 Sidecar 可达性,配置每个代理能访问网格中的任意服务可能会因为高内存使用量而影响网格的性能。

可以指定将 Sidecar 配置应用于特定命名空间中的所有工作负载,或者使用 workloadSelector 选择特定的工作负载。

例如,下面的 Sidecar 配置将 bookinfo 命名空间中的所有服务配置为仅能访问运行在相同命名空间和 istio-system 服务:

1
2
3
4
5
6
7
8
9
10
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: default
namespace: bookinfo
spec:
egress:
- hosts:
- "./*"
- "istio-system/*"

网络弹性和测试

除了为网格导流之外,Istio 还提供了可选的故障恢复和故障注入功能,可以在运行时动态配置这些功能。使用这些特性可以让应用程序运行稳定,确保服务网格能够容忍故障节点,并防止局部故障级联影响到其他节点。

超时

超时是 Envoy 代理等待来自给定服务的答复的时间量,以确保服务不会因为等待答复而无限期的挂起,并在可预测的时间范围内调用成功或失败。HTTP 请求的默认超时时间是 15 秒,这意味着如果服务在 15 秒内没有响应,调用将失败。

对于某些应用程序和服务,Istio 的缺省超时可能不合适。例如,超时太长可能会由于等待失败服务的回复而导致过度的延迟;而超时过短则可能在等待涉及多个服务返回的操作时触发不必要地失败。为了找到并使用最佳超时设置,Istio 允许使用 Virtual Service 按服务轻松地动态调整超时,而不必修改业务代码。

例如,下面的 Virtual Service 配置对 ratings 服务的 v1 子集的调用指定 10 秒超时:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
timeout: 10s

重试

重试设置指定如果初始调用失败,Envoy 代理尝试连接服务的最大次数。通过确保调用不会因为临时过载的服务或网络等问题而永久失败,重试可以提高服务可用性和应用程序的性能。重试之间的间隔(25ms+)是可变的,并由 Istio 自动确定,从而防止被调用服务被请求淹没。HTTP 请求的默认重试行为是在返回错误之前重试两次。

与超时一样,Istio 默认的重试行为在延迟方面可能不适合应用程序需求(对失败的服务进行过多的重试会降低速度)或可用性。可以在 Virtual Service 中按服务调整重试设置,而不必修改业务代码。还可以通过添加每次重试的超时来进一步细化重试行为,并指定每次重试都试图成功连接到服务所等待的时间量。

例如,下面的 Virtual Service 配置在初始调用失败后最多重试 3 次来连接到服务子集,每个重试都有 2 秒的超时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s

熔断器

熔断器是 Istio 为创建具有弹性的微服务应用提供的另一个有用的机制。在熔断器中,设置一个对服务中的单个主机调用的限制,例如并发连接的数量或对该主机调用失败的次数。一旦限制被触发,熔断器就会“跳闸”并停止连接到该主机。使用熔断模式可以快速失败而不必让客户端尝试连接到过载或有故障的主机。

熔断适用于在负载均衡池中的真实网格目标地址,可以在 Destination Rule 中配置熔断器阈值,让配置适用于服务中的每个主机。

例如,下面的 Destination Rule 配置将 v1 子集的 reviews 服务工作负载的并发连接数限制为 100:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100

故障注入

在配置了网络,包括故障恢复策略之后,可以使用 Istio 的故障注入机制来为整个应用程序测试故障恢复能力。故障注入是一种将错误引入系统以确保系统能够承受并从错误条件中恢复的测试方法。使用故障注入特别有用,能确保故障恢复策略不至于不兼容或者太严格,这会导致关键服务不可用。

目前,故障注入配置不能与同一个 Virtual Service 上的重试或超时配置相结合

与其他错误注入机制(如延迟数据包或在网络层杀掉 Pod)不同,Istio 允许在应用层注入错误。这可以注入更多相关的故障,例如 HTTP 错误码,以获得更多相关的结果。

可以注入两种故障,它们都使用 Virtual Service 配置:

  • 延迟:延迟是时间故障。它们模拟增加的网络延迟或一个超载的上游服务
  • 终止:终止是崩溃失败。它们模仿上游服务的失败。终止通常以 HTTP 错误码或 TCP 连接失败的形式出现

例如,下面的 Virtual Service 示例为千分之一的访问 ratings 服务的请求配置了一个 5 秒的延迟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 0.1
fixedDelay: 5s
route:
- destination:
host: ratings
subset: v1

与应用程序协同工作

Istio 故障恢复功能对应用程序来说是完全透明的。在返回响应之前,应用程序不知道 Envoy Sidecar 代理是否正在处理被调用服务的故障。这意味着,如果在应用程序代码中设置了故障恢复策略,那么需要记住这两个策略都是独立工作的,否则会发生冲突。例如,假设设置了两个超时,一个在 Virtual Service 中配置,另一个在应用程序中配置。应用程序为服务的 API 调用设置了 2 秒超时。而在 Virtual Service 中配置了一个 3 秒超时和重试。在这种情况下,应用程序的超时会先生效,因此 Envoy 的超时和重试尝试会失效。

虽然 Istio 故障恢复特性提高了网格中服务的可靠性和可用性,但应用程序必须处理故障或错误并采取适当的回退操作。例如,当负载均衡中的所有实例都失败时,Envoy 返回一个 HTTP 503 状态码。应用程序必须实现回退逻辑来处理 HTTP 503 错误代码。

Author

Shen Xianghong

Posted on

2022-07-04

Updated on

2023-06-19

Licensed under