了解流量路由

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” 的使用可能会令人困惑,因为在其他上下文中 TCP 用于区分 UDP 等其他 L4 协议。 当涉及到 Istio 中的 TCP 协议时,这通常意味着我们将其视为原始的字节流, 并且不解析 TLS 或 HTTP 这类应用程序级协议。

流量路由

当 Envoy 代理收到请求时,必须决定将此请求转发到哪里。 默认将转发到被请求的原始服务,除非进行了自定义。 处理方式取决于使用的协议。

TCP

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

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

TLS

处理 TLS 流量时,Istio 提供了比原始 TCP 更多的可用信息: 我们可以检查 TLS 握手期间呈现的 SNI 字段。

对于标准服务,与原始 TCP 一样使用相同的 IP:Port 匹配机制。 然而对于未定义 Service 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 服务的超集,并且主要与允许来自外部客户端的访问有关。 Istio 支持这些服务类型,并且与标准的 ClusterIP 服务具有完全相同的行为。

无头服务

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

总体而言,Istio 不会为每个 Pod IP 配置侦听器,因为它作用于服务级别。 但是,为了支持无头服务,在无头服务中的每个 IP:Port 对上设置侦听器。 一个例外是对于声明为 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.com 创建一个 ServiceEntry 可以使此配置生效。

ServiceEntry

除了 Kubernetes 服务之外,可以创建服务条目来扩展 Istio 已知的服务集。这可用于确保到外部服务(例如 example.com)的流量能够加持 Istio 的各项功能。

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

但是,对于没有任何 addresses 的服务条目,将匹配端口上的所有 IP。 这可能会防止在同一端口上的不匹配流量被正确转发。 因此,在可能的情况下最好避免使用它们,或者在需要时使用专用端口。 HTTP 和 TLS 不共享此约束条件,因为基于 hostname/SNI 进行路由。

这些信息有用吗?
您是否有更多建议和改进意见?

感谢您的反馈!