本文档不提供任何配置方面的帮助或提示,但它解释了在哪里可以找到相关文档。下面的概述旨在帮助您按名称搜索章节并在文档中导航。致文档贡献者:本文档的格式为每行 80 列,缩进使用偶数个空格,不使用制表符。请严格遵守这些规则,以便在任何地方都能轻松打印。如果您添加章节,请更新下面的概述以便于搜索。
| 1. | 可用文档 | |
2. |
负载均衡和负载均衡器的快速入门 | |
3. |
HAProxy 简介 | |
| 3.1. | ||
| 3.2. | ||
| 3.3. | ||
| 3.3.1. | ||
| 3.3.2. | ||
| 3.3.3. | ||
| 3.3.4. | ||
| 3.3.5. | ||
| 3.3.6. | ||
| 3.3.7. | ||
| 3.3.8. | ||
| 3.4. | ||
| 3.4.1. | ||
| 3.4.2. | ||
| 3.4.3. | ||
| 3.4.4. | ||
| 3.4.5. | ||
| 3.4.6. | ||
| 3.4.7. | ||
| 3.4.8. | ||
| 3.5. | ||
| 3.5.1. | ||
| 3.5.2. | ||
| 3.5.3. | ||
| 3.5.4. | ||
| 3.6. | ||
| 3.7. | ||
4. |
配套产品和替代方案 | |
| 4.1. | ||
| 4.2. | ||
| 4.3. | ||
| 4.4. | ||
5. |
联系方式 |
完整的 HAProxy 文档包含在以下文档中。请务必查阅相关文档,以节省时间并获得最准确的响应。另外,请避免向邮件列表中提问,因为这些问题的答案已包含在这些文档中。 - intro.txt(本文档):介绍负载均衡的基础知识、HAProxy 作为一种产品、它的功能、不具备的功能、一些要避免的已知陷阱、一些特定于操作系统的限制、如何获取它、它的发展历程、如何确保您运行的是所有已知已修复的版本、如何更新它、补充和替代方案。 - management.txt:解释如何启动 haproxy、如何在运行时管理它、如何在多个节点上管理它以及如何进行无缝升级。 - configuration.txt:参考手册详细介绍了所有配置关键字及其选项。在需要更改配置时使用。 - coding-style.txt:面向希望向项目提交代码的开发人员。它解释了代码应遵循的风格。它并不十分严格,而且并非所有代码库都完全遵守它,但过于偏离它的贡献将被拒绝。 - proxy-protocol.txt:这是 PROXY 协议的事实规范,HAProxy 和许多第三方产品都实现了该协议。 - README:如何从源代码构建 HAProxy
负载均衡是指聚合多个组件,以便实现高于每个组件单独容量的总处理能力,而无需最终用户进行任何干预,并且可以进行扩展。这使得在组件执行一个操作所需的时间内,可以同时执行更多的操作。然而,一个单独的操作仍然一次只在一个组件上执行,并且不会比没有负载均衡时更快。它总是需要至少与可用组件一样多的操作,以及一个高效的负载均衡机制来利用所有组件并充分发挥负载均衡的优势。高速公路的车道数量就是一个很好的例子,它允许在同一时间框架内通过更多的汽车,而无需提高它们的 Individuel 速度。负载均衡的例子: - 多处理器系统中的进程调度 - 链路负载均衡(例如,EtherChannel、Bonding) - IP 地址负载均衡(例如,ECMP、DNS 轮询) - 服务器负载均衡(通过负载均衡器) 执行负载均衡操作的机制或组件称为负载均衡器。在 Web 环境中,这些组件称为“网络负载均衡器”,更常见的是称为“负载均衡器”,因为这项活动是迄今为止最广为人知的负载均衡案例。负载均衡器可以发挥作用: - 在链路级别:这称为链路负载均衡,它包括选择将数据包发送到哪个网络链路; - 在网络级别:这称为网络负载均衡,它包括选择一系列数据包将遵循的路由; - 在服务器级别:这称为服务器负载均衡,它包括决定哪个服务器将处理连接或请求。存在两种不同的技术,它们满足不同的需求,尽管有一些重叠。在任何一种情况下,重要的是要记住,负载均衡包括将流量从其自然流中转移出来,这样做总是需要最小程度的注意,以保持所有路由决策之间所需的连贯性。第一种技术作用于数据包级别,或多或少地单独处理数据包。输入和输出数据包之间存在 1:1 的关系,因此可以使用常规网络嗅探器在负载均衡器的两侧跟踪流量。这种技术可能非常便宜且速度极快。它通常以硬件(ASIC)实现,能够达到线路速率,例如执行 ECMP 的交换机。通常是无状态的,它也可以是有状态的(考虑数据包所属的会话并称为层 4 负载均衡或 L4),如果数据包未被修改,则可能支持 DSR(直接服务器返回,无需再次通过负载均衡器),但几乎不提供内容感知。这种技术非常适合网络级别的负载均衡,尽管有时也用于非常基本的服务器负载均衡,速度非常快。第二种技术作用于会话内容。它要求将输入流重新组合并作为一个整体进行处理。内容可能会被修改,并且输出流将被分割成新的数据包。因此,它通常由代理执行,并且它们经常被称为第 7 层负载均衡器或 L7。这意味着每侧有两个独立的连接,并且输入和输出数据包的大小或计数之间没有关系。客户端和服务器不需要使用相同的协议(例如,IPv4 与 IPv6,明文与 SSL)。操作始终是有状态的,并且返回流量必须通过负载均衡器。额外的处理是有成本的,因此并非总是能够实现线路速率,尤其是对于小数据包。另一方面,它提供了广泛的可能性,并且通常由纯软件实现,即使嵌入到硬件设备中。这种技术非常适合服务器负载均衡。基于数据包的负载均衡器通常以直通模式部署,因此它们安装在流量的正常路径上,并根据配置将其分流。返回流量不一定通过负载均衡器。为了将流量导向正确的目的地,可能会对网络目标地址进行一些修改。在这种情况下,返回流量必须通过负载均衡器。如果路由无法实现这一点,负载均衡器也可能用自己的地址替换数据包的源地址,以强制返回流量通过它。基于代理的负载均衡器部署为具有自己 IP 地址和端口的服务器,而无需进行架构更改。有时这需要对应用程序进行一些调整,以便客户端被正确地导向到负载均衡器的 IP 地址而不是直接导向到服务器的 IP 地址。一些负载均衡器可能需要调整某些服务器的响应来实现这一点(例如,HTTP Location 标头字段用于 HTTP 重定向)。一些基于代理的负载均衡器可能会拦截它们不拥有的地址的流量,并在连接到服务器时伪装客户端的地址。这使得它们可以像常规路由器或防火墙一样部署,以直通模式运行,与基于数据包的负载均衡器非常相似。这对于结合了数据包模式和代理模式的产品尤其受到赞赏。在这种情况下,DSR 显然仍然不可能,并且返回流量仍然必须路由回负载均衡器。一种非常可扩展的分层方法将是拥有一个接收来自多个负载均衡链路的流量的前端路由器,并使用 ECMP 将此流量分发到第一层的多个有状态数据包负载均衡器(L4)。这些 L4 负载均衡器进而将流量传递给更多基于代理的负载均衡器(L7),这些负载均衡器必须解析内容才能决定哪个服务器最终将接收流量。组件数量和流量可能的路径会增加失败的风险;在非常大的环境中,即使有少量故障组件被修复或替换也是很正常的。不了解整个堆栈健康状况的负载均衡会严重降低可用性。因此,任何合理的负载均衡器都会验证它打算将流量传输到的组件仍然是活动的且可访问的,并且它将停止将流量传输到有故障的组件。这可以通过各种方法实现。最常见的方法包括定期发送探测以确保组件仍然运行。这些探测称为“健康检查”。它们必须能够代表要解决的故障类型。例如,基于 ping 的检查将无法检测到 Web 服务器已崩溃并且不再监听端口,而连接到端口将验证这一点,更高级的请求甚至可以验证服务器仍然正常工作并且它依赖的数据库仍然可访问。健康检查通常涉及几次重试,以涵盖偶尔的测量错误。检查之间的间隔必须足够小,以确保在发生错误后,有故障的组件不会被使用太长时间。其他方法包括对发送到目的地的生产流量进行采样,以观察它是否被正确处理,并驱逐返回不当响应的组件。但是,这需要牺牲一部分生产流量,这并不总是可接受的。这两种机制的组合提供了两全其美,两者都用于检测故障,而仅使用健康检查来检测故障的结束。最后一种方法涉及集中报告:一个中央监控代理定期向所有负载均衡器更新所有组件的状态。这为所有组件提供了基础设施的全局视图,尽管有时准确性或响应性较低。它最适合拥有许多负载均衡器和许多服务器的环境。第 7 层负载均衡器还面临另一个称为粘性或持久性的挑战。原则是它们通常必须将来自同一源(例如,最终用户)的多个后续请求或连接导向到同一目标。最著名的例子是在线商店的购物车。如果每次点击都会导致新的连接,那么用户必须始终被发送到持有其购物车的服务器。内容感知使得在请求中识别某些元素以识别要发送的服务器更容易,但这并不总是足够。例如,如果将源地址用作选择服务器的键,则可以决定使用基于哈希的算法,并且给定 IP 地址将根据地址除以可用服务器数量的结果始终发送到同一服务器。但是,如果一台服务器发生故障,结果会发生变化,所有用户都会突然被发送到另一台服务器,并丢失其购物车。解决此问题的方案是记住所选目标,以便每次看到相同的访问者时,无论可用服务器数量如何,他都会被导向到同一台服务器。信息可以存储在负载均衡器的内存中,在这种情况下,如果它不是唯一的,则可能需要将其复制到其他负载均衡器,或者可以将其存储在客户端的内存中使用提供的各种方法,前提是客户端能够每次都提供此信息(cookie 插入、重定向到子域等)。此机制提供了不依赖不稳定或分布不均的信息(例如源 IP 地址)的额外好处。这实际上是采用第 7 层负载均衡器而不是第 4 层负载均衡器的最强原因。为了提取诸如 cookie、主机标头字段、URL 或任何其他信息之类的信息,负载均衡器可能需要解密 SSL/TLS 流量,甚至可能在将其传递给服务器时重新加密。这项昂贵的工作解释了为什么在某些高流量基础设施中,有时会有很多负载均衡器。由于第 7 层负载均衡器可能对流量执行许多复杂的操作(解密、解析、修改、匹配 cookie、决定发送到哪个服务器等),因此它肯定会引起一些麻烦,并且通常会被指责是许多它仅暴露出来的麻烦的罪魁祸首。通常会发现服务器不稳定,并且会周期性地启动和停止,或者对于 Web 服务器,它们会提供一些硬编码链接的页面,迫使客户端直接连接到某个特定服务器而不经过负载均衡器,或者它们在高负载下需要很长时间才能响应,导致超时。这就是为什么日志记录是第 7 层负载均衡的一个极其重要的方面。一旦报告了麻烦,就必须弄清楚负载均衡器是否做出了错误的决定,如果是,为什么会发生这种情况,以免再次发生。
HAProxy 写成“HAProxy”以指代产品,写成“haproxy”以指代可执行程序、软件包或进程。然而,两者通常都用于这两个目的,并且读作 H-A-Proxy。很早以前,“haproxy”代表“high availability proxy”,并且这个名字被写成两个独立的单词,尽管现在它只代表“HAProxy”。
HAProxy 是: - TCP 代理:它可以从监听套接字接受 TCP 连接,连接到服务器并将这些套接字连接起来,允许双向通信;IPv4、IPv6 甚至 UNIX 套接字都支持双方,因此可以提供一种在不同族之间转换地址的便捷方式。 - HTTP 反向代理(在 HTTP 术语中称为“网关”):它将自己呈现为一个服务器,通过监听 TCP 套接字接受的连接接收 HTTP 请求,并将这些连接的请求转发给使用不同连接的服务器。它可以在任何一侧使用任何 HTTP/1.x 或 HTTP/2 的组合,甚至可以使用 ALPN over TLS 在每侧自动检测所使用的协议。 - SSL 终止/发起/卸载:SSL/TLS 可用于来自客户端的连接、到服务器的连接,甚至用于两个连接。可以按名称(SNI)应用许多设置,并且可以在运行时更新而无需重新启动。这种设置具有极高的可扩展性,并已报告了涉及数十万甚至数十万个证书的部署。 - TCP 正常化器:由于连接由操作系统本地终止,因此双方之间没有关联,因此异常流量(如无效数据包、标志组合、窗口广告、序列号、不完整连接(SYN Flood))不会传递到另一方。这可以保护脆弱的 TCP 堆栈免受协议攻击,并且还允许优化与客户端的连接参数,而无需修改服务器的 TCP 堆栈设置。 - HTTP 正常化器:当配置为处理 HTTP 流量时,只传递有效且完整的请求。这可以防止许多基于协议的攻击。此外,还修复了规范中允许的协议偏差,使其不会在服务器上引起问题(例如,多行标头)。 - HTTP 修复工具:它可以修改/修复/添加/删除/重写 URL 或任何请求或响应标头。这有助于修复复杂环境中的互操作性问题。 - 基于内容的开关:它可以考虑请求中的任何元素来决定将请求或连接传递给哪个服务器。因此,可以在同一端口上处理多种协议(例如,HTTP、HTTPS、SSH)。 - 服务器负载均衡器:它可以负载均衡 TCP 连接和 HTTP 请求。在 TCP 模式下,负载均衡决策是针对整个连接做出的。在 HTTP 模式下,决策是针对每个请求做出的。 - 流量调节器:它可以在不同点应用一些速率限制,保护服务器免过载,根据内容调整流量优先级,甚至通过标记数据包将此类信息传递给较低层和外部网络组件。 - 防止 DDoS 和服务滥用:它可以维护每个 IP 地址、URL、cookie 等的大量统计信息,并检测滥用何时发生,然后采取行动(减慢攻击者速度、阻止他们、将他们发送到过时内容等)。 - 网络故障排除的观察点:由于日志中报告的信息非常精确,因此通常用于缩小网络相关问题的范围。 - HTTP 压缩卸载器:它可以压缩未由服务器压缩的响应,从而减少连接不佳或使用高延迟、移动网络的客户端的页面加载时间。 - 缓存代理:它可以将响应缓存在 RAM 中,因此只要对象仍然存在且有效,后续对同一对象的请求就可以避免服务器的网络传输成本。但是,它不会将对象存储到任何持久存储中。请注意,此缓存功能旨在免维护,并且仅专注于节省 haproxy 的宝贵资源,而不是节省服务器的资源。旨在优化服务器的缓存需要更多的调整和灵活性。如果您需要此类高级缓存,请使用 Varnish Cache,它与 haproxy 集成得很好,尤其是在任何一侧都需要 SSL/TLS 时。 - FastCGI 网关:FastCGI 可以被视为 HTTP 的不同表示,因此,HAProxy 可以直接负载均衡由任何 FastCGI 应用程序服务器组合组成的集群,而无需在它们之间插入另一层网关。这可以节省资源并降低维护成本。HAProxy 不是: - 显式 HTTP 代理,即浏览器用于访问 Internet 的代理。有优秀的开源软件专门用于此任务,例如 Squid。但是,可以将 HAProxy 安装在此类代理的前面,以提供负载均衡和高可用性。 - 数据清理器:它不会修改请求或响应的正文。 - 静态 Web 服务器:在启动期间,它会将自身隔离在 chroot jail 中并放弃其权限,以便在启动后不会执行任何单一的文件系统访问。因此,它不能被转换成静态 Web 服务器(尽管通过 FastCGI 支持动态服务器)。有优秀的开源软件可用于此,例如 Apache 或 Nginx,并且可以将 HAProxy 轻松地安装在它们前面,以提供负载均衡、高可用性和加速。 - 基于数据包的负载均衡器:它看不到 IP 数据包或 UDP 数据报,也不会执行 NAT,更不用说 DSR。这些是较低层的任务。一些基于内核的组件(如 IPVS(Linux 虚拟服务器))已经做得很好,并且与 HAProxy 完美互补。
HAProxy 是一个事件驱动的非阻塞引擎,它将一个非常快的 I/O 层与一个基于优先级的多线程调度器相结合。由于它的设计目标是数据转发,因此它的架构经过优化,可以以最少的操作尽可能快地移动数据。它专注于通过尽可能长时间地将连接绑定到同一个 CPU 来优化 CPU 缓存的效率。因此,它实现了一个分层模型,在每个级别提供旁路机制,确保在需要时数据才会到达更高级别。大部分处理在内核中完成,HAProxy 通过提供一些提示或避免某些操作(当它猜测它们可以稍后分组时)来尽最大努力帮助内核尽可能快地完成工作。因此,典型数据显示,在 TCP 或 HTTP 关闭模式下,HAProxy 中花费的处理时间为 15%,而在内核中为 85%;在 HTTP keep-alive 模式下,HAProxy 占约 30%,内核占 70%。单个进程可以运行多个代理实例;据报告,单个进程中配置高达 300,000 个不同的代理的运行效果良好。单核、单 CPU 设置对于超过 99% 的用户来说绰绰有余,因此,鼓励容器和虚拟机用户使用他们能获得的最小的镜像,以节省运营成本并简化故障排除。但是,HAProxy 运行的机器绝不能进行交换,其 CPU 不能被人为限制(虚拟机中的子 CPU 分配),也不能与计算密集型进程共享,否则会导致非常高的上下文切换延迟。线程允许通过使用每个 CPU 核心一个线程来利用所有可用的处理能力。这主要在 SSL 或需要超过 40 Gbps 的数据转发速率时有用。在这种情况下,避免多个物理 CPU 之间的通信至关重要,因为这可能导致网络堆栈和 HAProxy 本身的强大瓶颈。虽然对某些人来说这似乎违反直觉,但在遇到性能问题时,首先要做的事情通常是减少 HAProxy 运行的 CPU 数量。HAProxy 仅需要 haproxy 可执行文件和配置文件即可运行。对于日志记录,强烈建议有一个正确配置的 syslog 守护进程和日志轮转。日志也可以发送到 stdout/stderr,这在容器内部很有用。配置文件在启动前进行解析,然后 HAProxy 尝试绑定所有监听套接字,如果任何失败则拒绝启动。在此之后,它就不会再失败了。这意味着没有运行时故障,并且如果它接受启动,它将一直工作直到被停止。一旦 HAProxy 启动,它只做 3 件事: - 处理传入连接; - 定期检查服务器状态(称为健康检查); - 与其他 haproxy 节点交换信息。处理传入连接是迄今为止最复杂的任务,因为它取决于许多配置可能性,但可以将其概括为以下 9 个步骤: - 从属于称为“前端”的配置实体的监听套接字接受传入连接,该实体引用一个或多个监听地址; - 对这些连接应用前端特定的处理规则,这可能会导致阻塞它们、修改某些标头,或拦截它们以执行某些内部小程序,如统计信息页面或 CLI; - 将这些传入连接传递给另一个代表服务器集群的配置实体,该实体称为“后端”,其中包含服务器列表和该服务器集群的负载均衡策略; - 对这些连接应用后端特定的处理规则; - 根据负载均衡策略决定将连接转发给哪个服务器; - 对响应数据应用后端特定的处理规则; - 对响应数据应用前端特定的处理规则; - 发出日志以详细报告发生了什么; - 在 HTTP 中,循环回到第二步以等待新请求,否则关闭连接。前端和后端有时被视为半代理,因为它们只关注端到端连接的一侧;前端只关心客户端,而后端只关心服务器。HAProxy 也支持完整的代理,它们是前端和后端的精确联合。当需要 HTTP 处理时,配置通常会拆分为前端和后端,因为它们提供了大量的可能性,因为任何前端都可以将连接传递给任何后端。对于仅 TCP 的代理,使用前端和后端很少有好处,并且配置可以通过完整代理更具可读性。
本节将列举 HAProxy 实现的许多功能,其中一些是任何现代负载均衡器通常期望的,而另一些则是 HAProxy 架构的直接优势。更高级的功能将在下一节中详细介绍。
代理是将数据通过两个独立连接在客户端和服务器之间传输的操作。HAProxy 在代理和连接管理方面支持以下基本功能: - 为服务器提供干净的连接,以保护它们免受任何客户端侧的缺陷或攻击; - 监听多个 IP 地址和/或端口,甚至端口范围; - 透明接受:拦截针对任何不属于本地系统的任意 IP 地址的流量; - 服务器端口不需要与监听端口相关联,甚至可以通过固定偏移量进行转换(范围有用); - 透明连接:在连接到服务器时,根据需要伪装客户端(或任何)IP 地址; - 为多站点负载均衡器提供可靠的返回 IP 地址给服务器; - 通过缓冲区和可能的短期连接来卸载服务器,以减少其并发连接数和内存占用; - 优化 TCP 堆栈(例如 SACK)、拥塞控制,并减少 RTT 影响; - 支持双方不同的协议族(例如,IPv4/IPv6/Unix); - 超时强制执行:HAProxy 支持多个级别的超时,具体取决于连接所处的阶段,这样死客户端或服务器,或攻击者就不会长时间占用资源; - 协议验证:检查 HTTP、SSL 或负载,并拒绝无效的协议元素,除非指示它们仍然接受; - 策略强制执行:确保只有允许的内容才会被转发; - 入站和出站连接都可以限制在特定的网络命名空间(仅限 Linux),从而可以轻松构建跨容器、多租户的负载均衡器; - PROXY 协议即使对于非 HTTP 流量,也能将客户端的 IP 地址呈现给服务器。这是 HAProxy 的一个扩展,已被许多第三方产品采用,至少在撰写本文时包括以下产品: - 客户端:haproxy、stud、stunnel、exaproxy、ELB、squid - 服务器:haproxy、stud、postfix、exim、nginx、squid、node.js、varnish
HAProxy 的 SSL 栈被 Google 工程师认为是最具功能的栈之一(http://istlsfastyet.com/)。最常用的功能使其相当完整,包括: - 基于 SNI 的多宿主,对站点数量没有限制,并专注于性能。已知至少有一个部署运行了 50,000 个域及其各自的证书; - 支持通配符证书,减少了对许多证书的需求; - 基于证书的客户端身份验证,具有对未提供有效证书的失败情况的可配置策略。例如,这允许呈现一个不同的服务器集群来重新生成客户端证书; - 后端服务器的身份验证确保后端服务器是真实的,而不是中间人; - 与后端服务器的身份验证允许后端服务器知道连接到它的是预期的 haproxy 节点; - TLS NPN 和 ALPN 扩展使得可靠地卸载 SPDY/HTTP2 连接并以明文形式传递给后端服务器成为可能; - OCSP 钉扎通过在客户端请求证书状态请求时内联传递 OCSP 响应,进一步减少了首次页面加载时间; - 动态记录大小调整同时提供高性能和低延迟,并通过允许浏览器在数据包仍在传输时开始获取新对象,显著减少页面加载时间; - 永久访问所有相关的 SSL/TLS 层信息,用于日志记录、访问控制、报告等。这些元素可以嵌入到 HTTP 标头中,甚至作为 PROXY 协议扩展,以便卸载的服务器可以获得如果在 SSL 终止时执行 SSL 终止所需的所有信息。 - 检测、记录和阻止某些已知的攻击,即使在易受攻击的 SSL 库上,例如影响 OpenSSL 某些版本的 Heartbleed 攻击。 - 支持无状态会话恢复(RFC 5077 TLS Ticket 扩展)。TLS 票据可以从 CLI 更新,这为它们提供了通过频繁轮换票据来实现完美前向保密的方法。
HAProxy 非常注重可用性。因此,它关心服务器状态,并向其他网络组件报告自身状态: - 服务器状态通过每个服务器参数持续监控。这确保了服务器的路径对于常规流量是可操作的; - 健康检查支持上/下转换的两种滞后,以防止状态抖动; - 检查可以发送到不同的地址/端口/协议:这使得检查被认为是多个服务的代表的单个服务变得容易,例如,HTTP+HTTPS 服务器的 HTTPS 端口。 - 服务器可以跟踪其他服务器并同时宕机:这确保托管多个服务的服务器可以原子化失败,并且没有人会被发送到部分失败的服务器; - 代理可以部署在服务器上以监控负载和健康状况:服务器可能希望报告其负载、运行状态、管理状态,独立于健康检查可以看到的内容。通过在服务器上运行一个简单的代理,除了验证整个路径的健康检查之外,还可以考虑服务器自身健康状况的视图; - 提供各种检查方法:TCP 连接、HTTP 请求、SMTP hello、SSL hello、LDAP、SQL、Redis、send/expect 脚本,所有这些都可以带/不带 SSL; - 状态更改会在日志和统计页面中通知,并带有失败原因(例如,检测到失败时收到的 HTTP 响应)。在发生此类更改时,也可以向可配置的地址发送电子邮件; - 服务器状态也在统计界面上报告,并可用于做出路由决策,以便根据其大小和/或健康状况(例如,DC 间链路丢失)将流量发送到不同的集群; - HAProxy 可以使用健康检查请求将信息传递给服务器,例如它们的名称、权重、集群中的其他服务器数量等,以便服务器可以根据这些知识调整其响应和决策(例如,推迟备份以腾出更多 CPU); - 服务器可以使用健康检查来报告比“开/关”更详细的状态(例如,我想停止,请不要再发送新访客); - HAProxy 本身可以向外部组件(如路由器或其他负载均衡器)报告其状态,从而可以构建非常完整的多路径和多层基础设施。
与其他任何严肃的负载均衡器一样,HAProxy 非常重视可用性,以确保最佳的全局服务连续性: - 只使用有效的服务器;其他服务器会自动从负载均衡集群中剔除;但在某些条件下仍然可以强制使用它们; - 支持优雅关闭,以便可以在不影响任何连接的情况下将服务器移出集群; - 当活动服务器宕机时,备份服务器会自动使用并替换它们,以便在可能的情况下不会丢失会话。这还允许构建到达同一服务器的多个路径(例如,多个接口); - 当太多服务器宕机时,可以为集群返回全局失败状态。这与监控功能相结合,使得上游组件可以选择一个不同的 LB 节点来提供给定服务; - 无状态设计易于构建集群:HAProxy 本身就尽最大努力确保最高的连续性,而无需存储可能在发生故障时丢失的信息。这确保了接管过程尽可能无缝; - 与标准 VRRP 守护进程 keepalived 集成良好:HAProxy 可以轻松地将自己的状态告知 keepalived,并很好地处理浮动的虚拟 IP 地址。注意:仅当群集解决方案(Heartbeat、...)提供最快、最无缝、最可靠的切换时,才在群集解决方案(Heartbeat、...)上使用 IP 冗余协议(VRRP/CARP)。
HAProxy 提供了一套相当完整的负载均衡功能,其中大多数在许多其他负载均衡产品中都无法获得: - 支持不少于 10 种负载均衡算法,其中一些适用于输入数据,提供无限的可能性。最常见的包括:轮询(对于短连接,依次选择每个服务器)、最少连接(对于长连接,选择连接数最少且最近最少使用的服务器)、源(对于 SSL 集群或终端服务器集群,服务器直接取决于客户端的源地址)、URI(对于 HTTP 缓存,服务器直接取决于 HTTP URI)、hdr(服务器直接取决于特定 HTTP 标头字段的内容)、first(对于短暂的虚拟机,所有连接都打包到尽可能小的服务器子集中,以便未使用的服务器可以断电); - 所有上述算法都支持每个服务器的权重,因此可以容纳集群中的不同服务器代,或将一小部分流量定向到特定服务器(调试模式、运行软件的下一个版本等); - 轮询、最少连接和一致性哈希支持动态权重;这允许从 CLI 甚至由服务器上的代理修改服务器权重; - 只要支持动态权重,就支持慢启动;这允许服务器逐渐处理流量。这对于需要运行时编译类以及需要填充冷缓存才能全速运行的脆弱应用程序服务器来说是一项重要功能; - 哈希可以应用于各种元素,例如客户端的源地址、URL 组件、查询字符串元素、标头字段值、POST 参数、RDP cookie; - 一致性哈希可以在添加或移除服务器时保护服务器集群免受大规模重新分配。这在大型缓存集群中非常重要,并且允许使用慢启动来填充冷缓存; - 一系列内部指标,例如每个服务器、每个后端的连接数、后端可用连接槽的数量等,使得构建非常高级的负载均衡策略成为可能。
没有粘性,应用程序负载均衡将毫无用处。HAProxy 提供了一套相当全面的可能性,可以在各种事件(如服务器添加/移除、上下线周期)中将访问者保持在同一服务器上,并且某些方法旨在抵抗多个负载均衡节点之间的距离,因为它们不需要任何复制: - 如果需要,粘性信息可以单独匹配并从不同来源学习。例如,JSESSIONID cookie 可以在 cookie 和 URL 中匹配。最多可以同时学习 8 个并行源,每个源可能指向不同的粘性表; - 粘性信息可以来自请求或响应中可以看到的任何内容,包括源地址、TCP 负载偏移量和长度、HTTP 查询字符串元素、标头字段值、cookie 等。 - 粘性表以主-主模式在所有节点之间复制; - 常用的元素,如 SSL-ID 或 RDP cookie(用于 TSE 集群),可以直接访问以方便操作; - 所有粘性规则都可以通过 ACL 动态条件化; - 可以决定不粘附某些服务器,例如备份服务器,以便在主服务器回来时,它会自动接管负载。这在多路径环境中经常使用; - 在 HTTP 中,通常倾向于不学习任何内容,而是操作专用于粘性的 cookie。为此,可以检测、重写、插入或前缀该 cookie,以便客户端记住分配了哪个服务器; - 服务器可以决定在注销时更改或清除粘性 cookie,以便离开的访问者可以自动与服务器解绑; - 使用基于 ACL 的规则,也可以选择性地忽略或强制执行粘性,而不管服务器的状态如何;结合高级健康检查,这有助于管理员在将服务器公开给全世界之前验证他们正在安装的服务器是否正常运行; - 一种创新的机制,用于设置 cookie 的最大空闲时间和持续时间,确保粘性可以在永不关闭的设备(智能手机、电视、家用电器)上平滑停止,而无需将其存储在持久存储中; - 多个服务器条目可以共享相同的粘性键,以便在一条路径断开的多路径环境中不会丢失粘性; - 软停止可确保只有具有粘性信息的用户将继续访问他们被分配到的服务器,但新用户将不会去那里。
日志记录是负载均衡器的一项极其重要的功能,首先是因为负载均衡器经常被错误地指责导致它暴露的问题,其次是因为它位于基础设施的关键点,需要分析所有正常和异常活动并与其他组件进行关联。HAProxy 提供非常详细的日志,具有毫秒精度和确切的连接接受时间,可以在防火墙日志中进行搜索(例如,用于 NAT 关联)。默认情况下,TCP 和 HTTP 日志非常详细,包含故障排除所需的一切,例如源 IP 地址和端口、前端、后端、服务器、计时器(请求接收持续时间、队列持续时间、连接设置时间、响应标头时间、数据传输时间)、全局进程状态、连接计数、队列状态、重试计数、详细的粘性操作和断开原因、具有安全输出编码的标头捕获。然后可以扩展或替换此格式以包含任何采样数据、变量、捕获,从而产生非常详细的信息。例如,可以记录客户端的累积请求数或访问的不同 URL 数。可以使用标准 ACL 按请求调整日志级别,因此可以自动静默某些被视为污染的日志,并在部分流量出现异常行为时提高警告(例如,源地址的 URL 或 HTTP 错误过多)。管理日志也会以自己的级别发出,以通知服务器的丢失或恢复等。每个前端和后端可以使用多个独立的日志输出,这便于多租户。日志首选通过 UDP 发送,可能是 JSON 编码,并在可配置的行长后截断,以保证传输。但也可以将它们发送到 stdout/stderr 或任何文件描述符,以及发送到一个客户端可以订阅的环形缓冲区以检索它们。
HAProxy 提供一个基于 Web 的统计信息报告界面,具有身份验证、安全级别和范围。因此,可以为每个托管的客户提供他们自己的页面,只显示他们自己的实例。此页面可以位于常规网站的隐藏 URL 部分,这样就不需要打开新端口。此页面还可以报告其他 HAProxy 节点的可用性,以便一目了然地发现一切是否按预期工作。视图是综合性的,并且有很多详细信息可供访问(例如,错误原因、最后访问时间和最后更改时长等),这些信息也可作为 CSV 表格提供,其他工具可以导入该表格来绘制图表。页面可以自我刷新,以便在大显示器上用作监控页面。在管理模式下,该页面还允许更改服务器状态以简化维护操作。还提供了一个 Prometheus 导出器,以便统计信息可以根据部署以不同的格式进行消耗。
在本节中,列举了一些在 HAProxy 中非常常用但并非在其他负载均衡器上普遍存在的功能。
HAProxy 支持使用大量的“样本提取函数”进行信息采样。原理是提取称为样本的信息片段,供即时使用。这用于粘性、构建条件、在日志中生成信息或丰富 HTTP 标头。样本可以从各种来源提取: - 常量:整数、字符串、IP 地址、二进制块; - 进程:日期、环境变量、服务器/前端/后端/进程状态、字节/连接计数/速率、队列长度、随机生成器等; - 变量:每个会话、每个请求、每个响应变量; - 客户端连接:源和目标地址和端口,以及所有相关的统计计数器; - SSL 客户端会话:协议、版本、算法、密码、密钥大小、会话 ID、所有客户端和服务器证书字段、证书序列号、SNI、ALPN、NPN、客户端对某些扩展的支持; - 请求和响应缓冲区内容:偏移量/长度处的任意负载、数据长度、RDP cookie、SSL hello 类型的解码、TLS SNI 的解码; - HTTP(请求和响应):方法、URI、路径、查询字符串参数、状态码、标头值、位置标头值、cookie、捕获、身份验证、正文元素; 然后,样本可以经过一系列称为“转换器”的操作符进行转换。转换器消耗一个样本并生成一个新的样本,可能是完全不同的类型。例如,转换器可用于仅返回输入字符串的整数长度,或将字符串转换为大写。在最终使用之前,可以将任意数量的转换器按顺序应用于样本。在所有可用的样本转换器中,以下是最常用的: - 算术和逻辑运算符:它们使得可以对输入数据执行高级计算,例如计算比率、百分比或简单地从一个单位转换为另一个单位; - IP 地址掩码在需要按较大网络对某些地址进行分组时非常有用; - 数据表示:URL 解码、base64、十六进制、JSON 字符串、哈希; - 字符串转换:提取固定位置的子字符串、固定长度、提取分隔符周围的特定字段、提取特定单词、更改大小写、应用基于正则表达式的替换; - 日期转换:转换为 HTTP 日期格式、将本地转换为 UTC 并反之、添加或删除偏移量; - 从粘性表中查找条目以查找统计信息或分配的服务器; - 基于映射的文件键值转换(主要用于地理定位)。
映射是一种强大的转换器类型,它通过在启动时将一个两列文件加载到内存中,然后从第一列查找每个输入样本,如果找到条目,则返回第二列中的相应模式,否则返回默认值。输出信息也是一个样本,它可以经历其他转换,包括其他映射查找。映射最常用于将客户端的 IP 地址转换为 AS 号或国家代码,因为它们支持网络地址的最长匹配,但它们可以用于各种其他目的。它们的部分优势在于它们可以从 CLI 或通过其他样本的某些操作进行动态更新,从而使它们能够存储和检索后续访问之间的信息。另一个优势来自基于二进制树的索引,这使得它们即使包含数十万个条目也极其快速,从而使地理位置的设置非常便宜且容易。
HAProxy 中的大多数操作都可以条件化。条件是通过使用逻辑运算符(AND、OR、NOT)组合多个 ACL 来构建的。每个 ACL 是一系列测试,基于以下元素: - 一个样本提取方法,用于检索要测试的元素; - 可选的一系列转换器,用于转换元素; - 要匹配的模式列表; - 一个匹配方法,指示如何将模式与样本进行比较。例如,样本可以从 HTTP “Host”标头中提取,然后可以转换为小写,然后使用正则表达式匹配方法与多个正则表达式模式进行匹配。从技术上讲,ACL 基于与映射相同的核心构建,它们共享完全相同的内部结构、模式匹配方法和性能。唯一真正的区别是,它们不像返回样本那样,只返回“找到”或“未找到”。在使用方面,ACL 模式可以在配置文件中内联声明,而不需要自己的文件。ACL 可以命名,以便于使用或使配置更易于理解。一个命名的 ACL 可以声明多次,它将按顺序评估所有定义,直到有一个匹配。提供了大约 13 种不同的模式匹配方法,其中包括 IP 地址掩码、整数范围、子字符串、正则表达式。它们的功能类似,就像任何编程语言一样,只评估需要的部分,因此当涉及 OR 的条件已经为真时,就不会评估后续条件,同样,当涉及 AND 的条件已经为假时,也不会评估其余条件。声明的 ACL 数量没有实际限制,并提供了一些常用的 ACL。然而,经验表明,使用大量命名 ACL 的设置很难进行故障排除,有时内联使用匿名 ACL 更容易,因为它需要的范围外引用更少。
HAProxy 实现了一种称为基于内容的切换的机制。原理是连接或请求到达一个前端,然后处理此请求或连接携带的信息,此时可以根据 ACL 编写条件,利用这些信息来决定哪个后端将处理该请求。因此,流量根据请求的内容被导向一个后端或另一个后端。最常见的例子是使用 Host 标头和/或路径中的元素(子目录或文件名扩展名)来决定 HTTP 请求是针对静态对象还是应用程序,并将静态对象的流量路由到一个由快速轻量级服务器组成的集群,而其余流量路由到一个更复杂的应用程序服务器,从而构成一种精细的虚拟主机解决方案。这对于使多种技术共存为更全局的解决方案非常方便。基于内容的切换的另一个用例是根据各种标准使用不同的负载均衡算法。缓存可以使用 URI 哈希,而应用程序将使用轮询。最后但同样重要的是,它允许多个客户共享一小部分通用资源,通过强制执行每个后端的连接限制(即每个客户的连接限制)。基于内容的切换规则的可扩展性很好,尽管它们的性能可能取决于 ACL 的数量和复杂性。但是,也可以编写动态基于内容的切换规则,其中样本值直接变成后端名称,而根本不使用 ACL。据报道,这种配置在生产环境中至少具有 300,000 个后端的运行效果良好。
粘性表通常用于存储粘性信息,即跟踪将某个访问者导向的服务器的引用。键是与访问者关联的标识符(其源地址、连接的 SSL ID、HTTP 或 RDP cookie、从 URL 或负载中提取的客户编号等),然后存储的值是服务器的标识符。粘性表可以使用 3 种不同类型的样本作为其键:整数、字符串和地址。每个代理最多只能引用一个粘性表,并且在所有地方都使用代理名称进行指定。最多可以并行跟踪 8 个键。服务器标识符在请求或响应处理过程中提交,一旦键和服务器都已知。粘性表的内容可以与称为“对等节点”的其他 HAProxy 节点以主-主模式复制,以及在重新加载操作期间与新进程复制,以便所有负载均衡节点共享相同的信息并在客户端请求分布在多个节点时做出相同的路由决策。由于粘性表是基于能够识别客户端的内容进行索引的,因此它们也经常用于存储额外信息,例如每个客户端的统计信息。额外的统计信息会占用一些额外空间,并且需要显式声明。可能存储的统计信息类型包括输入和输出带宽、并发连接数、一段时间内的连接速率和计数、错误量和频率、某些特定标签和计数器等。为了支持在不强制粘附到给定服务器的情况下保留此类信息,实现了一种特殊的“跟踪”功能,该功能允许同时跟踪来自不同表的最多 3 个并发键,而不管粘性规则如何。每个存储的统计信息都可以从 CLI 搜索、转储和清除,并增加了实时故障排除功能。虽然此机制可用于区分回头客或根据良好/不良行为调整提供的服务质量,但它主要用于打击服务滥用和更广泛的 DDoS,因为它允许构建复杂的模型以高速处理来检测某些不良行为。
HAProxy 需要在许多地方处理字符串,例如日志、重定向、标头添加等。为了提供最大的灵活性,引入了“格式化字符串”的概念,最初是为了日志记录目的,因此它仍然被称为“log-format”。这些字符串包含转义字符,允许将各种动态数据(包括变量和样本获取表达式)引入字符串,甚至在结果被转换为字符串时调整编码(例如,添加引号)。这提供了一种强大的方法来构建标头内容、响应数据甚至响应模板,或自定义日志行。此外,为了保持大多数常见字符串的构建简单,提供了大约 50 个特殊标签作为日志中常用信息的快捷方式。
在应用程序未针对负载均衡器进行设计的情况下,在应用程序前面安装负载均衡器可能是一项具有挑战性的任务,如果没有合适的工具。在这种情况下,最常见的操作之一是调整请求和响应标头,以使负载均衡器显示为源服务器并修复硬编码的信息。这包括更改请求中的路径(强烈反对这样做)、修改 Host 标头字段、修改 Location 响应标头字段用于重定向、修改 cookie 的路径和域属性等等。有时,许多服务器有点冗余,倾向于在响应中泄露过多信息,使其更容易受到针对性攻击。虽然理论上清理这些内容不是负载均衡器的作用,但实际上它位于基础设施的最佳位置,可以确保一切都得到清理。同样,有时负载均衡器需要拦截某些请求并以重定向到新目标 URL 的响应。虽然有些人倾向于混淆重定向和重写,但这是两个完全不同的概念,因为重写使客户端和服务器看到不同的内容(并且在页面访问位置上不一致),而重定向要求客户端访问新 URL,以便它看到与服务器相同的位置。为此,HAProxy 支持各种重写和重定向的可能性,其中包括: - 基于正则表达式的请求和响应中的 URL 和标头重写。正则表达式是修改标头值的最常用工具,因为它们易于操作且易于理解; - 标头也可以根据格式化的字符串进行追加、删除或替换,以便可以传递信息(例如,客户端 TLS 算法和密码); - HTTP 重定向可以使用任何 3xx 代码到相对、绝对或完全动态(格式化字符串)的 URI; - HTTP 重定向还支持一些额外选项,例如设置或清除特定 cookie、删除查询字符串、在缺少时附加斜杠等; - 强大的“return”指令允许使用动态内容甚至模板文件来自定义响应的每个部分,如状态、标头、正文; - 所有操作都支持基于 ACL 的条件;
HAProxy 在最大化服务可用性方面做了很多工作,为此它付出了巨大的努力来保护服务器免受过载和攻击。第一个也是最重要的观点是,只有完整且有效的请求才会被转发到服务器。最初的原因是 HAProxy 需要找到协议元素,它需要与字节流保持同步,第二个原因是直到请求完成,才有可能知道某些元素是否会改变其语义。这样做直接的好处是服务器不会暴露给无效或不完整的请求。这是对慢速攻击者(Slowloris)非常有效的保护,它们对 HAProxy 的影响几乎为零。另一个要点是,HAProxy 包含用于存储请求和响应的缓冲区,并且通过仅在请求完成后才将请求发送到服务器,并从本地网络非常快速地读取整个响应,服务器端连接的使用时间非常短,这尽可能地节省了服务器资源。一个直接的扩展是,HAProxy 可以人为地限制到服务器的并发连接数或未决请求数,即使在流量高峰期服务器持续以 100% 的容量运行,也保证服务器不会过载。所有过多的请求都将被排队,以便在释放一个槽时进行处理。最终,这种巨大的资源节省通常能确保更好的服务器响应时间,以至于最终比服务器过载还要快。排队的请求可能会重新分发到其他服务器,甚至在客户端中止时在队列中中止,这也保护了服务器免受“重新加载效应”,即访问者在加载缓慢的页面上每次单击“重新加载”通常会产生一个新请求并使服务器保持过载状态。慢启动机制还可以在服务器启动完成或编译某些类时保护重新启动的服务器免受高流量。关于协议级别的保护,可以放宽 HTTP 解析器以接受非标准兼容但无害的请求或响应,甚至修复它们。这允许在开发修复程序时访问错误应用程序。同时,攻击性消息被完全捕获,并附有详细报告,帮助开发人员发现应用程序中的问题。最危险的协议违规会得到妥善检测、处理和修复。例如,具有两个 Content-length 标头的格式错误的请求或响应,如果值完全相同,则会被修复,如果不同则会被拒绝,因为这会成为安全问题。协议检查不限于 HTTP,也适用于 TLS 或 RDP 等其他协议。检测到协议违规或攻击时,有各种响应用户的方式,例如返回常见的“HTTP 400 bad request”,通过 TCP reset 关闭连接,或在长时间延迟后伪造错误(“tarpit”)以混淆攻击者。所有这些都有助于通过阻止攻击者继续进行代价高昂的攻击来保护服务器。HAProxy 还提供了一些更高级的选项来防止意外数据泄露和会话交叉。它不仅可以记录可疑的服务器响应,还可以记录并选择性地阻止可能影响给定访问者机密性的响应。一个例子是出现在可缓存响应中的可缓存 cookie,这可能导致中间缓存将其提供给另一个访问者,从而导致意外的会话共享。
HAProxy 设计用于在常规生产环境中保持极其稳定和安全。它提供为一个单一的可执行文件,无需任何安装过程。多个版本可以轻松共存,这意味着可以通过重要性顺序逐步升级实例,而不是一次性迁移所有实例(并推荐这样做)。配置文件易于版本控制。配置文件检查在离线进行,因此不需要重新启动可能失败的服务。在配置文件检查期间,可以检测到许多高级错误(例如,一个规则隐藏了另一个规则,或者粘性将不起作用),并提供详细的警告和配置提示来修复它们。配置文件的向后兼容性非常长,版本 1.5 仍然完全支持 13 年前编写的版本 1.1 的配置,而 1.6 只删除了几乎不使用的、可以有其他替代方法的过时关键字。配置和软件升级机制平滑且非破坏性,因为它允许新旧进程在系统上共存,每个进程处理自己的连接。系统状态、构建选项和库兼容性在启动时报告。一些高级功能允许应用程序管理员平滑地停止服务器,检测到它不再有任何活动时,然后将其脱机,停止它,升级它,并确保在升级期间不接收任何流量,然后通过正常路径对其进行测试,而无需将其公开给公众,所有这些操作都无需触及 HAProxy。这确保了即使是复杂的生产操作也可以在工作时间内完成,并提供所有技术资源。该过程尽量节省资源,使用内存池来节省分配时间并限制内存碎片,尽早释放负载缓冲区,并支持强制执行严格的内存限制,超过该限制连接必须等待缓冲区可用而不是分配更多内存。此系统有助于在某些严格的环境中保证内存使用。提供了一个命令行界面(CLI),作为 UNIX 或 TCP 套接字,用于执行许多操作和检索故障排除信息。在此套接字上执行的所有操作都不需要更改配置,因此它主要用于临时更改。使用此界面,可以更改服务器的地址、权重和状态,咨询统计信息并清除计数器,转储和清除粘性表(可能按键条件选择性地),转储和杀死客户端和服务器连接,使用详细的错误原因和位置分析转储捕获的错误,转储、添加和删除 ACL 和映射条目,更新 TLS 共享密钥,在运行时将连接限制和速率限制应用于任意前端(在共享托管环境中很有用),并禁用特定前端以释放监听端口(在白天操作被禁止但仍需要修复时很有用)。允许在运行时更新证书及其配置,以及启用和查询流量的每个处理步骤的跟踪。对于必须使用 SNMP 的环境,至少存在两个代理,一个由 HAProxy 源代码提供,依赖于 Net-SNMP Perl 模块。另一个由商业软件包提供,不需要 Perl。两者在覆盖范围方面大致相当。通常建议在部署 HAProxy 的机器上安装 4 个实用程序: - socat(用于连接到 CLI,尽管某些 netcat 的分支也可以在一定程度上做到); - 来自最新 HAProxy 版本的 halog:这是日志分析工具,它以极快的速度(每秒 1 到 2 GB)解析原生的 TCP 和 HTTP 日志,并提取有用的信息和统计信息,例如每个 URL 的请求、每个源地址的请求、按响应时间或错误率排序的 URL、终止代码等。它被设计用于部署在生产服务器上以帮助对实时问题进行故障排除,因此它必须在那里随时可用; - tcpdump:强烈建议使用 tcpdump 来捕获日志中显示的问题所需的网络跟踪。应用程序和 haproxy 的分析之间会有分歧的时候,网络跟踪是确定谁对谁错的唯一方法。通过 tcpdump 检测到网络堆栈和虚拟机管理程序中的错误也相当常见; - strace:它是 tcpdump 的伴侣。它将报告 HAProxy 实际看到的内容,并将帮助区分操作系统负责的问题和 HAProxy 负责的问题。当怀疑 HAProxy 中存在错误时,通常会要求使用 strace;
根据 HAProxy 部署的操作系统,可能存在或需要某些额外功能。虽然它支持许多平台,但 HAProxy 主要在 Linux 上开发,这解释了为什么某些功能仅在该平台上可用。透明绑定和连接功能、绑定连接到特定网络接口的支持,以及将多个进程绑定到同一 IP 地址和端口的能力仅在 Linux 和 BSD 系统上可用,尽管只有 Linux 在内核端对可用进程之间的入站请求进行负载均衡。在 Linux 上,还有一些额外的功能和优化,包括对网络命名空间(也称为“容器”)的支持,允许 HAProxy 作为所有容器之间的网关,能够设置 MSS、Netfilter 标记和客户端连接上的 IP TOS 字段,支持监听端的 TCP FastOpen,TCP 用户超时以在内核检测到客户端在配置的超时之前消失时快速终止连接,TCP 拼接以让内核在连接的两侧之间转发数据,从而避免多次内存复制,能够启用“defer-accept”绑定选项,直到内核缓冲区中有数据可用时才通知传入连接,以及能够在确认连接的 ACK(有时称为“piggy-back”)中发送请求,这通过“tcp-smart-connect”选项启用。在 Linux 上,HAProxy 还非常注意操纵 TCP 延迟 ACK,以在网络上节省尽可能多的数据包。一些系统具有不可靠的时钟,会来回跳动。这曾经发生在某些 NUMA 系统上,多个处理器看不到完全相同的当日时间,最近在虚拟化环境中变得更加普遍,虚拟时钟与实际时钟无关,导致巨大的时间跳跃(有时最多观察到 30 秒)。这导致了许多与超时强制执行相关的问题。由于这些系统的缺陷,HAProxy 维护自己的单调时钟,该时钟基于系统时钟,但会测量和补偿漂移。这确保即使在系统时钟非常差的情况下,计时器也能保持合理的准确性,并且超时继续工作。请注意,此问题会影响在该系统上运行的所有软件,而不是 HAProxy 特有的。常见影响是虚假的超时或应用程序冻结。因此,如果在此系统上检测到此行为,则必须修复它,而不管 HAProxy 对其进行自我保护的事实。在 Linux 上,新的启动进程可以与前一个进程通信以重用其监听文件描述符,从而在进程替换期间监听套接字永远不会中断。
HAProxy 可以通过 Lua 嵌入式语言进行构建,这为复杂的请求或响应操作、路由决策、统计处理等领域开辟了广泛的新可能性。使用 Lua 甚至可以建立到其他服务器的并行连接以交换信息。这样,例如,开发身份验证系统就成为可能(尽管很复杂)。有关如何使用 Lua 的更多信息,请参阅文件“doc/lua-api/index.rst”中的文档。
管理员可以在任何时候通过 CLI 连接并启用各种内部子系统的跟踪。默认提供不同级别的详细信息,因此实际上可以检索从每条请求一行到每条请求 500 行的内容。过滤器以及自动捕获开/关/暂停机制可用,因此实际上可以等待某个特定事件并详细观察它。这对于诊断故障服务器和客户端的协议违规或拒绝服务攻击非常方便。
典型的 CPU 使用率数据显示,在 TCP 或 HTTP 关闭模式下,HAProxy 占用了 15% 的处理时间,而操作系统内核占用了 85%;在 HTTP 长连接模式下,HAProxy 占用了约 30%,而操作系统内核占用了约 70%。这意味着操作系统及其调优对整体性能有很大的影响。用户的使用场景差异很大,有的侧重带宽,有的侧重请求速率,有的侧重连接并发,有的侧重 SSL 性能。本节旨在提供一些帮助完成这项任务的要素。重要的是要记住,每项操作都有成本,因此每项单独的操作都会在其之上增加开销,在某些情况下可能微不足道,而在其他情况下可能占主导地位。在处理连接的请求时,我们可以说:- 转发数据比解析请求或响应头部的成本低;- 解析请求或响应头部的成本低于建立然后关闭服务器连接的成本;- 建立和关闭连接的成本低于 TLS 恢复操作的成本;- TLS 恢复操作的成本低于带有密钥计算的完整 TLS 握手的成本;- 空闲连接的 CPU 成本低于持有数据的连接的成本;- TLS 上下文比带有数据的连接消耗更多的内存。因此,在实际操作中,处理有效负载字节比处理头部字节成本更低,因此使用大对象(每单位体积请求数少)比使用小对象(每单位体积请求数多)更容易实现高网络带宽。这解释了为什么最大带宽总是使用大对象进行测量,而请求速率或连接速率是使用小对象进行测量的。一些操作可以在分布在多个 CPU 上的多个进程中很好地扩展,而其他操作则不然。网络带宽的扩展性不是很好,因为对于大对象来说,CPU 很少是瓶颈,主要是网络带宽和到达网络接口的数据总线。由于在处理本地端口表时系统中的一些锁,连接速率在多个处理器上的扩展性不是很好。持久连接上的请求速率扩展性非常好,因为它不涉及太多内存或网络带宽,也不需要访问锁定的结构。TLS 密钥计算的扩展性非常好,因为它完全是 CPU 绑定的。TLS 恢复的扩展性适中,但在约 4 个进程后会达到极限,因为访问共享表的开销抵消了从更多核心获得的微小收益。一个经过良好调优的系统可以预期的性能数据在以下范围内。重要的是将它们视为数量级,并期望在任何方向上出现显著差异,具体取决于处理器、中断设置、内存类型、网络接口类型、操作系统调优等。以下数据是在一台 3.7 GHz 的 Core i7 处理器上测得的,配备双端口 10 Gbps 网卡,运行 Linux 内核 3.10、HAProxy 1.6 和 OpenSSL 1.0.2。HAProxy 以单个进程运行在单个专用 CPU 核心上,另外两个核心专用于网络中断:- 明文模式下,256 kB 或更大的对象的最大网络带宽为 20 Gbps,41 kB 或更大的对象为 10 Gbps;- 使用 AES256-GCM 密码的大对象 TLS 流量为 4.6 Gbps;- 每秒 83,000 个 TCP 连接(从客户端到服务器);- 每秒 82,000 个 HTTP 连接(从客户端到服务器);- 服务器关闭模式下每秒 97,000 个 HTTP 请求(与客户端保持长连接,与服务器关闭);- 端到端长连接模式下每秒 243,000 个 HTTP 请求;- 每秒 300,000 个过滤的 TCP 连接(反 DDoS);- 在长连接上的每秒 160,000 个 HTTPS 请求(使用持久 TLS 连接);- 每秒 13,100 个 HTTPS 请求(使用 TLS 恢复的连接);- 每秒 1,300 个 HTTPS 连接(使用 RSA2048 重协商的 TLS 连接);- 每 GB RAM 可支持约 20,000 个并发饱和连接,包括系统缓冲区所需的内存;通过仔细调优可以做得更好,但这个结果很容易达到。- 每 GB RAM 可支持约 8,000 个并发 TLS 连接(仅客户端),包括系统缓冲区所需的内存;- 每 GB RAM 可支持约 5,000 个并发端到端 TLS 连接(双向),包括系统缓冲区所需的内存;一项更近期的基准测试,在 AWS 的 64 核 ARM Graviton2 处理器上运行启用了多线程的 HAProxy 2.4,达到了每秒 200 万个 HTTPS 请求,响应时间低于毫秒,以及 100 Gbps 的流量:https://www.haproxy.com/blog/haproxy-forwards-over-2-million-http-requests-per-second-on-a-single-aws-arm-instance/ 因此,一个需要牢记的经验法则是,请求速率在 TLS 长连接和 TLS 恢复之间以及 TLS 恢复和 TLS 重协商之间会降低 10 倍,而在 HTTP 长连接和 HTTP 关闭之间仅降低 3 倍。另一个经验法则是,支持 AES 指令的高频核心每核心可以处理约 20 Gbps 的 AES-GCM。另一个经验法则是,在同一服务器上,HAProxy 将能够饱和:- 约 5-10 个静态文件服务器或缓存代理;- 约 100 个防病毒代理;- 约 100-1000 个应用程序服务器,具体取决于所使用的技术。
HAProxy 是一个开源项目,受 GPLv2 许可证保护,这意味着任何人都可以重新分发它,前提是应要求提供源代码访问权限,特别是如果进行了任何修改。HAProxy 以一个名为“master”或“mainline”的主开发分支进行演进,一旦代码被认为稳定,就会从中派生出新的分支。许多网站出于自愿原则在生产环境中运行一些开发分支,无论是为了参与项目还是因为它们需要最新的功能,它们的反馈对于修复 bug 和判断正在开发中的版本的整体质量和稳定性非常有价值。当代码足够稳定时创建的新分支构成了稳定版本,并且通常会维护数年,因此即使您不在最新版本上,也没有必要急于迁移到更新的分支。一旦发布了稳定分支,它可能只接收 bug 修复,并且在用户生活更轻松的情况下,非常罕见地会进行小的功能更新。进入稳定分支的所有修复都必然来自 master 分支。这保证了升级后不会丢失任何修复。因此,如果您修复了一个 bug,请针对 master 分支打补丁,而不是稳定分支。您甚至可能会发现它已经被修复了。这个过程还确保了稳定分支中的回归非常罕见,因此没有任何借口不升级到当前分支的最新版本。分支使用两个由点分隔的数字进行编号,例如“1.6”。自 1.9 版本以来,第二个数字为奇数的分支主要侧重于敏感的技术更新,更适合高级用户,因为它们可能比其他分支更容易触发 bug。它们仅维护约一年,并且不得在无法紧急回滚的地方部署。完整的版本包括一个或两个子版本号,表示修复级别。例如,版本 1.5.14 是在 1.5.0 版本发布后,1.5 分支中的第 14 个修复版本。它包含 126 个针对单个 bug 的修复,24 个文档更新,以及 75 个其他向后移植的补丁,其中大部分是为了修复上述 126 个 bug。为了保证同一分支内的升级始终是安全的,已有的功能在稳定分支中永远不会被修改或删除。HAProxy 可从多个来源获得,发布节奏不同:- 官方社区网站:https://haproxy.cn/:该网站提供最新开发版本、所有稳定版本以及每个分支的夜间快照的源代码。发布周期不快,稳定版本之间或开发快照之间有几个月的时间。旧版本仍在那里支持。一切都以源代码形式提供,因此任何来自那里的内容都需要重新构建和/或重新打包;- GitHub:https://github.com/haproxy/haproxy/:这是仅用于开发分支的镜像,它集成了问题跟踪器、持续集成和代码覆盖率工具。这仅供贡献者使用;- 许多操作系统,如 Linux 发行版和 BSD 端口。这些系统通常提供长期维护的版本,这些版本不一定包含官方版本的所有修复,但至少包含关键修复程序。对于大多数不寻求高级配置的用户来说,这通常是一个不错的选择,他们只想轻松地进行更新;- 来自 http://www.haproxy.com/ 的商业版本:这些是为各种操作系统构建的、经过支持的专业软件包,或以设备形式提供,基于最新的稳定版本,并包含从下一个版本向后移植的许多功能,因为有很强的需求。对于寻求最新功能、稳定分支的可靠性、最快的 bug 修复响应时间,或仅仅是开源产品之上的支持合同的用户来说,这是最佳选择;为了确保您使用的版本是您分支中的最新版本,您需要按以下方式进行:- 验证您正在运行哪个 HAProxy 可执行文件:某些系统默认提供它,管理员会在系统上的其他位置安装自己的版本,因此重要的是在启动脚本中验证使用的是哪个;- 确定您的 HAProxy 版本来自哪个来源。为此,通常输入“haproxy -v”就足够了。开发版本会像这样显示,在分支号后带有“dev”字样:HAProxy version 2.4-dev18-a5357c-137 2021/05/09 - https://haproxy.cn/稳定版本会像这样显示,以及操作系统供应商提供的未修改的稳定版本:HAProxy version 1.5.14 2015/07/02夜间快照版本会像这样显示,版本后有一个十六进制序列,日期是快照日期而不是发布日期:HAProxy version 1.5.14-e4766ba 2015/07/29任何其他格式可能表示一个具有自己补丁集的系统特定软件包。例如,HAProxy Enterprise 版本将以以下格式显示(<branch>-<latest commit>-<revision>):HAProxy version 1.5.0-994126-357 2015/07/02请注意,历史上 2.4 版本之前的版本习惯上在“HA”和“Proxy”之间使用连字符报告进程名称,包括上述版本,它们已被调整为仅显示正确格式,因此最好忽略此单词或在脚本中使用宽松匹配。此外,现代版本会添加一个指向项目主页的 URL。最后,2.1 及更高版本将包含一个“Status”行,指示版本是否可用于生产环境,如果是,直到何时,以及指向受该版本影响的已知 bug 列表的链接。- 对于系统特定软件包,您必须与您的供应商的软件包存储库或更新系统进行核对,以确保您的系统仍受支持,并且仍为您的分支提供修复。对于来自 haproxy.org 的社区版本,只需访问该网站,验证您分支的状态,并将最新版本与您的进行比较,看看您是否使用的是最新版本。如果不是,您可以升级。如果您的分支不再维护,您肯定已经非常落后了,并且将不得不考虑升级到更近期的分支(升级时请仔细阅读 README)。HAProxy 的更新必须根据其来源进行。通常遵循系统供应商的软件包升级方式。如果它是从源代码获取的,请在解压源代码后阅读源代码目录中的 README 文件,并按照您操作系统的说明进行操作。
HAProxy 与下面列出的一些产品集成得相当好,因此即使它们不直接与 HAProxy 相关,也在此处提及。
Apache 是事实上的标准 HTTP 服务器。它是一个非常完整且模块化的项目,支持文件服务和动态内容。它可以作为一些应用程序服务器的前端。它甚至可以代理请求和缓存响应。在所有这些用例中,通常需要一个前端负载均衡器。Apache 可以以各种模式工作,有些模式比其他模式更重。某些模块仍然需要更重的预派发模型,这将阻止 Apache 在大量连接下良好扩展。在这种情况下,HAProxy 可以提供巨大的帮助,通过将每个服务器的连接限制强制设置为安全值,并将显著加快服务器速度并保留其资源,以便应用程序更好地使用这些资源。Apache 可以使用“mod_rpaf”扩展从 X-Forwarded-For 头部提取客户端的地址。当在 HAProxy 的配置中指定“option forwardfor”时,HAProxy 会自动填充此头部。当暴露于互联网时,HAProxy 也可以为 Apache 提供良好的保护,从而更好地抵抗大量类型的 DoS 攻击。
NGINX 是事实上的第二标准 HTTP 服务器。与 Apache 类似,它涵盖了广泛的功能。NGINX 基于与 HAProxy 类似的模型构建,因此它在处理数万个并发连接方面没有问题。当用作某些应用程序(例如使用包含的 PHP FPM)的网关时,设置一些前端连接限制以减少 PHP 应用程序的负载通常是有益的。HAProxy 在这里显然很有用,既作为常规负载均衡器,也作为流量调节器,通过疏通 PHP 来加速它。此外,由于两个产品都因其事件驱动的架构而消耗很少的 CPU,因此通常很容易将它们安装在同一系统上。NGINX 实现 HAProxy 的 PROXY 协议,因此 HAProxy 可以轻松地将客户端连接信息传递给 NGINX,从而使应用程序获得所有相关信息。一些基准测试还表明,对于大型静态文件服务,在 NGINX 前面使用 HAProxy 实现一致性哈希可能是有益的,通过优化操作系统缓存的命中率,基本上乘以服务器节点的数量。
Varnish 是一个智能的缓存反向代理,可能被描述为一个 Web 应用程序加速器。Varnish 不实现 SSL/TLS,并希望将其所有 CPU 周期都用于其最擅长的领域。Varnish 还实现了 HAProxy 的 PROXY 协议,以便 HAProxy 可以非常轻松地部署在 Varnish 前面,作为 SSL 卸载器以及负载均衡器,并向其传递所有相关的客户端信息。此外,Varnish 自然支持在服务器提供压缩对象时从缓存中解压缩,但不压缩。然后,HAProxy 可以用于压缩后端服务器未实现压缩时的传出数据,尽管除非流量很低,否则在负载均衡器上进行压缩通常不是一个好主意。当跨多个节点构建大型缓存集群时,HAProxy 可以利用一致的 URL 哈希来智能地将负载分配到缓存节点,并避免缓存重复,从而使总缓存大小等于所有缓存节点的总和。此外,在 HAProxy 上缓存非常小的、傻瓜式的对象并保持很短的时间有时可以节省网络往返,并减少 HAProxy 和 Varnish 节点上的 CPU 负载。只有当 Varnish 上不对此类对象进行任何处理时,才可能实现这一点(这通常被称为“favicon 缓存”的概念,通过它可以避免相当大比例的无用下游请求)。但是,不要在其他任何缓存前面长时间(超过几秒钟)启用 HAProxy 缓存,这会显著地使故障排除复杂化,而没有提供真正显著的节省。
Linux Virtual Server (LVS 或 IPVS) 是包含在 Linux 内核中的第 4 层负载均衡器。它在数据包级别工作,并处理 TCP 和 UDP。在大多数情况下,它更像是补充而不是替代,因为它根本不具备第 7 层知识。Pound 是另一个知名的负载均衡器。它更简单,功能比 HAProxy 少得多,但对于许多非常基础的设置,两者都可以使用。它的作者始终将代码可审计性放在首位,并希望保持功能集较少。它的基于线程的架构在连接数很高时扩展性较差,但它是一个优秀的产品。Pen 是一个相当轻量级的负载均衡器。它支持 SSL,通过维护一个固定大小的客户端 IP 地址表来保持持久性。它支持面向数据包的模式,允许它在一定程度上支持直接服务器返回和 UDP。它适用于小负载(持久性表只有 2048 个条目)。NGINX 在一定程度上可以进行负载均衡,尽管这显然不是它的主要功能。生产流量用于检测服务器故障,负载均衡算法更有限,粘性非常有限。但在某些已经存在的简单部署场景中,它可能是有意义的。好处是,由于它与 HAProxy 集成得非常好,因此当达到其极限时,添加 HAProxy 是没有问题的。Varnish 也对后端服务器进行一些负载均衡,并支持实际的健康检查。它不实现粘性,因此与 NGINX 类似,只要不需要粘性,这就可以足够开始。同样,由于 HAProxy 和 Varnish 集成得非常好,因此可以轻松地将其添加到混合中以补充功能集。
如果您想就任何事情联系开发者或任何社区成员,通常最好的方式是通过邮件列表,将您的消息发送到 haproxy@formilux.org。请注意,此列表是公开的,其存档也是公开的,因此您应避免披露敏感信息。那里有成千上万不同经验水平的用户,即使是最复杂的问题通常也能相对快速地找到最佳答案。也欢迎提出建议。对于在电子邮件方面遇到困难的用户,可以在 http://discourse.haproxy.org/ 上使用 Discourse 平台。但是请记住,在那里阅读问题的人较少,而且大多数问题由一个非常小的团队处理。无论如何,请耐心等待并尊重那些利用业余时间帮助他人的人。如果您认为您发现了 bug 但不确定,最好在邮件列表中报告。如果您非常确信自己发现了 bug,您的版本在其分支中是最新的,并且您已经拥有 GitHub 账户,请随时直接前往 https://github.com/haproxy/haproxy/ 并以尽可能详细的信息提交一个 issue。再次声明,这是公开的,因此请小心不要发布您以后可能后悔的信息。由于问题跟踪器呈现为一个很长的线程,请避免粘贴非常长的转储(几百行或更多),而是将它们作为附件。如果您发现了绝对确信可以被认为是关键安全问题,如果公开讨论会给许多用户带来严重麻烦,那么您可以将其与复现器一起发送到 security@haproxy.org。一小群受信任的开发人员将收到它,并能够提出修复方案。我们通常不使用禁运,一旦修复可用,就会合并。在某些罕见情况下,可能会发生与软件供应商协调发布的事件。请注意,此过程通常会扰乱每个人的工作,并且匆忙发布的版本有时会引入新 bug,因此最好避免,除非绝对必要;因此,对于无缘无故引起额外负担的报告,通常很少考虑,而看到您的工作得到认可的最佳方式通常是提供一个可行的修复程序,该程序将出现在更改日志中。