参考:Ingress | Kubernetes、《Kubernetes进阶实战》、《Kubernetes网络权威指南 》

何谓Ingress?从字面意思解读,就是入站流量。Kubernetes的Ingress资源对象是指授权入站连接到达集群内服务的规则集合

我们知道,Kubernetes上的NodePort和LoadBalancer类型的Service资源能够把集群内部服务暴露给集群外部客户端访问,但两个负载均衡跃点(路由)必然产生更大的网络延迟,且无疑会大大增加组织在使用云服务方面的费用开销。

因此,Kubernetes为这种需求提供了一种更为高级的流量管理约束方式,尤其是对HTTP/HTTPS协议的约束。Kubernetes使用Ingress控制器作为统一的流量入口,管理内部各种必要的服务,并通过Ingress这一API资源来描述如何区分流量以及内部的路由逻辑。有了Ingress和Ingress控制器,我们就可通过定义路由流量的规则来完成服务发布,而无须创建一堆NodePort或LoadBalancer类型的Service,而且流量也会由Ingress控制器直接到达Pod对象。

Kubernetes Ingress提供了负载平衡器的典型特性:HTTP路由、黏性会话、SSL终止、SSL直通、TCP和UDP负载平衡等。目前,并不是所有的Ingress Controller都实现了这些功能,需要查看具体的Ingress Controller文档。Ingress控制器有各种类型,包括GoogleCloud Load Balancer、Nginx、Istio等。有些Ingress Controller可能还依赖各种插件,例如cert-manager,它可以为服务自动提供SSL证书。

Ingress资源主要用于向Kubernetes集群外部发布服务,它能够通过一个统一的接口管理所有南北向的流量。

💡Tips:人们通常把Kubernetes集群内部的通信称为东西向流量,而把集群内外部的通信称为南北向流量。

下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:

image-20220317141340808

通常情况下,Service和Pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由的流量或被丢弃或被转发到其他地方。

Ingress的作用就是在边界路由处开个口子,放外部流量进来。因此,Ingress是建立在Service之上的L7访问入口,它支持通过URL的方式将Service暴露到k8s集群外;支持自定义Service的访问策略;提供按域名访问的虚拟主机功能;支持TLS通信。

Ingress规则

每个 HTTP 规则都包含以下信息:

  • 可选的 host。在此示例中,未指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host
  • 路径列表 paths(例如,/testpath),每个路径都有一个由 serviceNameservicePort 定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。
  • backend(后端)是 Kubernetes:服务与负载均衡中所述的服务和端口名称的组合。 与规则的 hostpath 匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的 backend

通常在 Ingress 控制器中会配置 defaultBackend(默认后端),以服务于任何不符合规约中 path 的请求。

💡注意:没有 rules 的 Ingress 将所有流量发送到同一个默认后端。如果 hostspaths 都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端。

路径类型

Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。当前支持的路径类型有三种:

  • ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者与 PrefixExact 类型作相同处理。
  • Exact:精确匹配 URL 路径,且区分大小写。
  • Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。

💡注意:如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。

例如:

类型 路径 请求路径 匹配与否?
Prefix / (所有路径)
Exact /foo /foo
Exact /foo /bar
Exact /foo /foo/
Exact /foo/ /foo
Prefix /foo /foo, /foo/
Prefix /foo/ /foo, /foo/
Prefix /aaa/bb /aaa/bbb
Prefix /aaa/bbb /aaa/bbb
Prefix /aaa/bbb/ /aaa/bbb 是,忽略尾部斜线
Prefix /aaa/bbb /aaa/bbb/ 是,匹配尾部斜线
Prefix /aaa/bbb /aaa/bbb/ccc 是,匹配子路径
Prefix /aaa/bbb /aaa/bbbxyz 否,字符串前缀不匹配
Prefix /, /aaa /aaa/ccc 是,匹配 /aaa 前缀
Prefix /, /aaa, /aaa/bbb /aaa/bbb 是,匹配 /aaa/bbb 前缀
Prefix /, /aaa, /aaa/bbb /ccc 是,匹配 / 前缀
Prefix /aaa /ccc 否,使用默认后端
混合 /foo (Prefix), /foo (Exact) /foo 是,优选 Exact 类型

在某些情况下,Ingress 中的多条路径会匹配同一个请求。 这种情况下最长的匹配路径优先。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。

主机名通配符

