理解流量路由

Istio 的目标之一是充当“透明代理”,可以将其放入现有集群中,从而允许流量像以前一样继续流动。但是,由于 Istio 具有请求负载均衡等附加功能,因此它可以以与典型 Kubernetes 集群不同的方式管理流量。为了了解网格中发生的情况,了解 Istio 如何路由流量非常重要。

前端和后端

Istio 的流量路由包含两个主要阶段

  • “前端”指的是我们如何匹配正在处理的流量类型。这对于确定将流量路由到哪个后端以及应用哪些策略至关重要。例如,我们可能会读取 http.ns.svc.cluster.localHost 头,并识别请求的目标是 http 服务。有关此匹配工作原理的更多信息,请参见下文。
  • “后端”指的是在匹配流量后将其发送到的位置。使用上面的示例,在识别请求的目标是 http 服务后,我们会将其发送到该服务中的一个端点。但是,此选择并不总是那么简单;Istio 允许通过 VirtualService 路由规则自定义此逻辑。

标准的 Kubernetes 网络也具有相同的概念,但它们要简单得多,并且通常是隐藏的。创建 Service 时,通常会关联一个前端——自动创建的 DNS 名称(例如 http.ns.svc.cluster.local)和一个自动创建的 IP 地址来表示服务(ClusterIP)。类似地,也会创建一个后端——EndpointsEndpointSlice——它表示服务选择的所有 Pod。

协议

与 Kubernetes 不同,Istio 能够处理 HTTP 和 TLS 等应用程序级协议。这允许进行不同类型的前端匹配,而 Kubernetes 中则无法实现。

一般来说,Istio 理解三种协议类别

  • HTTP,包括 HTTP/1.1、HTTP/2 和 gRPC。请注意,这并不包括 TLS 加密流量(HTTPS)。
  • TLS,包括 HTTPS。
  • 原始 TCP 字节。

协议选择文档描述了 Istio 如何决定使用哪种协议。

“TCP”的使用可能会造成混淆,因为在其他上下文中它用于区分其他 L4 协议,例如 UDP。在 Istio 中引用 TCP 协议时,这通常意味着我们将它视为原始字节流,而不是解析 TLS 或 HTTP 等应用程序级协议。

流量路由

当 Envoy 代理收到请求时,它必须决定是否以及在何处将其转发。默认情况下,这将转发到最初请求的服务,除非自定义。其工作原理取决于使用的协议。

TCP

处理 TCP 流量时,Istio 只有少量有用的信息来路由连接——只有目标 IP 和端口。这些属性用于确定目标服务;代理配置为监听每个服务 IP(<Kubernetes ClusterIP>:<Port>)对,并将流量转发到上游服务。

对于自定义,可以配置 TCP VirtualService,它允许匹配特定的 IP 和端口,并将流量路由到与请求不同的上游服务。

TLS

处理 TLS 流量时,Istio 可获得比原始 TCP 略多一些的信息:我们可以检查 TLS 握手期间提供的SNI字段。

对于标准服务,使用与原始 TCP 相同的 IP:端口匹配。但是,对于未定义服务 IP 的服务,例如ExternalName 服务,将使用 SNI 字段进行路由。

此外,可以使用 TLS VirtualService 配置自定义路由,以匹配 SNI并将请求路由到自定义目标。

HTTP

HTTP 允许比 TCP 和 TLS 更丰富的路由。使用 HTTP,您可以路由单个 HTTP 请求,而不仅仅是连接。此外,还有许多丰富的属性可用,例如主机、路径、标头、查询参数等。

虽然 TCP 和 TLS 流量在使用或不使用 Istio 时通常表现相同(假设未应用任何配置来自定义路由),但 HTTP 存在重大差异。

  • Istio 将对单个请求进行负载均衡。通常,这是非常理想的,尤其是在存在 gRPC 和 HTTP/2 等长连接的场景中,在这些场景中,连接级负载均衡效率低下。
  • 请求根据端口和Host 标头进行路由,而不是端口和 IP。这意味着目标 IP 地址实际上会被忽略。例如,curl 8.8.8.8 -H "Host: productpage.default.svc.cluster.local" 将被路由到 productpage 服务。

未匹配的流量

如果无法使用上述方法之一匹配流量,则将其视为直通流量。默认情况下,这些请求将按原样转发,以确保 Istio 未知服务的流量(例如未创建 ServiceEntry 的外部服务)继续正常工作。请注意,转发这些请求时,不会使用双向 TLS,并且遥测收集功能有限。

服务类型

除了标准的 ClusterIP 服务之外,Istio 还支持 Kubernetes 服务的全部范围,但有一些注意事项。

LoadBalancerNodePort 服务

这些服务是 ClusterIP 服务的超集,主要用于允许外部客户端访问。这些服务类型受支持,其行为与标准 ClusterIP 服务完全相同。

无头服务

一个无头服务是一个未分配 ClusterIP 的服务。相反,DNS 响应将包含服务中每个端点(即 Pod IP)的 IP 地址。

通常,Istio 不会为每个 Pod IP 配置侦听器,因为它在服务级别工作。但是,为了支持无头服务,将为无头服务中的每个 IP:端口对设置侦听器。一个例外是声明为 HTTP 的协议,它将根据 Host 标头匹配流量。

ExternalName 服务

一个ExternalName 服务本质上只是一个 DNS 别名。

为了使问题更具体,请考虑以下示例

apiVersion: v1
kind: Service
metadata:
  name: alias
spec:
  type: ExternalName
  externalName: concrete.example.com

由于没有 ClusterIP 也没有 Pod IP 可供匹配,因此对于 TCP 流量,Istio 中的流量匹配没有任何变化。当 Istio 收到请求时,它们将看到 concrete.example.com 的 IP。如果这是 Istio 知道的服务,它将按照上面所述进行路由。否则,它将被视为未匹配的流量

对于根据主机名匹配的 HTTP 和 TLS,情况略有不同。如果目标服务(concrete.example.com)是 Istio 知道的服务,则别名主机名(alias.default.svc.cluster.local)将作为TLSHTTP匹配的额外匹配项添加。否则,将不会有任何更改,因此它将被视为未匹配的流量

ExternalName 服务永远不能单独作为后端。相反,它仅用作现有服务的额外前端匹配项。如果显式地将其用作后端,例如在 VirtualService 目标中,则将应用相同的别名。也就是说,如果将 alias.default.svc.cluster.local 设置为目标,则请求将发送到 concrete.example.com。如果 Istio 不认识该主机名,则请求将失败;在这种情况下,concrete.example.comServiceEntry 将使此配置生效。

服务条目

除了 Kubernetes 服务之外,还可以创建服务条目以扩展 Istio 已知服务的集合。这对于确保发送到外部服务(例如 example.com)的流量获得 Istio 的功能非常有用。

设置了 addresses 的 ServiceEntry 将执行与 ClusterIP 服务相同的路由。

但是,对于没有任何 addresses 的服务条目,将匹配端口上的所有 IP。这可能会阻止相同端口上的未匹配流量被正确转发。因此,最好避免这种情况,或者在需要时使用专用端口。HTTP 和 TLS 不共享此约束,因为路由是根据主机名/SNI 完成的。

这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!