L7是指网络七层模型中的应用层,L7规则是基于HTTP协议配置的一些转发规则,可以让我们以多种方式处理HTTP报文,并允许在应用程序层面对流量进行处理。例如,可以基于URL路径、HTTP头、HTTP/2帧类型等属性对流量进行分类,还可以通过不同方式处理来自多个客户端的请求,例如创建特定的服务版本,将请求路由到特定的服务实例,以及实现A/B测试和金丝雀部署等策略。通过使用L7规则,我们可以更好地控制运行中的应用程序的流量,从而实现性能、可用性和安全性等方面的目标。
以下列举了一些常见的L7规则应用案例:
基于URL路径的流量分类:使用L7规则,我们可以根据请求的URL路径路由流量。例如,我们可以将所有以'/api/v1'开头的URL路径路由到特定的服务版本,以确保基于相同API规范的所有服务版本都处理相关的请求。同时,我们可以将其他URL路径分类到不同的服务版本或实例中,以便更好地分配流量和资源。
基于HTTP头的流量分类:除了URL路径外,L7规则还支持根据HTTP头对流量进行分类。例如,我们可以检查请求中的Authorization头,并使用它来确定访问权限。然后,我们可以将响应路由到适当的服务实例,以确保已经获得适当访问权限的用户才能访问我们的服务。
基于请求方法的流量分类:使用L7规则,我们可以根据请求方法对流量进行处理。例如,如果客户端发起的请求是HTTP POST请求,则我们可以使用L7规则,将请求路由到处理此类请求的专门服务或实例中,以确保这些请求得到更好的处理。
eBay数据中心在公网和内网都配置了了大量的L7规则,公网L7规则在UFES项目中已经迁移到了基于Contour的软件负载均衡,而内网的L7规则还主要在硬件负载均衡设备。由于数据中心采用的是基于Istio的应用网关,相应的内网L7规则也需要迁移到Istio。虽然Istio原生支持L7转发规则,但是对于多集群,跨集群以及高可用性方面还是有所不足。本文将介绍数据中心内网基于Istio的L7规则的多种技术方案和选型时的考虑因素,以及最终技术方案的细节和遇到的挑战。
02 数据中心
数据边缘(POP)的L7规则已经通过UFES项目基本将所有公网流量接入到软件负载均衡,具体可以参考:eBay边缘节点的云原生网络实践
数据中心的L7主要是处理内网的流量转发,如应用A需要将一部分请求转发到应用B,或者应用A对内网的请求需要做一些特殊的处理,比如加入某些header。相对于公网L7规则,数据中心内网的L7规则数量比面向公网流量比如主站www.ebay.com上的规则数量少很多,但是也有着广泛的应用。并且由于业务的需求以及长时间的积累,内网L7规则采用了不同的网络拓扑架构,同时也具有多种规则类型,这些也增加了将内网L7规则迁移到Istio应用网关的复杂性。
2.1 L7规则部署架构
2.1.1 内网2-Tier L7规则
这种部署架构是生产环境广泛采用的,数量比较多。由于生产环境采用了2层HLB进行流量治理,L7规则的流量也采用了2层部署架构,体现在L7的目的地址为目标应用的App Tier VIP,其架构图如下:
图2.1.1内网2-Tier L7规则
2.1.2 内网1-Tier L7规则
这种部署架构是PCI/RNPCI环境所采用的,数量相对来说比较少。PCI/RNPCI环境采用的是1层HLB进行流量治理,1层LB的架构L7规则与2层部署架构有所不同,具体体现在L7的目的地址为目标应用的机器地址,其架构图如下:
图2.1.2内网1-Tier L7规则
2.2 L7规则类别
数据中心内网常见的L7规则有以下几种:
Responder/Rewrite:
该规则用于在处理HTTP请求时可以替换或修改请求中的部分信息,例如修改URL或HTTP头,或者返回一个特定的响应。Responder/Rewrite规则的作用可以是为了增强安全性,为特定的域名或URL提供重定向或错误页面等。
Drop/Reset:
该规则用于在传输层或网络层中阻止或重置连接。Drop规则会将客户端的数据包直接丢弃,不会有任何响应发送给客户端;Reset规则将发送一个TCP Reset包告诉客户端连接已被重置,这种规则可以用于防止DDoS攻击或非法连接。
Forward Policy:
Forward Policy又叫Context Switch Policy,该规则的作用是根据匹配条件(如URI/Host/Header)将请求转发到另一个上游服务进行处理,这是内网常见的一种L7规则。
Responder/Rewrite以及Drop/Reset这些规则不涉及到上游服务,转发规则在当前VIP(source)就能处理掉,比如插入header,drop或者reset。但是Forward Policy需要将请求转发到上游服务,而上游服务又可能会有不同的部署架构,所以主要的挑战是如何用合适的模型描述上游服务,这也是本文重点介绍的地方。
03 数据中心内网
2-Tier SLB L7方案
这种方案的提出基于生产环境采用的是2-tier SLB。在此背景下,为了保持功能的一致性,我们设计了一种2层SLB的网络架构,具体的实现细节可以参考:eBay基于Istio的高可用的两层应用网关架构。2-tier SLB机构本质上是给一个VIP配置了两个端口来模拟App Tier以及Web Tier,所以可以保留完整的2层HLB的流量管理能力。
在2- Tier SLB的L7的方案中,上游应用有两层VIP,443以及7443,L7的源443 VIP可以将L7流量转发到上游应用的7443 VIP,这里我们可以通过Istio原生的WorkloadEntry来描述VIP(虽然社区原生语义是用来描述非mesh的VM,但如果把它抽象成一个IP地址的话,VIP和VM其实是一样的),并且用ServiceEntry选择这些VIP。其架构图如下:
图3.1 2-Tier SLB L7规则
2-Tier SLB方案中,目的应用的App Tier VIP比如bar的7443 VIP,能够接受从bar-443转发过来的默认流量的请求,同时也能接受从foo-443 VIP转发过来的 URI prefix匹配“/bar”的L7请求。这种架构能够提供和目前HLB 2-tier架构一致的流量管理能力。
优点
与HLB 2-Tier L7具有相同的流量管理能力,能够单独控制L7流量的比例,停用或启用某些VIP的L7 流量
统一的L7流量管理模型管理公网3-Tier以及内网2-Tier L7流量
L7 流量支持跨数据中心
缺点
依赖AppGateway 2-Tier的生产可用状态
缺少L7流量主动健康检查
2-Tier架构上的复杂度影响控制面性能
后来因为生产环境没有采用2-Tier,2-Tier的L7方案也没有被采用,但是该L7流量模型仍然在公网的3-Tier L7中使用。因为公网L7不依赖2-Tier并且由于数量相对来说比较少,以及健康检查的功能在UFES中已经实现,所以该模型能够无缝的适配。
DNS L7方案
这种方案的核心是将上游服务的DNS作为L7的转发目的地址。Istio社区提供了DNS解析类型的ServiceEntry资源,能够原生支持利用DNS解析来管理服务发现。
采用DNS类型的ServiceEntry资源最终会生成一个strict_dns类型的upstream cluster。在使用严格的DNS服务发现时,Envoy将持续和异步地解析指定的DNS目标。DNS结果中返回的每个IP地址都将被视为上游群集中的显式主机。这意味着,如果查询返回3个IP地址,Envoy将假定群集有3个主机,并且所有3个主机都应进行负载平衡。如果从结果中移除了主机,则Envoy将假定它不存在,并从任何现有的连接池中删除流量。另外由于Envoy不会在转发路径上同步解析DNS,虽然最终一致性会受到影响,但不会出现因长时间运行的DNS查询而发生阻塞影响数据面的情况。
图3.2 DNS L7方案
优点
GTM作为唯一的流量入口统一管理L7流量以及内网流量
VIP的健康检查委派给GTM,GTM会进行TCP检查并且决定停用或启用VIP
简化了L7的配置,只需要用社区原生支持的ServiceEntry,不需要WorkloadEntry
缺点
DNS TTL会影响L7流量
由于VIP 443到8443的映射,远端VIP需要用443端口而同网关VIP需要用8443端口,L7数据平面不能正常工作
如果L7目标应用是源应用,使用DNS会发生流量循环
不支持L7单独的流量控制
L7故障转移功能不起作用,即L7流量不工作时无法将请求转移到首页
这种方案在2-Tier SLB L7方案的时候提出来的时候讨论过,但是由于当时生产环境采用的是2层SLB,DNS方案缺少相应的L7流量管理能力,因而当时没采用这种方案。不过由于后来生产环境也是采用的1层SLB,同时也不需要2层的流量管理能力,在没有了历史包袱的情况下,选择该方案的逻辑变成了:如果默认非L7的流量入口是DNS(GTM),并且客户端能够接受DNS TTL带来的副作用以及不需要精确的流量控制,那么L7流量也可以采用同样的入口以及管理方式。
在DNS方案中,Envoy会周期性地进行DNS解析,Envoy默认的DnsRefreshRate是5秒,但为了避免对DNS服务器造成过大负担,Istio将其增加到60秒。DnsRefreshRate配置只有当Envoy发起的DNS请求失败(NXDOMAIN或SERVFAIL)时才会使用。因此最坏的情况即当DNS解析不能正常返回时,L7最长可能会受到60秒钟的TTL或者negative cache的影响。
另外一个可能存在的问题是,对于strict_dns,ServiceEntry的DNS由Envoy解析,而端点数量由DNS查询返回的IP地址确定。对于我们的情况,GTM的最大返回地址通常是1,因此只会有一个端点,这也就决定了它不能支持跨数据中心的L7流量,同时因为只有一个Endpoint也不会有流量转移到其它Endpoint或者其它上游集群的能力。
1-Tier 443转443方案
这种方案是在决定将生产环境从2层转成1层以后的一种折中方案,它能够在上游应用网络拓扑转成1层SLB架构之后,同时提供类似原来2层VIP的L7流量管理能力。本质上是,即使上游应用只有一层VIP,仍然可以将L7流量转发到上游应用的443 VIP,同时通过Istio原生的WorkloadEntry来描述VIP,并且用ServiceEntry选择这些VIP。其架构图如下:
图3.3 1-Tier 443转443方案
优点
统一的流量管理模型同时支持内网1-tier/2-tier L7
支持L7流量跨数据中心
与HLB 2-Tier L7具有相同的流量管理能力,但不依赖于2-Tier SLB架构
与3层公共L7相同的流量模型
缺点
由于443/8443映射,同网关数据平面无法工作
对于2-tier应用,如果将443作为L7目标,数据面将成为3-Tier
如果L7目标应用是源应用,则会发生流量循环
2-Tier/3-Tier架构上的复杂度影响控制面性能
缺少443 VIP的健康检查
这种方案本质上和2-Tier SLB L7方案是同一个方案,只是将L7的目的VIP变成443,所以能够同时支持1-Tier或者2-Tier SLB,能够与2-Tier SLB的生产可用解耦。但是2-Tier L7因为复杂度带来的控制面的性能问题,会影响数据中心流量退出的SLO,同时像L7健康检查的问题也仍然存在。
1-Tier 443转7443方案
这种方案是3.3方案的一种扩展,意在解决443的端口转发的问题,同时保留2-Tier SLB L7流量控制的能力。这种方案的核心是给上游应用增加一个7443的VIP用来作为L7的转发流量目的地址,其架构图如下:
图3.4 1-Tier 443转7443方案
优点
支持L7流量跨数据中心
与HLB 2-Tier L7具有相同的流量管理能力
与3-Tier公网L7相同的流量模型
即使L7目标应用是源应用也不会发生流量循环
没有443到8443端口映射的问题
缺点
需要为7443实现健康检查
默认流量1-Tier,L7流量2-Tier
2-Tier/3-Tier架构上的复杂度影响控制面性能
7443不在默认流量路径,可能会变成故障旁路
这种方案是1-Tier 443转443方案的一个变种,重点是解决了端口映射的问题同时保留了2-Tier HLB的L7流量管理能力,但是控制面复杂度的性能问题以及健康检查问题还没有解决。
4.1 L7解决方案的考虑以及限制因素
当我们选择L7技术方案的时候,从技术上到业务上都会有一些限制因素,一方面我们需要一个高可用的L7技术方案,同时又需要尽快交付功能,这样才能够支持业务顺利迁移到应用网关。
另外还有一点需要注意的是:原有的2-Tier HLB的架构提供了很完善的L7管理能力,在新的应用网关上我们是否仍然需要保持功能对等。总结起来主要有以下几个方面的考虑:
1.支持跨数据中心L7流量
a.生产环境2-Tier HLB默认都有跨数据中心L7流量,但同时也存在部分1-Tier L7不具备跨数据中心L7流量
b.L7流量不受DNS缓存影响,在某些VIP出现故障时自动转移到健康的VIP
2.L7流量管理能力
a.L7流量比例控制分配到不同的数据中心上游VIP
b.能够单独控制L7流量退出,单独启用或者停用L7的VIP
3.实现统一的L7解决方案
a.满足生态系统的要求
b.适用于PCI/RNPCI/MPT-Prod环境
c.以最小的努力获得最大的业务价值
4.2 L7方案比较
我们从功能的角度对比了不同的L7技术方案,整理出如下列表:
05 L7 DNS
5.1 443到8443端口映射挑战
从Istio 1.7版本开始,Istio IngressGateway以非root身份运行,无法再绑定到低于1024的端口,所以对443 VIP,实际上Envoy监听在8443,而443只是4层IPVS所暴露的VIP端口,该端口在进入到IngressGateway Pod的时候会被PREROUTING链上的DNAT规则转为8443。因此,对于远端数据中心的L7 VIP,仍然可以连接到远端443 VIP,流量将经过远端TLB,然后TLB转发到远端的IngressGateway。IngressGateway的pod将入站的443端口请求转通过DNAT规则转成8443。
然而对于源VIP以及L7目的VIP在同一IngressGateway Pod中的L7而言,由于这时候只是一个本地转发,无法经过PREROUTING链做DNAT,因此443无法工作(缺少到8443的DNAT)。具体细节如下图所示:
图5.1 L7 DNS方案端口转发问题
这里也有不同的解决方案,比如我们可以改Istio,让它支持本地和远端用不同的端口,但是这种改动太侵入了。
最终我们采用的解决方案是:Netd为每个VIP在OUTPUT链中创建本地生成的数据包的DNAT策略。这条规则与在PREROUTING链中的规则很类似,只是一个在进入网络协议栈前将443转成8443,另一个是在应用层将数据发送到网络协议栈之前做的转换。最终我们在IngressGateway上给所有VIP配置了如下两条规则:
5.2 MPT-Prod L7流量迁移挑战
DNS L7的方案只能解决内网L7流量迁移以及管理的问题,但是生成环境存在大量的应用既配置了内网的L7,又有从公网接入的L7流量。由于公网我们仍然采用的是3-Tier架构,L7的目标地址是VIP而不是DNS,公网L7的目的地址实质上是一个由VIP组成的上游集群。
然而在公网对接HLB 2-Tier的L7中,上游集群(L7 LBPool)通常是有两个,一个是端口为8080的LBPool,协议为HTTP,接受从HTTP VIP过来的流量,另一个是端口为8082的LBPool,协议也是HTTP,接收从HTTPS VIP过来的流量。但是当转到1-Tier SLB之后,目标应用只有80 VIP以及443 VIP,所以这里会存在端口协议的问题,即如何将流量从8082 HTTP的LBPool转移到443 HTTPS的LBPool。具体细节如下图所示:
图5.1 L7 DNS方案公网流量转发挑战
即使在HLB中,LBPool中的成员的协议和端口应该相同,同样的限制也存在于Envoy集群管理中,因为Envoy 上游集群(Upstream Cluster)的TLS context也是配置在Cluster级别而不是Endpoint级别,因此无法在一个集群中既有HTTP的Endpoint也有HTTPS的Endpoint。
但是Istio社区也提供了解决方案,也就是基于权重的流量导向(weight based traffic steering),该方案支持在VirtualSerice上配置多个Destination,将流量从8082-HTTP的目的地址转移到443-HTTPS的目的地址。目前该方案已经实现并且迁移了部分应用。
5.3 软件防火墙集成
软件防火墙集成的问题和端口映射的问题类似,如果源VIP和目的VIP在同一个IngressGateway,这种情况下因为是一个本地转发,实际上不会经过软件防火墙iptables的过滤,所以默认本地转发是工作的。对于远端数据中心的VIP,则需要额外创建一些规则。但是通常由于DNS解析会优先返回同一个数据中心的目标VIP,所以即使在没有防火墙规则的情况下默认也是工作,远端的VIP就变成一个旁路,只有当同一个数据中心的目标VIP被停用了才会接受流量,或者也只有这时候才意识到防火墙没有工作。
另外还存在一种情况就是,即使在同一个数据中心,源VIP和目标VIP也可能在不同的IngressGateway Pod,因为我们在一个mesh会部署多个IngressGateway Deploy。这种情况下对于同数据中心的目标与远端数据中心的VIP具有相同的问题,默认都是不工作的,需要人工介入开防火墙。
5.4 L7转发规则Authority
重写以及流量循环
在DNS L7的方案中,由于L7的目的地址是一个DNS,这里一个潜在的问题是,当L7到源VIP时,HTTP请求中的host是源DNS,但是当请求转发到目的VIP时,目的VIP可能并不接受HTTP请求中的host位其它应用的host。
下面通过一个例子说明:
- match:
- uri:
prefix: /viewitem
authority:
exact: searchsvc.vip.ebay.com
name: searchsvc-to-viewitem
rewrite:
authority: searchsvc.vip.ebay.com
uri: /
route:
- destination:
host: viewitem.vip.ebay.com
用户发起的请求是searchsvc.vip.ebay.com/viewitem,其中的host是searchsvc.vip.ebay.com。当请求到达searchsvc.vip.ebay.com应用时,根据转发规则需要将请求转发到viewitem.vip.ebay.com应用,然而viewitem应用并不接受host为searchsvc.vip.ebay.com的请求。这里的一种处理方式是在转发之前加一条authority rewrite的规则,但也会带来如果该应用有多个域名会无法逐一匹配的问题,并且语义上相对于HLB来说可能发生了变化,因为在HLB目标应用那一端有可能不会校验host。
我们建议在此采用的做法是将目的应用的Gateway以及VirtualService中的host改成 “*”,这样也实现了HLB一致的语义并且不需要配置authority rewrite的规则。
另外一个挑战就是如果L7的目的地是应用本身,DNS方案也不适用,因为会出现请求死循环。在生产环境中也存在一些这样的规则,一部分的原因是应用开发者需要对某些api做一些特殊的处理,比如添加header,会导致一部分规则配置在LB上。
- match:
- uri:
prefix: /viewitem/details
authority:
exact: viewitem.vip.ebay.com
name: viewitem-details-to-details
rewrite:
authority: viewitem.vip.ebay.com
uri: /details
route:
- destination:
host: viewitem.vip.ebay.com → traffic loop
比如这里需要将viewitem.vip.ebay.com/viewitem/details转到viewitem.vip.ebay.com/details,这里的目的host就不能继续使用viewitem.vip.ebay.com,必须要使用service name,也就是当成默认流量来处理。
尽管L7是Istio社区原生支持的功能,但是在将这个功能应用到实际的生成环境中时又会遇到各种各样的挑战,有技术层面的,也有业务层面的,我们花了将近半年的时间才最终决定采用DNS的方案。
这里面最重要的决策其实是生产环境从2-Tier SLB转成了1-Tier SLB,有了这个决策之后我们卸下了很多历史包袱。尤其是和HLB 2-Tier的功能对等(feature parity)不再是一个硬性的要求,这并不是说原本2-Tier HLB的架构不够好,而是在这种大规模的基础架构转型的项目中,我们必须要有所取舍。没有完美的架构和技术方案,也不存在一个银弹能够解决我们遇到的所有问题。但很重要的一点是,在不牺牲可用性,安全性以及稳定性的条件下,控制面应该朝着简单,轻量的方向上迭代,也就是“高内聚,低耦合”,太大而全,太复杂的控制面不仅仅会带来性能方面的问题,同时也会大大增加后期维护成本甚至走向不维护。从API定义的角度来看,类似的例子比如Contour最开始采用单个的CRD IngressRoute,或者k8s的Ingress API,最终被k8s的Gateway API取代。从控制面的角度来看,早期的Istio也是采用的多个控制面功能组件单独部署的架构,后期也合并成单体的控制面部署。
最后一点是,在我们实现一个新的技术方案的时候,有必要从原来的架构实现中跳出来。并不是因为原来是怎么样,将来也应该是怎么样,而是要想想它为什么原来是那个样子,怎样才能让它变得更好?
END