主机名可以是精确匹配(例如“foo.bar.com”)或者使用通配符来匹配 (例如“*.foo.com”)。 精确匹配要求 HTTP host 头部字段与 host 字段值完全匹配。 通配符匹配则要求 HTTP host 头部字段与通配符规则中的后缀部分相同。

主机 host 头部 匹配与否?
*.foo.com bar.foo.com 基于相同的后缀匹配
*.foo.com baz.bar.foo.com 不匹配,通配符仅覆盖了一个 DNS 标签
*.foo.com foo.com 不匹配,通配符仅覆盖了一个 DNS 标签

Ingress类型

单个Service实现

现有的 Kubernetes 概念允许你暴露单个 Service,例如:

1
2
3
4
5
6
7
8
9
10
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
defaultBackend:
service:
name: test
port:
number: 80

执行:

1
2
[root@master test]# kubectl apply -f ./test-ingress.yaml 
ingress.networking.k8s.io/test-ingress created

简单扇出(Simple fanout)

一个扇出(fanout)配置根据请求的 HTTP URI 将来自同一 IP 地址的流量路由到多个 Service。 Ingress 允许你将负载均衡器的数量降至最低。例如,这样的设置:

image-20220317143027055

配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /bar
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080

基于名称的虚拟托管(Name based virtual hosting)

基于名称的虚拟主机支持将针对多个主机名的 HTTP 流量路由到同一 IP 地址上。

image-20220317145747418

Contour控制器

Contour是Kubernetes Ingress控制器的另一款开源实现,它以高性能的Envoy代理程序作为数据平面,支持开箱即用的动态配置和多种高级路由机制,支持TCP代理,并且提供了自定义资源类型HTTPProxy资源以扩展IngressAPI,以更丰富的功能集部分解决了Ingress原始设计中的缺点,是Ingress控制器较为出色的实现之一。

Contour Ingress controller 由两个组件组成:

  • Envoy : 提供高性能反向代理。
  • Contour : 充当 Envoy 的控制平面,为 Envoy 的路由配置提供统一的来源。

image-20220321134850499

官方文档提供了部署方法:Getting Started with Contour (projectcontour.io)

Envoy

Envoy是专为大型现代SOA架构设计的应用代理和通信总线,使用**C++**语言编写,是高性能的HTTP/HTTPS协议代理,支持负载均衡、超时、重试、熔断、限流和回退等多种高级路由功能,支持HTTP/2和gRPC,而且能够作为HTTP/1.1和HTTP/2之间的双向透明代理机制。Envoy原生支持动态配置和服务发现机制,能够通过xDS API从控制平面(管理服务器)获取配置信息,同时提供了良好的可观测性,未来甚至会作为UPDA(Universal Data Plane API)的标准实现之一。

作为代理服务,Envoy使用“侦听器”(listener)维护与下游客户端的连接,使用集群管理器管理上游服务器集群,并根据侦听器上配置的代理层级和路由策略“桥接”客户端与后端服务器。它支持配置任意数量的侦听器和任意数量的上游集群,这些配置可使用静态方式指定,也可分别基于不同的服务发现API动态生成。

image-20220321135105225

Envoy通过侦听器监听网络套接字以接收客户端请求,并且支持在一个进程上启用任意数量的侦听器,从而赋予用户按需配置Envoy监听一至多个套接字的能力。每个侦听器都应独立配置一些网络(L3/L4)过滤器,在接收到请求后,侦听器负责实例化指定过滤器链中的各个过滤器,并由这些过滤器处理后续的相关事件。一般说来,用户可根据不同的过滤器链配置侦听器以完成不同代理任务,例如HTTP代理、TCP代理、TLS客户端认证、限速等。

HTTP是现代面向服务的架构中至关重要的组件,因此Envoy通过名为HTTP连接管理器的网络(L3/L4)过滤器实现HTTP协议的应用层代理,内置支持HTTP/1.1、WebSockets和HTTP/2几类协议。另外,Envoy在传输层和应用层分别提供了对gRPC协议的原生支持,gRPC是来自Google的RPC框架,它基于HTTP/2构建和传递请求/响应报文。Envoy内置的HTTP过滤器中的Router负责为HTTP代理实现高级路由功能,包括将DNS域映射到特定的虚拟主机、URL重定向、URL重写、重试、超时、基于权重或百分比进行流量分发、基于优先级的路由和基于哈希策略的路由等。Envoy通过配置的路由信息生成路由表,并由Router过滤器据此做出路由决策。

