理解流量路由
Istio 的目标之一是充当“透明代理”,可以将其放入现有集群中,从而允许流量像以前一样继续流动。但是,由于 Istio 具有请求负载均衡等附加功能,因此它可以以与典型 Kubernetes 集群不同的方式管理流量。为了了解网格中发生的情况,了解 Istio 如何路由流量非常重要。
前端和后端
Istio 的流量路由包含两个主要阶段
- “前端”指的是我们如何匹配正在处理的流量类型。这对于确定将流量路由到哪个后端以及应用哪些策略至关重要。例如,我们可能会读取
http.ns.svc.cluster.local
的Host
头,并识别请求的目标是http
服务。有关此匹配工作原理的更多信息,请参见下文。 - “后端”指的是在匹配流量后将其发送到的位置。使用上面的示例,在识别请求的目标是
http
服务后,我们会将其发送到该服务中的一个端点。但是,此选择并不总是那么简单;Istio 允许通过VirtualService
路由规则自定义此逻辑。
标准的 Kubernetes 网络也具有相同的概念,但它们要简单得多,并且通常是隐藏的。创建 Service
时,通常会关联一个前端——自动创建的 DNS 名称(例如 http.ns.svc.cluster.local
)和一个自动创建的 IP 地址来表示服务(ClusterIP
)。类似地,也会创建一个后端——Endpoints
或 EndpointSlice
——它表示服务选择的所有 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 服务的全部范围,但有一些注意事项。
LoadBalancer
和 NodePort
服务
这些服务是 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
)将作为TLS或HTTP匹配的额外匹配项添加。否则,将不会有任何更改,因此它将被视为未匹配的流量。
ExternalName
服务永远不能单独作为后端。相反,它仅用作现有服务的额外前端匹配项。如果显式地将其用作后端,例如在 VirtualService
目标中,则将应用相同的别名。也就是说,如果将 alias.default.svc.cluster.local
设置为目标,则请求将发送到 concrete.example.com
。如果 Istio 不认识该主机名,则请求将失败;在这种情况下,concrete.example.com
的 ServiceEntry
将使此配置生效。
服务条目
除了 Kubernetes 服务之外,还可以创建服务条目以扩展 Istio 已知服务的集合。这对于确保发送到外部服务(例如 example.com
)的流量获得 Istio 的功能非常有用。
设置了 addresses
的 ServiceEntry 将执行与 ClusterIP
服务相同的路由。
但是,对于没有任何 addresses
的服务条目,将匹配端口上的所有 IP。这可能会阻止相同端口上的未匹配流量被正确转发。因此,最好避免这种情况,或者在需要时使用专用端口。HTTP 和 TLS 不共享此约束,因为路由是根据主机名/SNI 完成的。