阅读前置要求:
需要有CCNA或者等价的基础网络知识
负载均衡
我印象中负载均衡其实是个硬件设备。其实一开始确实是的,然而现在已经不同了。尤其是云厂商提供的负载均衡方案几乎全部是靠软件。现在的负载均衡不仅是网络流量复杂均衡,几乎所有的平衡多个计算资源负载的方案都可以叫做负载均衡。在云计算背景下,负载均衡其实有一个软件实体--proxy。准确说,他们其实是不一样的,但是不能否认他们的功能其实重叠,proxy将网络转发作为主要功能。并且在所有的service mesh、cloud的资料中,这两个词指的就是同一个东西。
现代云负载均衡
现代云负载均衡一般会做些什么?基本上就是下面这三个
service discover -- target pool -- liveness
health check -- readiness
load balancing
L4/L7
其实业界的负载均衡方案,就是这两类:四层负载均衡和七层负载均衡(L4 & L7)。四层和七层指的是OSI 7层模型下的四层、七层。
第4层是传输层,第7层是应用层。要是之前不知道OSI模型那还好,要是以前仔细学过网络这个时候已经蒙了。四层还能负载均衡?那不是二层三层做的么?!
L4 load balancing
实际上四层负载均衡的意思是这种模式的负载均衡会维持同一个TCP连接,而不是说它工作在四层(大部分L4 LB还是工作在二层和三层的)。所以L4 load balancing有一个特点,同一个连接的流量会流向同一个后端服务器。技术上还是靠传统的二层和三层设备转发,只不过现在统一叫四层负载均衡了。
重新回顾OSI七层,
第一次是服务层,传输的是bit比特流,这一层的设备包括网卡、各种电缆光纤,设备之间需要靠交叉线进行直连通信。
第二层是数据链路层,传输的是Frame数据帧,WIFI(802.11),Ethernet (802.3), PPP 工作在这一层。这一层的设备一般是交换机,设备之间依靠MAC地址寻址。同一个LAN里面的设备可以相互通信。
第三层是网络层,传输的是Packets 网络包,各种路由协议工作在这一层,比如RIP,OSPF,IS-IS,BGP。这一层的设备就是我们看到的路由器了,设备之间依靠 IPv4/IPv6地址寻址。网络包可以跨越LAN子网,在整个WAN广域网上通信了(比如 internet)。
第四层叫做传输层,传输的是segments 数据段,这里主要是 TCP,UDP协议。协议里包含了主机的port端口号,如果说IP确定了互联网上主机的位置,那么port指向该主机上监听该端口的程序。
前三层叫依靠设备进行转发,从第四层开始流量就已经运行在主机上了。你可以认为前三层的流量转发不需要经过OS Kernel基本上在网卡上就转发走了;但是第四层以上是程序对程序的通信,所以会经过os kernel处理,所以这种只能叫做代理。
二层负载均衡/转发
所谓的转发,其实大致上有两类:封装 & 改写。
在二层主要是改写。你要是找一个802.3的帧结构来看一下的话,大概就是这个样子
别的字段暂时不用看,需要注意的是MAC源地址和MAC目标地址。MAC地址其实是写在网卡上的,交换机会按照目标地址判断目标设备位于哪个端口,然后进行转发。那么问题来了,如果只靠交换机转发,这个网络包是不能跨越子网的。所以路由器其实会进行拆包和封包。
路由器收到网络包之后,会先拆包,移除原来的二层header(主要就是mac源地址和目标地址),然后根据路由判断next hop(下一跳)的地址,将自己的MAC地址作为新的源地址,下一跳的地址作为新的目标地址,重新封包一个新的二层header。二层负载均衡基本上也是这么做的,通过改写二层header进行转发。
三层负载均衡/转发
三层的功能就比较齐全了,除了改写header之外,还可以套娃header。
这种叫做 IP Tunnel 隧道模式。简单说就是将已有ip header payload 看作一个整体,当成一个完整的payload,进入隧道之前加上一层新的header,在新的header中写入隧道另一端的ip地址,然后发送出去。隧道另一端的路由器会根据配置解除外层的header,还原网络包。V**里面的tunnel模式用的就是这种方法。
另一种改写目标地址的方法和二层一样,只不过这次是改写IP header。这就是传说中的NAT转换,一般是用来将私网地址转换成公网地址。在负载均衡里面则是将真实服务器转换成网关地址。
四层负载均衡/转发
说到四层负载均衡就比较复杂了。理论上四层应该是经过OS Kernel来完成的,因为四层已经是程序和程序之间通信,只能做代理。但是实际上四层的负载均衡是做在三层的路由器上的。这就是理论和实际的差异了…
事实上,这个场景还挺常见,家用路由器上就有。三层上我们说了NAT转换会将私网地址转换成公网地址。比如你在家里有多台设备,但是只有一个对外的公网地址,那这么多设备怎么同时上网呢?或者说多个私网地址怎么映射到一个公网地址?
其实路由器上的NAT模式真正起的是NAPT,它会将来自不同设备的请求映射到同一个公网IP的不同端口。可用的端口号是从0-65535,大部分家用场景完全够用了。
但是改写端口这个事情,这可不归网络层管了。如果你还记得本文最开头关于OSI 7层模型那一段,你会发现端口号并不包含在IP协议里,而是在传输层TCP/UDP协议里。虽然设备还是三层设备,但是做的是四层端口的改写。
四层负载均衡可以通过硬件完成,也可以选择软件的解决方案,比如Ngnix 或者 envoy。Google Cloud的NetLB就是通过Maglev和Andromeda实现的,二者各有一篇论文。
这些软件一般可以提供L3 L4的四层负载均衡,和针对某一类流量比如HTTP/HTTPS的7层负载均衡。如果你熟悉Istio 或者 Envoy,或者观看过Cloud Next2019的相关演讲,你会发现Google Cloud内部使用了Envoy实现7层负载均衡。
L7 load balancing
四层负载均衡的主要工作原理是转发TCP流量,客户端和服务器之间维持着同一条TCP通道。7层负载均衡的工作方式是代理,客户端会和负载均衡之间建立一条链接,负载均衡器和服务器之间会保持另一条链接。你可以认为7层负载均衡是客户端和真实服务器之间的一个额外的服务器,它会接受来自客户端的请求根据请求的内容进行初步判断,然后再将请求转发给真实服务器。所以一般l7 负载均衡也有反向代理的功能。
举个例子,有些场景是四层负载均衡无法做到,需要依靠7层负载均衡。四层负载均衡只能将流量转发到某一台主机,7层负载均衡可以将流量转达某一类服务。
比如云端用户可以利用负载均衡对已有的服务进行微服务拆分,这时候就可以通过7层负载均衡按照访问路径对流量重新定向到新的微服务,来取代旧的服务。
总的来说,七层(应用层)负载均衡可以感知应用层的内容,提供更复杂的功能,代价是效率会比较低(至少要多进行一轮三次握手)。在微服务架构下,大多数链路治理的工作都是通过7层负载均衡器完成的。
sidecar proxy
sidecar proxy是service mesh中一种常见的负载均衡模式。
Google Cloud 在 Cloud Next '19中展示了如何在prod 环境使用Istio进行traffic routing。
在微服务的场景下,拆分后的服务是一个个独立的单元。每个服务实例都与一个sidecar proxy(envoy) 处于一个单元。来自单个服务实例的所有网络流量(HTTP、REST、gRPC、Redis等)都通过其本地sidecar proxy (envoy)流向适当的目的地。因此,服务实例不知道整个网络,只知道其本地proxy。
Google认为应该将service mesh理解成管理服务的SDN(Software-defined networking for services)。在数据平面层,envoy 负责转换、重定向和审查来往服务实例的每个网络数据包。
那么sidecar 和l7ilb有什么关系?
当客户启用L7ilb时,Google Cloud 会通过injection的方式注入envoy作为sidecar proxy,在托管模式下envoy是traffic director自动管理的,使用者可以毫无感觉。
References:
Introduction to modern network load balancing and proxying by Matt Klein
Cloud Load Balancing Deep Dive and Best Practices (Cloud Next '19)
Envoy Internals Deep Dive - Matt Klein, Lyft (Advanced Skill Level)