每个上游服务器可由一个称为端点的逻辑组件进行标识,它代表着此服务监听的套接字,因此也可直接作为上游服务的标识符使用。一个集群可以包含一到多个端点,而一个端点也可隶属于一到多个集群。

Envoy的集群管理器负责管理配置的所有上游集群,包括获知上游主机健康可用状态、负载状态、连接类型以及适用的上游通信协议(HTTP/1.1、HTTP/2)等,并向前端的过滤器堆栈暴露API,允许过滤器根据请求及处理结果按需建立与上游集群的L3/L4或L7的通信连接。一旦在配置中定义了集群,集群管理器需要知道如何解析集群中的成员,即端点配置信息。目前,Envoy支持的端点配置支持包括静态配置、严格DNS、逻辑DNS、原始目标和EDS几种。集群管理器可通过纯静态的配置信息加载并初始化集群,也可通过集群发现服务(Cluster Discovery Service,CDS)API动态获取相关的配置,而后一种方式允许将配置信息存储在集中式的配置服务中,从而减少重启Envoy或重新分发配置的次数。

服务发现是面向服务架构的基石。为了更好地适配云原生环境,Envoy内置了一个层次化的动态配置API用于集中式管理配置信息,该API可借助服务发现服务(Service Discovery Service,即服务发现自身作为一种服务)机制支持多种服务发现方式,从而为Envoy提供以下配置信息的动态发现和更新。

  • 端点发现服务(Endpoint Discovery Service,EDS):用于为Envoy动态添加、更新和删除Service端点,这些端点通常是指上游集群后端提供特定服务的主机的套接字;EDS事实上是v1时代的SDS(Service Discovery Service)的替代品,而且,SDS在v2版本中专用于指代Secret Discovery Service。
  • 集群发现服务(Cluster Discovery Service,CDS):用于动态添加、更新和删除Upstream集群,其中,每个集群本身都有自己的端点发现(v1版本中的服务发现)机制。
  • 路由发现服务(Route Discovery Service,RDS):用于动态添加或更新HTTP路由表。
  • 密钥发现服务(Secret Discovery Service,SDS):传递TLS密钥信息,从而LDS/ CDS能将主要侦听器和集群配置通过密钥管理系统与密钥信息在传送时相分离。
  • 侦听器发现服务(Listener Discovery Service,LDS):用于动态添加、更新和删除整个侦听器,包括其完整的L4或L7过滤器堆栈。
  • 健康发现服务(Health Discovery Service,HDS):配置Envoy成为分布式健康状态检查网络的成员,负责将健康状态检查的相关信息报告给上级或集中式的健康状态检查服务。
  • 聚合发现服务(Aggregated Discovery Service,ADS):构建在单个EnvoyAPI提供的数据平面服务之上的多个管理API(例如Istio、Linkerd等);同时操作Envoy API时,使用单个管理服务器处理单个Envoy的所有更新操作有助于确保Envoy的数据一致性;此API允许所有其他API通过单个管理服务器的单个gRPC双向流进行编组,从而实现确定性排序。
  • 另外还有ALS(gRPC Access Log Service)、RTDS(Runtime DiscoveryService)、RLS(Rate Limit Service)和CSDS(Client Status DiscoveryService)等,这些API统称为xDS,相较于v1版本来说,它是一种新的REST-JSON API,其中JSON/YAML格式是基于proto3规范的JSON映射机制派生而来的。另外,xDS API通过gRPC流式传输进行更新,该方式有着较低资源需求,因而能降低更新延迟。

HTTPProxy

除了能实现类似于Ingress资源的流量分发等基础功能,HTTPProxy还封装了Envoy相当一部分高级路由功能的API,例如基于标头的路由、流量镜像和流量分割等多种高级路由功能,能帮助用户实现诸如金丝雀部署、蓝绿部署和A/B测试等功能。

HTTPProxy支持在单个路由规则中同时指定多个后端服务,默认情况下,所有流量将以等量切分的方式平均分发到多个后端之上,每个后端内部再按照代理服务器配置的调度算法进行二级负载均衡。同时,HTTPProxy也允许用户为每个后端服务使用weight字段指定一个特定流量百分比,从而将流量以指定的比例在不同的后端服务间进行分发。

image-20220321140330336

