GLB - 看看 GibHub 如何做负载均衡
本文是 GitHub 工程团队博客对他们的负载均衡器 GLB (GitHub Load Balancer) 做的简介。
- 挑战
- 要同时支持 HTTP, SSH, Git 三种协议的流量。
- 每天流量数十亿。
- 历史架构
- 跑在专用的,调优过,巨大的 Bare Metal (裸机)。
- Haproxy 做故障转移。
- 硬件上支持 10G link 故障转移。
- ^ 遇到的问题
- 网络硬件,负载均衡的主机,硬件配置,三者都混写在一起,太痛苦。
- 没法水平扩展。
- 描述期望
- 能跑在普通的商用硬件上。
- 能水平扩展。
- 支持高可用,故障转移时 abort TCP 连接。
- 支持 connection draining (感觉是 graceful shutdown 的另一种说法)。
- 支持服务级别的负载均衡(因为一台机器可能跑多个服务)。
- 能像普通软件一样更新。
- 能基于每一层做测试,而不仅仅是集成测试。
- 能支持多个 POPs 和数据中心。
- 抵御和减轻 DDos。
- 思考
- 如果只是用 DNS 负载均衡分流,会遇到的问题是:效果并不好。客户端 DNS 可能不遵守 TTL,用户也有可能硬编码 IP 地址。
- ^ 对此引入的做法是:用 VIP。一个 IP 可以对应多个物理机。
- ^ 再引入 Equal-Cost Multi-Path (ECMP):对 packet 里的 source-ip-addr-port, destination-ip-addr-port 做哈希运算,将相同哈希的流量路由到同样的路径上。挂了一台物理机,哈希池子重新计算,有 1/N 的流量被重新分配到剩下的机器。小缺点是这 1/N 的流量连接可能不完全是原来那些 1/N 的连接;做维护的时候,这些 1/N 的流量也没法做到 graceful remove servers。
- L4/L7 两种路由器分别对应4层和7层的流量。L4 用 ipvs/LVS, 直接转发流量;L7 用 haproxy,给后端服务代理连接。我们可以用 LVS 同步连接状态给其它 L4 层的路由器,也可以转发流量给 L7 层的 Haproxy。
- ^ 这样的设计好处是:L7 层这一层的配置更新更频繁,同时它可以维护连接,我们就能做到 graceful 移除节点。
- ^ 问题在于 连接断开 在 git 的使用场景是个很费事的事情,因为 git 不会去重试。想想下了一大半的代码了,连接被重置那多不爽。
- 于是 GitHub 主要在 L4 这一层下工夫。他们引入的做法是使用 Rendezvous hashing 的变种。具体来说,packet 被封装进入一个 Foo-over-UDP IP packet,内部 payload 除了原有的数据,还塞入了一组有序的 proxy servers 和 states。这样就算一台 proxy server 挂了,其它 server 还能处理这个请求。Proxy Servers 应用 DSR(Direct Server Return), 回响应的时候就直接丢给客户端,不经由 L4 的节点了。
衍生思考:本文并没有很深入地讲实现,而是从需求,和现有方案出发,层层分析,引出了自家的方案,非常有 GitHub 家做工程的风格。