流量管理
Istio 的流量路由规则可以让您轻松控制服务之间流量和 API 调用的流动。Istio 简化了服务级属性(如断路器、超时和重试)的配置,并使设置重要任务(如 A/B 测试、金丝雀发布和基于百分比流量拆分的阶段式发布)变得容易。它还提供开箱即用的可靠性功能,帮助您的应用程序更能抵御依赖服务或网络故障。
Istio 的流量管理模型依赖于与您的服务一起部署的 Envoy 代理。网格服务发送和接收的所有流量(数据平面 流量)通过 Envoy 代理,使您可以轻松地绕过网格引导和控制流量,而无需对您的服务进行任何更改。
如果您有兴趣详细了解本指南中描述的功能的工作原理,您可以在 体系结构概述 中找到有关 Istio 流量管理实现的更多信息。本指南的其余部分将介绍 Istio 的流量管理功能。
介绍 Istio 流量管理
为了在网格中引导流量,Istio 需要知道所有端点的位置以及它们属于哪个服务。为了填充自己的 服务注册表,Istio 连接到服务发现系统。例如,如果您在 Kubernetes 集群上安装了 Istio,那么 Istio 会自动检测该集群中的服务和端点。
使用此服务注册表,Envoy 代理可以将流量引导到相关服务。大多数基于微服务的应用程序都有每个服务工作负载的多个实例来处理服务流量,有时称为负载均衡池。默认情况下,Envoy 代理使用最少请求模型在每个服务的负载均衡池中分配流量,其中每个请求都被路由到具有更少活动请求的主机,这些主机是从池中的两个随机选定的主机中选择的;这样,负载最重的主机将不会接收请求,直到它的负载不高于其他任何主机。
虽然 Istio 的基本服务发现和负载均衡为您提供了可用的服务网格,但这远远不是 Istio 的全部功能。在许多情况下,您可能希望对网格流量的处理方式有更细粒度的控制。您可能希望在 A/B 测试过程中将特定百分比的流量引导到新版本的某个服务,或者对特定服务实例子集的流量应用不同的负载均衡策略。您可能还希望对进入或退出网格的流量应用特殊规则,或将网格的外部依赖项添加到服务注册表中。您可以通过使用 Istio 的流量管理 API 向 Istio 添加自己的流量配置来完成所有这些操作,以及更多操作。
与其他 Istio 配置一样,API 是使用 Kubernetes 自定义资源定义 (CRDs) 指定的,您可以使用 YAML 进行配置,正如您将在示例中看到的那样。
本指南的其余部分将检查每个流量管理 API 资源以及您可以使用它们做什么。这些资源是
本指南还概述了 API 资源中内置的一些 网络弹性和测试功能。
虚拟服务
虚拟服务 与 目标规则 是 Istio 流量路由功能的关键构建块。虚拟服务可以让您配置在 Istio 服务网格中如何将请求路由到某个服务,它建立在 Istio 和您的平台提供的基本连接和发现之上。每个虚拟服务都包含一组按顺序评估的路由规则,让 Istio 将每个给定的请求与虚拟服务匹配到网格内的特定实际目标。根据您的用例,您的网格可能需要多个虚拟服务,也可能不需要任何虚拟服务。
为什么要使用虚拟服务?
虚拟服务在使 Istio 的流量管理灵活和强大方面起着关键作用。它们通过将客户端发送请求的位置与实际实现这些请求的目标工作负载之间进行强解耦来实现这一点。虚拟服务还提供了一种丰富的方式来指定发送流量到这些工作负载的不同流量路由规则。
为什么这很有用?如果没有虚拟服务,Envoy 将使用最少请求负载均衡在所有服务实例之间分配流量,如简介中所述。您可以使用您对工作负载的了解来改善这种行为。例如,有些工作负载可能代表不同的版本。这在 A/B 测试中非常有用,在 A/B 测试中,您可能希望根据不同服务版本的百分比配置流量路由,或者将来自内部用户的流量引导到特定的一组实例。
使用虚拟服务,您可以为一个或多个主机名指定流量行为。您在虚拟服务中使用路由规则来告诉 Envoy 如何将虚拟服务的流量发送到适当的目标。路由目标可以是同一服务的不同版本,也可以是完全不同的服务。
一个典型的用例是将流量发送到服务的不同版本,指定为服务子集。客户端将请求发送到虚拟服务主机,就好像它是一个单一实体一样,然后 Envoy 根据虚拟服务规则将流量路由到不同的版本:例如,“20% 的调用转到新版本”或“来自这些用户的调用转到版本 2”。这使您可以创建金丝雀发布,其中您逐步增加发送到新服务版本的流量百分比。流量路由与实例部署完全分离,这意味着实现新服务版本的实例数量可以根据流量负载进行扩展和缩减,而无需参考流量路由。相比之下,像 Kubernetes 这样的容器编排平台只支持基于实例扩展的流量分配,这很快就会变得很复杂。您可以在 使用 Istio 的金丝雀部署 中详细了解虚拟服务如何帮助金丝雀部署。
虚拟服务还允许您
- 通过单个虚拟服务来处理多个应用程序服务。例如,如果您的网格使用 Kubernetes,您可以配置虚拟服务来处理特定命名空间中的所有服务。将单个虚拟服务映射到多个“真实”服务在促进将单体应用程序转变为由独立微服务构建的复合服务方面特别有用,而无需要求服务的消费者适应这种转变。您的路由规则可以指定“对
monolith.com
的这些 URI 的调用转到microservice A
”,等等。您可以在 我们下面的一个示例 中了解它的工作原理。 - 与 网关 结合使用流量规则来控制入站和出站流量。
在某些情况下,您还需要配置目标规则来使用这些功能,因为这些规则是您指定服务子集的地方。在单独的对象中指定服务子集和其他特定于目标的策略可以让您在虚拟服务之间干净地重用这些策略。您可以在下一节中找到有关目标规则的更多信息。
虚拟服务示例
以下虚拟服务根据请求是否来自特定用户将请求路由到服务的不同版本。
apiVersion: networking.istio.io/v1
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
字段列出了虚拟服务的宿主 - 换句话说,这些路由规则适用的用户可寻址目标或目标。这是客户端在向服务发送请求时使用的地址或地址。
hosts:
- reviews
虚拟服务主机名可以是 IP 地址、DNS 名称,或者根据平台,可以是解析为完全限定域名 (FQDN) 的短名称(例如 Kubernetes 服务短名称)。您还可以使用通配符(“*”)前缀,让您为所有匹配的服务创建一组路由规则。虚拟服务主机实际上不必是 Istio 服务注册表的一部分,它们只是虚拟目标。这使您可以为网格中没有可路由条目的虚拟主机建模流量。
路由规则
http
部分包含虚拟服务的路由规则,描述了匹配条件和路由到 hosts 字段中指定的目标的 HTTP/1.1、HTTP2 和 gRPC 流量的操作(您还可以使用 tcp
和 tls
部分配置针对 TCP 和未终止的 TLS 流量的路由规则)。路由规则由您希望流量流向的目的地以及根据您的用例选择零个或多个匹配条件组成。
匹配条件
示例中的第一个路由规则有一个条件,因此以 match
字段开头。在这种情况下,您希望此路由适用于来自用户“jason”的所有请求,因此您使用 headers
、end-user
和 exact
字段来选择适当的请求。
- match:
- headers:
end-user:
exact: jason
目标
路由部分的 destination
字段指定了与该条件匹配的流量的实际目的地。与虚拟服务的宿主不同,目的地的宿主必须是 Istio 服务注册表中存在的真实目的地,否则 Envoy 将不知道将流量发送到哪里。这可以是具有代理的网格服务,也可以是使用服务条目添加的非网格服务。在这种情况下,我们在 Kubernetes 上运行,主机名是 Kubernetes 服务名称。
route:
- destination:
host: reviews
subset: v2
请注意,在本示例页面上的其他示例中,为了简单起见,我们对目标宿主使用了 Kubernetes 短名称。当评估此规则时,Istio 会根据包含路由规则的虚拟服务的命名空间添加域名后缀,以获取宿主的完全限定名称。在我们的示例中使用短名称也意味着您可以在您喜欢的任何命名空间中复制和尝试它们。
destination 部分还指定了您希望与该规则的条件匹配的请求发送到的此 Kubernetes 服务的哪个子集,在本例中是名为 v2 的子集。您将在下面有关 目标规则 的部分中看到如何定义服务子集。
路由规则优先级
路由规则是 **按顺序从上到下评估** 的,虚拟服务定义中的第一个规则优先级最高。在这种情况下,您希望任何不匹配第一个路由规则的流量都转到默认目的地,在第二个规则中指定。因此,第二个规则没有匹配条件,只是将流量直接发送到 v3 子集。
- route:
- destination:
host: reviews
subset: v3
我们建议您提供一个默认的“无条件”或基于权重的规则(如下所述)作为每个虚拟服务的最后一个规则,以确保发送到虚拟服务的流量始终至少有一条匹配的路由。
关于路由规则的更多信息
如您在上面看到的,路由规则是将特定流量子集路由到特定目的地的强大工具。您可以对流量端口、标题字段、URI 等设置匹配条件。例如,此虚拟服务允许用户将流量发送到两个独立的服务(评分和评论),就好像它们是 http://bookinfo.com/.
中更大虚拟服务的一部分一样。虚拟服务规则根据请求 URI 匹配流量并将请求直接发送到适当的服务。
apiVersion: networking.istio.io/v1
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
对于某些匹配条件,您还可以选择使用精确值、前缀或正则表达式来选择它们。
您可以将多个匹配条件添加到同一个 match
块中以对条件进行 AND 操作,或者将多个匹配块添加到同一个规则中以对条件进行 OR 操作。您还可以对任何给定的虚拟服务使用多个路由规则。这使您可以在单个虚拟服务中根据需要使路由条件变得复杂或简单。可以在 HTTPMatchRequest
参考 中找到匹配条件字段及其可能值的完整列表。
除了使用匹配条件外,您还可以按百分比“权重”分配流量。这对于 A/B 测试和金丝雀发布很有用。
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 75
- destination:
host: reviews
subset: v2
weight: 25
您还可以使用路由规则对流量执行一些操作,例如
- 追加或删除标题。
- 重写 URL。
- 为对该目的地的调用设置 重试策略。
要了解有关可用操作的更多信息,请参阅 HTTPRoute
参考。
目标规则
与 虚拟服务 一样,目标规则 是 Istio 流量路由功能的关键部分。您可以将虚拟服务视为如何将流量 **路由到** 给定目的地的方式,然后使用目标规则来配置 **针对** 该目的地的流量将发生什么。目标规则在评估虚拟服务路由规则后应用,因此它们适用于流量的“真实”目的地。
特别是,您使用目标规则来指定命名服务子集,例如按版本对给定服务的实例进行分组。然后,您可以在虚拟服务的路由规则中使用这些服务子集来控制发送到服务的不同实例的流量。
目标规则还允许您在调用整个目标服务或特定服务子集时自定义 Envoy 的流量策略,例如您首选的负载均衡模型、TLS 安全模式或断路器设置。您可以在 目标规则参考 中看到目标规则选项的完整列表。
负载均衡选项
默认情况下,Istio 使用最少请求负载均衡策略,其中请求在具有最少请求数的实例之间分配。Istio 还支持以下模型,您可以在目标规则中为发送到特定服务或服务子集的请求指定这些模型。
- 随机:请求随机转发到池中的实例。
- 加权:根据特定百分比将请求转发到池中的实例。
- 轮询:按顺序将请求转发到每个实例。
- 一致性哈希:根据 HTTP 头、cookie 或其他属性提供软会话亲缘性。
- 环形哈希:使用 Ketama 算法 对上游宿主实现一致性哈希。
- Maglev:根据 Maglev 论文 中所述,对上游宿主实现一致性哈希。
有关每个选项的更多信息,请参阅 Envoy 负载均衡文档。
目标规则示例
以下目标规则示例为 my-svc
目标服务配置了三个不同的子集,并使用不同的负载均衡策略。
apiVersion: networking.istio.io/v1
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 服务的部署中作为 metadata
应用,以标识不同的版本。
除了定义子集外,此目标规则还具有针对此目标中的所有子集的默认流量策略和子集特定的策略,该策略会覆盖针对该子集的策略。在 subsets
字段上方定义的默认策略为 v1
和 v3
子集设置了一个简单的随机负载均衡器。在 v2
策略中,在相应子集的字段中指定了一个轮询负载均衡器。
网关
您使用 网关 来管理网格的入站和出站流量,让您可以指定要进入或离开网格的流量。网关配置应用于运行在网格边缘的独立 Envoy 代理,而不是与您的服务工作负载一起运行的 sidecar Envoy 代理。
与控制进入您系统的流量的其他机制(例如 Kubernetes Ingress API)不同,Istio 网关允许您使用 Istio 流量路由的全部功能和灵活性。您可以这样做,因为 Istio 的网关资源只允许您配置第 4-6 层负载均衡属性,例如要公开的端口、TLS 设置等等。然后,您不必将应用程序层流量路由 (L7) 添加到同一个 API 资源,而是将常规 Istio 虚拟服务 绑定到网关。这使您基本上可以像管理 Istio 网格中的任何其他数据平面流量一样管理网关流量。
网关主要用于管理入站流量,但您也可以配置出站网关。出站网关允许您为离开网格的流量配置专用出口节点,让您可以限制哪些服务可以或应该访问外部网络,或者启用 出站流量的安全控制 以将安全性添加到您的网格中,例如。您还可以使用网关来配置纯内部代理。
Istio 提供了一些预配置的网关代理部署(istio-ingressgateway
和 istio-egressgateway
),您可以使用这些部署 - 两种部署都在您使用我们的 演示安装 时部署,而只有入站网关在使用我们的 默认配置文件 时部署。您可以将自己的网关配置应用于这些部署,也可以部署和配置自己的网关代理。
网关示例
以下示例显示了外部 HTTPS 入站流量的可能网关配置。
apiVersion: networking.istio.io/v1
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
credentialName: ext-host-cert
此网关配置允许来自 ext-host.example.com
的 HTTPS 流量进入端口 443 上的网格,但没有指定任何流量路由。
要指定路由并使网关按预期工作,您还必须将网关绑定到虚拟服务。使用虚拟服务的 gateways
字段执行此操作,如以下示例所示。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: virtual-svc
spec:
hosts:
- ext-host.example.com
gateways:
- ext-host-gwy
然后,您可以使用外部流量的路由规则配置虚拟服务。
服务条目
您使用 服务条目 将条目添加到 Istio 在内部维护的服务注册表中。在添加服务条目后,Envoy 代理可以将流量发送到该服务,就好像它是在您的网格中的服务一样。配置服务条目允许您管理在网格外部运行的服务的流量,包括以下任务。
您无需为网格服务要使用的每个外部服务添加服务条目。默认情况下,Istio 会配置 Envoy 代理以将请求直通到未知服务。但是,您无法使用 Istio 功能来控制未在网格中注册的目标的流量。
服务条目示例
以下示例 mesh-external 服务条目将 ext-svc.example.com
外部依赖项添加到 Istio 的服务注册表中
apiVersion: networking.istio.io/v1
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
字段指定外部资源。您可以完全限定它或使用以通配符为前缀的域名。
您可以配置虚拟服务和目标规则以更细粒度的方式控制对服务条目的流量,这与您为网格中的任何其他服务配置流量的方式相同。例如,以下目标规则调整了对我们使用服务条目配置的 ext-svc.example.com
外部服务的 TCP 连接超时
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: ext-res-dr
spec:
host: ext-svc.example.com
trafficPolicy:
connectionPool:
tcp:
connectTimeout: 1s
有关更多可能的配置选项,请参阅服务条目参考。
Sidecar
默认情况下,Istio 会配置每个 Envoy 代理以接受其关联工作负载的所有端口上的流量,并在转发流量时到达网格中的每个工作负载。您可以使用边车配置来执行以下操作
- 微调 Envoy 代理接受的端口和协议集。
- 限制 Envoy 代理可以到达的服务集。
在大型应用程序中,您可能希望限制边车可达性,因为在大型应用程序中,将每个代理配置为到达网格中的每个其他服务可能会由于内存使用量高而影响网格性能。
您可以指定希望边车配置应用于特定命名空间中的所有工作负载,或者使用 workloadSelector
选择特定工作负载。例如,以下边车配置将配置 bookinfo
命名空间中的所有服务,使其仅到达运行在同一命名空间中的服务和 Istio 控制平面(Istio 的出口和遥测功能需要)。
apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
name: default
namespace: bookinfo
spec:
egress:
- hosts:
- "./*"
- "istio-system/*"
有关更多详细信息,请参阅边车参考。
网络弹性和测试
除了帮助您在网格周围引导流量外,Istio 还提供了可选的故障恢复和故障注入功能,您可以在运行时动态配置这些功能。使用这些功能有助于您的应用程序可靠运行,确保服务网格可以容忍节点故障,并防止局部故障级联到其他节点。
超时
超时是 Envoy 代理应等待给定服务回复的时间,确保服务不会无限期地等待回复,并且调用会在可预测的时间范围内成功或失败。默认情况下,Istio 中的 HTTP 请求的 Envoy 超时被禁用。
对于某些应用程序和服务,Istio 的默认超时可能不合适。例如,超时时间过长会导致由于等待来自失败服务的回复而导致过高的延迟,而超时时间过短会导致在等待涉及多个服务的操作返回时调用不必要地失败。为了找到并使用最佳超时设置,Istio 允许您使用虚拟服务在每个服务的基础上轻松地动态调整超时,而无需编辑您的服务代码。以下是一个虚拟服务,它为对评分服务的 v1 子集的调用指定了 10 秒的超时
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
timeout: 10s
重试
重试设置指定了 Envoy 代理在初始调用失败时尝试连接到服务的最大次数。重试可以通过确保调用不会因为瞬态问题(例如临时过载的服务或网络)而永久失败来提高服务可用性和应用程序性能。重试之间的间隔(25ms+)是可变的,并由 Istio 自动确定,防止被调用服务被请求淹没。HTTP 请求的默认重试行为是在返回错误之前重试两次。
与超时一样,Istio 的默认重试行为可能不适合您的应用程序在延迟方面的需求(对失败服务的过多重试会导致速度变慢)或可用性方面。与超时一样,您也可以在虚拟服务中每个服务的基础上调整您的重试设置,而无需触及您的服务代码。您还可以通过添加每个重试超时来进一步优化您的重试行为,指定您希望每个重试尝试成功连接到服务等待的时间。以下示例配置了在初始调用失败后连接到此服务子集的最大 3 次重试,每次重试都具有 2 秒的超时。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s
断路器
断路器是 Istio 提供的另一种有用的机制,用于创建弹性的基于微服务的应用程序。在断路器中,您为对服务中单个主机的调用设置限制,例如并发连接的数量或对该主机的调用失败的次数。一旦达到该限制,断路器就会“跳闸”,并停止进一步连接到该主机。使用断路器模式可以实现快速失败,而不是让客户端尝试连接到过载或故障主机。
由于断路器适用于负载均衡池中的“真实”网格目标,因此您可以在目标规则中配置断路器阈值,这些设置适用于服务中的每个主机。以下示例将 v1 子集的 reviews
服务工作负载的并发连接数限制为 100
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
您可以在断路中了解更多关于创建断路器的信息。
故障注入
配置完网络(包括故障恢复策略)后,您可以使用 Istio 的故障注入机制来测试应用程序整体的故障恢复能力。故障注入是一种测试方法,它在系统中引入错误,以确保它能够承受和恢复错误条件。使用故障注入对于确保您的故障恢复策略不冲突或过于严格特别有用,这可能会导致关键服务不可用。
与其他用于引入错误的机制(如延迟数据包或在网络层终止 pod)不同,Istio 允许您在应用程序层注入故障。这使您可以注入更相关的故障,例如 HTTP 错误代码,以获得更相关的结果。
您可以注入两种类型的故障,两种故障都使用虚拟服务配置
- 延迟:延迟是计时故障。它们模拟增加的网络延迟或过载的上游服务。
- 中止:中止是崩溃故障。它们模拟上游服务中的故障。中止通常表现为 HTTP 错误代码或 TCP 连接失败。
例如,此虚拟服务为每 1000 个对 ratings
服务的请求中的 1 个引入 5 秒的延迟。
apiVersion: networking.istio.io/v1
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 边车代理是否在返回响应之前处理了被调用服务的故障。这意味着,如果您还在应用程序代码中设置故障恢复策略,则需要牢记两者的工作方式是独立的,因此可能会发生冲突。例如,假设您有两个超时,一个配置在虚拟服务中,另一个配置在应用程序中。应用程序为对服务的 API 调用设置了 2 秒的超时。但是,您在虚拟服务中配置了 3 秒的超时和 1 次重试。在这种情况下,应用程序的超时会先生效,因此您的 Envoy 超时和重试尝试不会生效。
虽然 Istio 故障恢复功能提高了网格中服务的可靠性和可用性,但应用程序必须处理故障或错误并采取适当的回退操作。例如,当负载均衡池中的所有实例都失败时,Envoy 会返回 HTTP 503
代码。应用程序必须实现处理 HTTP 503
错误代码所需的任何回退逻辑。