基于流量分割的流量切分是完成灰度发布的常用手段。上线应用的新版本时,无论是出于产品稳定性还是用户接受程度等因素的考虑,直接以新代旧都充满风险。因此,灰度发布是应用程序在生产环境安全上线的一种重要手段,而对于Envoy来说,灰度发布仅是其流量治理功能的一种典型应用,结合分割策略便能实现常见的金丝雀部署、蓝绿部署和A/B测试等应用场景。

事实上,基于标头的流量分割算是“基于请求内容”灰度部署的一种实现,而流量分割则是“基于流量比例”进行灰度部署的方式。与Kubernetes的Deployment控制器的滚动更新机制相比,HTTPProxy允许用户按需指定要切分的流量比例,而非按照Pod数量来固定分割的方式。

HTTPProxy中的负载均衡策略是路由规则中的定义,每个路由规则都可以为其后端调用的服务按需指定最为合用的负载均衡机制。目前,HTTPProxy暴露了Envoy在集群上支持的部分调度算法,主要有如下几个。

  • RoundRobin:按顺序轮询选择上游端点,此为默认策略。
  • WeightedLeastRequest:加权最少连接,但该算法仅随机选择两个健康的端点,并从中挑选出负载少的端点作为调度目标。
  • Random:从后端健康端点中随机挑选端点。
  • Cookie:粘性会话调度机制,把来自某客户端的所有请求始终调度给同一个后端端点。

工作原理

Contour 同时支持 Ingress 资源对象和 IngressRoute 资源对象(通过 CRD 创建),这些对象都是为进入集群的请求提供路由规则的集合。这两个对象的结构和实现方式有所不同,但它们的核心意图是相同的,都是为进入集群的请求提供路由规则。如不作特殊说明,后面当我们描述 “Ingress” 时,它将同时适用于 IngressIngressRoute 对象。

通常情况下,当 Envoy 配置了 CDS 的 endpoint 时,它会定期轮询端点,然后将返回的 JSON 片段合并到其运行配置中。如果返回到 Envoy 的集群配置代表当前的 Ingress 对象的集合,则可以将 Contour 视为从 Ingress 对象到 Envoy 集群配置的转换器。随着 Ingress 对象的添加和删除,Envoy 会动态添加并删除相关配置,而无需不断重新加载配置。

在实践中,将 Ingress 对象转换为 Envoy 配置更加微妙,需要将 Envoy 中的 xDS 配置(包括 CDSEDSRDS)映射到 Kubernetes 中。Contour 至少需要观察 IngressServiceEndpoint 这几个资源对象以构建这些服务的响应,它通过 client-gocache/informer 机制免费获得这些 watchers。这些 watchers 提供添加,更新和删除对象的边缘触发通知,也可以通过 watch API 在本地缓存缓存对象,以便后续查询。

Contour 将收集到的这些对象处理为虚拟主机及其路由规则的有向非循环图(DAG),这表明 Contour 将有权构建路由规则的顶级视图,并将群集中的相应服务和TLS秘钥连接在一起。一旦构建了这个新的数据结构,我们就可以轻松实现 IngressRoute 对象的验证,授权和分发。改数据结构导出的 png 图片如下图所示:

img

Envoy API 调用和 Kubernetes API 资源之间的映射关系如下:

  • CDS : 集群发现服务。映射为 Kubernetes 中的 Service 以及一部分 Ingress 对象的 TLS 配置。
  • EDS : 服务发现服务。映射为 Kubernetes 中的 Endpoint。Envoy 使用 EDS 自动获取 Cluster 成员,这与 Endpoint 对象中包含的信息非常匹配。Envoy 使用 Contour 在 EDS 响应中返回的名称查询 EDS
  • RDS : 路由发现服务。映射为 Kubernetes 中的 Ingress。提供了虚拟主机名和前缀路由信息的 RDS 与 Ingress 匹配得更好。

总结

Kubernetes的Ingress简单理解就是个规则定义,例如某个域名对应某个Service,即当某个域名的请求进来时,转发给某个Service。Ingress Controller负责实现这个规则,即Ingress Controller将其动态写入负载均衡器的配置中,从而实现服务的负载均衡。

基于Kubernetes内置的Ingress发布服务时,底层使用的是IngressNginx还是Contour控制器并没有本质上的区别。但Contour提供的CRD资源类型HTTPProxy能够提供更为完整的路由功能集,大大丰富了Ingress的特性表现。HTTPProxy封装了Envoy的部分API,从而支持流量切分、流量镜像、基于标头的路由、自定义使用的负载均衡策略、超时和重试、后端端点的健康状态检测等高级功能。