入门指南

版本 3.3.0


本文档不提供任何配置方面的帮助或提示,但它解释了在哪里可以找到相关文档。下面的概述旨在帮助您按名称搜索章节并在文档中导航。致文档贡献者:本文档的格式为每行 80 列,缩进使用偶数个空格,不使用制表符。请严格遵守这些规则,以便在任何地方都能轻松打印。如果您添加章节,请更新下面的概述以便于搜索。
1. 可用文档

2.

负载均衡和负载均衡器的快速入门

3.

HAProxy 简介
3.1.
3.2.
3.3.
3.3.1.
3.3.2.
SSL
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。
负载均衡通过聚合多个组件来实现超越单个组件能力的总处理能力,整个过程无需最终用户干预,并且是可扩展的。这使得在单个组件执行一次操作的时间内,可以同时执行更多的操作。然而,单个操作仍然在单个组件上一次执行,其速度不会比没有负载均衡时更快。它总是需要至少与可用组件数量一样多的操作和一个高效的负载均衡机制,才能充分利用所有组件并从负载均衡中受益。一个很好的例子是高速公路上的车道数量,它允许同样多的汽车在同一时间段内通过,而不会增加它们各自的速度。 负载均衡的例子: - 多处理器系统中的进程调度 - 链路负载均衡(例如 EtherChannel, Bonding) - IP 地址负载均衡(例如 ECMP, DNS 轮询) - 服务器负载均衡(通过负载均衡器) 执行负载均衡操作的机制或组件称为负载均衡器。在 Web 环境中,这些组件被称为“网络负载均衡器”,更普遍地称为“负载均衡器”,因为这是迄今为止最知名的负载均衡案例。 负载均衡器可以作用于: - 链路层:这称为链路负载均衡,包括选择将数据包发送到哪个网络链路; - 网络层:这称为网络负载均衡,包括选择一系列数据包将遵循的路由; - 服务器层:这称为服务器负载均衡,包括决定哪个服务器将处理连接或请求。 存在两种截然不同的技术来满足不同的需求,尽管有一些重叠。在每种情况下,重要的是要记住,负载均衡在于将流量从其自然流向中转移出来,这样做总是需要最基本的谨慎,以维持所有路由决策之间所需的一致性。 第一种技术作用于数据包级别,或多或少地单独处理数据包。输入和输出数据包之间存在一对一的关系,因此可以使用常规的网络嗅探器在负载均衡器的两侧跟踪流量。这项技术可以非常便宜且速度极快。它通常在硬件(ASIC)中实现,以达到线速,例如执行 ECMP 的交换机。通常是无状态的,但也可以是状态化的(考虑数据包所属的会话,称为 L4-LB 或 L4),如果数据包未被修改,可能支持 DSR(直接服务器返回,无需再次通过 LB),但几乎不提供内容感知。这项技术非常适合网络级负载均衡,尽管它有时也用于高速下的非常基本的服务器负载均衡。 第二种技术作用于会话内容。它要求将输入流重组并作为一个整体进行处理。内容可能会被修改,输出流被分段成新的数据包。因此,它通常由代理执行,通常被称为第 7 层负载均衡器或 L7。这意味着两侧有两个独立的连接,并且输入和输出数据包的大小或数量之间没有关系。客户端和服务器不需要使用相同的协议(例如 IPv4 vs IPv6,明文 vs SSL)。操作总是状态化的,并且返回流量必须通过负载均衡器。额外的处理带来了成本,因此并不总是能达到线速,尤其是在处理小数据包时。另一方面,它提供了广泛的可能性,并且通常由纯软件实现,即使嵌入到硬件设备中。这项技术非常适合服务器负载均衡。 基于数据包的负载均衡器通常以直通模式部署,因此它们安装在流量的正常路径上,并根据配置转移流量。返回流量不一定通过负载均衡器。可能会对网络目标地址进行一些修改,以便将流量引导到正确的目的地。在这种情况下,返回流量必须通过负载均衡器。如果路由无法实现这一点,负载均衡器也可能用自己的地址替换数据包的源地址,以强制返回流量通过它。 基于代理的负载均衡器作为具有自己 IP 地址和端口的服务器部署,无需改变架构。有时这需要对应用程序进行一些调整,以便将客户端正确引导到负载均衡器的 IP 地址,而不是直接引导到服务器的 IP 地址。一些负载均衡器可能需要调整某些服务器的响应以实现这一点(例如,HTTP 重定向中使用的 HTTP Location 头部字段)。 一些基于代理的负载均衡器可能会拦截发往非其自有地址的流量,并在连接到服务器时伪造客户端的地址。这使它们可以像常规路由器或防火墙一样以直通模式部署,非常类似于基于数据包的负载均衡器。这对于结合了数据包模式和代理模式的产品尤其受欢迎。在这种情况下,DSR 显然仍然不可能,返回流量仍然必须路由回负载均衡器。 一个非常可扩展的分层方法是:一个前端路由器接收来自多个负载均衡链路的流量,并使用 ECMP 将此流量分发到第一层的多个状态化基于数据包的负载均衡器(L4)。这些 L4 负载均衡器再将流量传递给更多数量的基于代理的负载均衡器(L7),后者必须解析内容以决定最终哪个服务器将接收流量。 组件和可能的流量路径数量增加了故障的风险;在非常大的环境中,永久性地有一些故障组件正在被修复或更换甚至是很正常的。在不了解整个堆栈健康状况的情况下进行负载均衡会显著降低可用性。因此,任何正常的负载均衡器都会验证它打算将流量传送到的组件是否仍然存活和可达,并停止向故障组件传送流量。 这可以通过多种方法实现。最常见的一种是定期发送探测以确保组件仍然可以操作。这些探测被称为“健康检查”。它们必须能代表要处理的故障类型。例如,基于 ping 的检查无法检测到 Web 服务器已经崩溃并且不再监听端口,而连接到该端口可以验证这一点,更高级的请求甚至可以验证服务器仍然在工作,并且它所依赖的数据库仍然可访问。健康检查通常涉及几次重试,以应对偶然的测量错误。检查之间的周期必须足够短,以确保故障组件在发生错误后不会被使用太长时间。 其他方法包括对发送到目的地的生产流量进行抽样,以观察其是否被正确处理,并排除返回不当响应的组件。然而,这需要牺牲一部分生产流量,这并非总是可以接受的。 这两种机制的结合提供了两全其美的效果,两者都用于检测故障,而只有健康检查用于检测故障的结束。 最后一种方法涉及集中报告:一个中央监控代理定期向所有负载均衡器更新所有组件的状态。这为所有组件提供了基础设施的全局视图,尽管有时准确性或响应性较低。它最适合拥有许多负载均衡器和许多服务器的环境。 第 7 层负载均衡器还面临另一个挑战,即粘性(stickiness)或持久性(persistence)。其原理是,它们通常必须将来自同一来源(例如最终用户)的多个后续请求或连接导向到同一个目标。最著名的例子是在线商店的购物车。如果每次点击都导致一个新的连接,用户必须总是被发送到持有他购物车的服务器。内容感知使得更容易在请求中发现一些元素来识别要将请求发送到的服务器,但这并不总是足够的。例如,如果使用源地址作为选择服务器的密钥,可以决定使用基于哈希的算法,并且根据地址除以可用服务器数量的结果,将给定的 IP 地址总是发送到同一台服务器。但是如果一台服务器发生故障,结果就会改变,所有用户突然被发送到不同的服务器并丢失他们的购物车。解决这个问题的办法是记住选择的目标,这样每次看到同一个访问者时,无论可用服务器数量如何,他都会被导向到同一个服务器。该信息可以存储在负载均衡器的内存中,在这种情况下,如果它不是唯一的负载均衡器,可能需要复制到其他负载-均衡器,或者可以使用各种方法存储在客户端的内存中,只要客户端能够在每次请求时都带回此信息(Cookie 插入、重定向到子域等)。这种机制提供了额外的好处,即不必依赖不稳定或分布不均的信息(如源 IP 地址)。这实际上是采用第 7 层负载均衡器而不是第 4 层负载均衡器的最强有力的理由。 为了提取诸如 Cookie、主机头字段、URL 或任何其他信息,负载均衡器可能需要解密 SSL/TLS 流量,甚至在将其传递给服务器时可能需要重新加密。这项昂贵的任务解释了为什么在一些高流量的基础设施中,有时可能会有很多负载均衡器。 由于第 7 层负载均衡器可能会对流量执行许多复杂的操作(解密、解析、修改、匹配 Cookie、决定发送到哪个服务器等),它肯定会引起一些麻烦,并且很常会被指责为它仅仅是揭示出来的许多问题的罪魁祸首。通常会发现服务器不稳定并周期性地上下线,或者对于 Web 服务器,它们提供的页面带有一些硬编码的链接,迫使客户端直接连接到某个特定服务器而不经过负载均衡器,或者它们在高负载下响应需要很长时间,导致超时。这就是为什么日志记录是第 7 层负载均衡一个极其重要的方面。一旦报告了问题,重要的是要弄清楚负载均衡器是否做出了错误的决定,如果是,为什么,以便不再发生。
HAProxy 写成“HAProxy”以指代产品,写成“haproxy”以指代可执行程序、软件包或进程。然而,两者通常都用于这两个目的,并且读作 H-A-Proxy。很早以前,“haproxy”代表“high availability proxy”,并且这个名字被写成两个独立的单词,尽管现在它只代表“HAProxy”。

3.1. HAProxy 是什么,不是什么

HAProxy 是: - 一个 TCP 代理:它可以从一个监听套接字接受 TCP 连接,连接到一个服务器,并将这些套接字连接在一起,允许流量双向流动;IPv4、IPv6 甚至 UNIX 套接字在两侧都受支持,因此这可以提供一种在不同地址族之间轻松转换地址的方法。 - 一个 HTTP 反向代理(在 HTTP 术语中称为“网关”):它将自己呈现为一个服务器,通过在监听的 TCP 套接字上接受的连接接收 HTTP 请求,并使用不同的连接将这些连接的请求传递给服务器。它可以在任何一侧使用 HTTP/1.x 或 HTTP/2 的任意组合,并且在使用 TLS 上的 ALPN 时甚至会自动检测每一侧所使用的协议。 - 一个 SSL 终结者/发起者/卸载器:SSL/TLS 可以在来自客户端的连接上使用,也可以在去往服务器的连接上使用,甚至可以在两个连接上都使用。可以为每个名称(SNI)应用大量设置,并且可以在运行时更新而无需重启。这种设置具有极高的可扩展性,据报道有涉及数十万到数百万个证书的部署。 - 一个 TCP 规整器:由于连接由操作系统在本地终止,两侧之间没有关系,因此异常流量,如无效数据包、标志组合、窗口通告、序列号、不完整的连接(SYN 洪水)等,不会传递到另一侧。这可以保护脆弱的 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 代理,即浏览器用来访问互联网的代理。有许多优秀的开源软件专门用于此任务,例如 Squid。但是,HAProxy 可以安装在此类代理的前面,以提供负载均衡和高可用性。 - 一个数据清洗器:它不会修改请求或响应的主体。 - 一个静态 Web 服务器:在启动期间,它会将自己隔离在一个 chroot jail 中并放弃其权限,因此一旦启动,它将不会执行任何文件系统访问。因此,它不能被用作静态 Web 服务器(但通过 FastCGI 支持动态服务器)。有许多优秀的开源软件可以做到这一点,例如 Apache 或 Nginx,并且可以轻松地将 HAProxy 安装在它们的前面以提供负载均衡、高可用性和加速。 - 一个基于数据包的负载均衡器:它看不到 IP 数据包或 UDP 数据报,不会执行 NAT,更不会执行 DSR。这些是下层协议的任务。一些基于内核的组件,如 IPVS(Linux 虚拟服务器),已经很好地完成了这项工作,并与 HAProxy 完美互补。

3.2. HAProxy 的工作原理

HAProxy 是一个事件驱动的、非阻塞的引擎,它结合了一个非常快速的 I/O 层和一个基于优先级的多线程调度器。由于其设计目标是数据转发,其架构经过优化,以便以最少的操作尽可能快地移动数据。它专注于通过尽可能长时间地将连接固定在同一个 CPU 上来优化 CPU 缓存的效率。因此,它实现了一个分层模型,在每一层都提供旁路机制,确保数据除非需要,否则不会到达更高层级。大部分处理在内核中进行,HAProxy 通过给出一些提示或在猜测某些操作可以在稍后分组时避免这些操作,来尽力帮助内核尽快完成工作。因此,典型数据显示,在 TCP 或 HTTP 关闭模式下,15% 的处理时间花费在 HAProxy 上,而 85% 在内核中;在 HTTP 保持连接(keep-alive)模式下,大约 30% 用于 HAProxy,70% 用于内核。 一个进程可以运行许多代理实例;据报道,单个进程中多达 300,000 个不同代理的配置运行良好。对于超过 99% 的用户来说,单核、单 CPU 的设置绰绰有余,因此,鼓励容器和虚拟机的用户使用他们能获得的绝对最小的镜像,以节省运营成本并简化故障排除。但是,HAProxy 运行的机器绝不能交换(swap),其 CPU 绝不能被人为限制(虚拟机管理程序中的子 CPU 分配),也不应与计算密集型进程共享,这会导致非常高的上下文切换延迟。 线程化允许通过为每个 CPU 核心使用一个线程来利用所有可用的处理能力。这主要用于 SSL 或需要超过 40 Gbps 的数据转发速率时。在这种情况下,避免多个物理 CPU 之间的通信至关重要,这可能导致网络堆栈和 HAProxy 本身的严重瓶颈。虽然对某些人来说可能违反直觉,但当面临一些性能问题时,首先要做的事情通常是减少 HAProxy 运行的 CPU 数量。 HAProxy 只需要 haproxy 可执行文件和一个配置文件即可运行。为了进行日志记录,强烈建议配置好 syslog 守护进程和日志轮转。日志也可以发送到 stdout/stderr,这在容器内部可能很有用。 配置文件在启动前被解析,然后 HAProxy 尝试绑定所有监听套接字,如果任何操作失败,则拒绝启动。过了这一点,它就不会再失败了。这意味着没有运行时故障,如果它同意启动,它将一直工作直到被停止。 一旦 HAProxy 启动,它只做 3 件事: - 处理传入的连接; - 定期检查服务器的状态(称为健康检查); - 与其他 haproxy 节点交换信息。 处理传入连接是迄今为止最复杂的任务,因为它依赖于大量的配置可能性,但可以总结为以下 9 个步骤: - 从属于一个称为“前端(frontend)”的配置实体的监听套接字接受传入连接,该实体引用一个或多个监听地址; - 对这些连接应用前端特定的处理规则,这可能导致阻止它们、修改某些头部,或拦截它们以执行一些内部小程序,如统计页面或 CLI; - 将这些传入连接传递给代表服务器场的另一个配置实体,称为“后端(backend)”,其中包含服务器列表和该服务器场的负载均衡策略; - 对这些连接应用后端特定的处理规则; - 根据负载均衡策略决定将连接转发到哪个服务器; - 对响应数据应用后端特定的处理规则; - 对响应数据应用前端特定的处理规则; - 发出一条日志,详细报告发生了什么; - 在 HTTP 中,循环回到第二步等待新请求,否则关闭连接。 前端和后端有时被认为是半个代理,因为它们只关注端到端连接的一侧;前端只关心客户端,而后端只关心服务器。HAProxy 也支持完整代理,它正是前端和后端的结合体。当需要 HTTP 处理时,配置通常会分为前端和后端,因为它们开启了许多可能性,任何前端都可以将连接传递给任何后端。对于纯 TCP 代理,使用前端和后端很少能带来好处,使用完整代理的配置可能更具可读性。

3.3. 基本功能

本节将列举 HAProxy 实现的许多功能,其中一些是任何现代负载均衡器通常期望的,而另一些则是 HAProxy 架构的直接优势。更高级的功能将在下一节中详细介绍。

3.3.1. 基本功能:代理

代理是指通过两个独立的连接在客户端和服务器之间传输数据的行为。关于代理和连接管理,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

3.3.2. 基本功能:SSL

HAProxy 的 SSL 栈被谷歌工程师认为是功能最丰富的之一 (http://istlsfastyet.com/)。使其功能相当完整的常用特性包括: - 基于 SNI 的多站点托管,对站点数量没有限制,并专注于性能。已知至少有一个部署运行着 50000 个域名及其各自的证书; - 支持通配符证书,减少了对大量证书的需求; - 基于证书的客户端身份验证,可配置未提供有效证书时的失败策略。例如,这允许呈现一个不同的服务器场来重新生成客户端证书; - 后端服务器的身份验证确保后端服务器是真实的,而不是中间人攻击; - 与后端服务器的身份验证让后端服务器知道确实是预期的 haproxy 节点正在连接它; - TLS NPN 和 ALPN 扩展使得可以可靠地卸载 SPDY/HTTP2 连接,并以明文形式将其传递给后端服务器; - OCSP Stapling 通过在客户端请求证书状态时内联提供 OCSP 响应,进一步减少了首页加载时间; - 动态记录大小调整提供了高性能和低延迟,并通过让浏览器在数据包仍在传输时开始获取新对象,显著减少了页面加载时间; - 永久访问所有相关的 SSL/TLS 层信息,用于日志记录、访问控制、报告等。这些元素可以嵌入到 HTTP 头部中,甚至作为 PROXY 协议扩展,以便被卸载的服务器获得如果它自己执行 SSL 终止时会拥有的所有信息。 - 检测、记录和阻止某些已知的攻击,即使是在易受攻击的 SSL 库上,例如影响某些 OpenSSL 版本的“心脏出血”攻击。 - 支持无状态会话恢复(RFC 5077 TLS Ticket 扩展)。TLS ticket 可以从 CLI 更新,这为通过频繁轮换 ticket 来实现完美前向保密提供了手段。

3.3.3. 基本功能:监控

HAProxy 非常注重可用性。因此,它关心服务器的状态,并关心向其他网络组件报告自身的状态: - 服务器的状态通过每个服务器的参数进行持续监控。这确保了到服务器的路径对常规流量是可操作的; - 健康检查支持上下线转换的两种滞后机制,以防止状态抖动; - 检查可以发送到不同的地址/端口/协议:这使得检查一个被认为能代表多个服务的单一服务变得容易,例如一个 HTTP+HTTPS 服务器的 HTTPS 端口。 - 服务器可以跟踪其他服务器并同时下线:这确保了托管多个服务的服务器可以原子性地失败,并且没有人会被发送到一个部分失败的服务器; - 代理可以部署在服务器上以监控负载和健康状况:服务器可能有兴趣独立于健康检查所能看到的情况报告其负载、操作状态、管理状态。通过在服务器上运行一个简单的代理,除了验证整个路径的健康检查之外,还可以考虑服务器对其自身健康状况的看法; - 提供多种检查方法:TCP 连接、HTTP 请求、SMTP hello、SSL hello、LDAP、SQL、Redis、send/expect 脚本,所有方法都支持带/不带 SSL; - 状态变化会在日志和统计页面中通知,并附带失败原因(例如,检测到失败时收到的 HTTP 响应)。在这种变化发生时,也可以向一个可配置的地址发送电子邮件; - 服务器状态也会在统计界面上报告,并可用于做出路由决策,以便根据服务器场的大小和/或健康状况将流量发送到不同的服务器场(例如,一个数据中心间链路的丢失); - HAProxy 可以使用健康检查请求向服务器传递信息,例如它们的名称、权重、服务器场中其他服务器的数量等,以便服务器可以根据这些知识调整其响应和决策(例如,推迟备份以保留更多可用的 CPU); - 服务器可以使用健康检查报告比简单的开/关更详细的状态(例如,我想停止,请停止发送新访客); - HAProxy 本身可以向外部组件(如路由器或其他负载均衡器)报告其状态,从而构建非常完整的多路径和多层基础设施。

3.3.4. 基本功能:高可用性

与任何严肃的负载均衡器一样,HAProxy 非常关心可用性,以确保最佳的全球服务连续性: - 只使用有效的服务器;其他服务器会自动从负载均衡服务器场中移除;但在某些条件下,仍然可以强制使用它们; - 支持优雅关闭,因此可以在不影响任何连接的情况下将服务器从服务器场中移除; - 当活动服务器宕机时,备份服务器会自动启用并替换它们,以便在可能的情况下会话不会丢失。这也允许构建多条路径到达同一台服务器(例如,多个接口); - 当太多服务器宕机时,能够为整个服务器场返回一个全局失败状态。这与监控功能相结合,使得上游组件可以为给定服务选择一个不同的负载均衡节点; - 无状态设计使得构建集群变得容易:通过设计,HAProxy 尽其所能确保最高的服务连续性,而无需存储可能在发生故障时丢失的信息。这确保了接管尽可能无缝; - 与标准的 VRRP 守护进程 keepalived 很好地集成:HAProxy 可以轻松地将自己的状态告知 keepalived,并能很好地处理浮动虚拟 IP 地址。 注意:仅在基于集群的解决方案(Heartbeat, ...)之上使用 IP 冗余协议(VRRP/CARP),因为它们提供最快、最无缝、最可靠的切换。

3.3.5. 基本功能:负载均衡

HAProxy 提供了一套相当完整的负载均衡功能,其中大部分不幸地在许多其他负载均衡产品中不可用: - 支持不少于 10 种负载均衡算法,其中一些适用于输入数据,以提供无限的可能性列表。最常见的有 round-robin(轮询,用于短连接,依次选择每个服务器)、leastconn(最少连接,用于长连接,选择连接数最少的服务器中最近最少使用的那个)、source(源地址哈希,用于 SSL 场或终端服务器场,服务器直接取决于客户端的源地址)、URI(用于 HTTP 缓存,服务器直接取决于 HTTP URI)、hdr(头部哈希,服务器直接取决于特定 HTTP 头部字段的内容)、first(首个可用,用于短暂的虚拟机,所有连接都集中在尽可能小的服务器子集上,以便未使用的服务器可以关闭); - 以上所有算法都支持每个服务器的权重,因此可以在一个服务器场中容纳不同代的服务器,或者将一小部分流量引导到特定的服务器(调试模式、运行下一个版本的软件等); - round-robin、leastconn 和一致性哈希支持动态权重;这允许通过 CLI 或甚至通过在服务器上运行的代理程序动态修改服务器权重; - 每当支持动态权重时,都支持慢启动(slow-start);这允许服务器逐渐接收流量。这对于需要在运行时编译类以及需要在满负荷运行前填充的冷缓存的脆弱应用程序服务器来说是一个重要的功能; - 哈希可以应用于各种元素,如客户端源地址、URL 组件、查询字符串元素、头部字段值、POST 参数、RDP cookie; - 一致性哈希保护服务器场免受在添加或移除服务器时发生的大规模重新分布。这在大型缓存场中非常重要,并允许使用慢启动来重新填充冷缓存; - 许多内部指标,如每个服务器的连接数、每个后端的连接数、后端中可用的连接槽数量等,使得构建非常高级的负载均衡策略成为可能。

3.3.6. 基本功能:粘性(Stickiness)

如果没有粘性(stickiness),应用负载均衡将毫无用处。HAProxy 提供了一套相当全面的可能性来将会话保持在同一个服务器上,即使在各种事件(如服务器添加/删除、宕机/上线周期)中也是如此,并且一些方法被设计为能够抵抗多个负载均衡节点之间的距离,因为它们不需要任何复制: - 如果需要,可以从不同地方单独匹配和学习粘性信息。例如,一个 JSESSIONID cookie 可以在 cookie 中和 URL 中同时匹配。最多可以同时学习 8 个并行来源,并且每个来源都可以指向一个不同的粘性表(stick-table); - 粘性信息可以来自请求或响应中可见的任何内容,包括源地址、TCP 负载的偏移量和长度、HTTP 查询字符串元素、头部字段值、cookie 等。 - 粘性表的内容在多主模式下与被称为“对等节点(peers)”的其他 HAProxy 节点之间进行复制,以及在重载操作期间与新进程进行复制,以便所有负载均衡节点共享相同的信息并做出相同的路由决策,即使客户端的请求分布在多个节点上。 - 常用的元素,如 SSL-ID 或 RDP cookie(用于 TSE 场)可以直接访问以方便操作; - 所有粘性规则都可以通过 ACL 动态地设置条件; - 可以决定不粘附到某些服务器,例如备份服务器,这样当主服务器恢复时,它会自动接管负载。这在多路径环境中经常使用; - 在 HTTP 中,通常首选不学习任何东西,而是操作一个专用于粘性的 cookie。为此,可以检测、重写、插入或为这样的 cookie 添加前缀,让客户端记住分配了哪个服务器; - 服务器可以在注销时决定更改或清除粘性 cookie,以便离开的访问者自动与服务器解绑; - 使用基于 ACL 的规则,也可以选择性地忽略或强制执行粘性,而不管服务器的状态如何;结合高级健康检查,这有助于管理员在向全世界开放之前,验证他们正在安装的服务器是否已启动并正常运行; - 一种创新的机制,用于在 cookie 上设置最大空闲时间和持续时间,确保在永不关闭的设备(智能手机、电视、家用电器)上可以平滑地停止粘性,而无需将其存储在持久存储中; - 多个服务器条目可以共享相同的粘性密钥,以便在多路径环境中当一条路径中断时不会丢失粘性; - 软停止(soft-stop)确保只有带有粘性信息的用户将继续访问他们被分配到的服务器,但没有新用户会去那里。

3.3.7. 基本功能:日志

日志记录对于负载均衡器来说是一个极其重要的功能,首先因为负载均衡器经常被错误地指责为它所揭示的问题的起因,其次因为它位于基础设施中的一个关键点,所有正常和异常的活动都需要在这里进行分析并与其他组件关联。 HAProxy 提供非常详细的日志,具有毫秒级的精度和精确的连接接受时间,可以在防火墙日志中进行搜索(例如,用于 NAT 关联)。默认情况下,TCP 和 HTTP 日志相当详细,包含故障排除所需的一切,例如源 IP 地址和端口、前端、后端、服务器、计时器(请求接收持续时间、队列持续时间、连接建立时间、响应头时间、数据传输时间)、全局进程状态、连接数、队列状态、重试次数、详细的粘性操作和断开原因,以及使用安全输出编码的头部捕获。 然后可以扩展或替换此格式,以包括任何采样数据、变量、捕获,从而获得非常详细的信息。例如,可以记录客户端的累积请求数或访问的不同 URL 的数量。 日志级别可以根据标准 ACL 为每个请求进行调整,因此可以自动静默一些被认为是污染的日志,而当一小部分流量出现某些异常行为时(例如,源地址的 URL 或 HTTP 错误过多)则发出警告。 管理日志也以其自己的级别发出,以通知服务器的丢失或恢复等情况。 每个前端和后端可以使用多个独立的日志输出,这简化了多租户环境。 日志最好通过 UDP 发送,可以是 JSON 编码的,并且在可配置的行长度后被截断以保证交付。但也可以将它们发送到 stdout/stderr 或任何文件描述符,以及一个客户端可以订阅以检索它们的环形缓冲区。

3.3.8. 基本功能:统计

HAProxy 提供一个基于 Web 的统计信息报告界面,具有身份验证、安全级别和范围。因此,可以为每个托管的客户提供他们自己的页面,只显示他们自己的实例。此页面可以位于常规网站的隐藏 URL 部分,这样就不需要打开新端口。此页面还可以报告其他 HAProxy 节点的可用性,以便一目了然地发现一切是否按预期工作。视图是综合性的,并且有很多详细信息可供访问(例如,错误原因、最后访问时间和最后更改时长等),这些信息也可作为 CSV 表格提供,其他工具可以导入该表格来绘制图表。页面可以自我刷新,以便在大显示器上用作监控页面。在管理模式下,该页面还允许更改服务器状态以简化维护操作。还提供了一个 Prometheus 导出器,以便统计信息可以根据部署以不同的格式进行消耗。

3.4. 标准功能

在本节中,列举了一些在 HAProxy 中非常常用但并非在其他负载均衡器上普遍存在的功能。

3.4.1. 标准功能:信息采样和转换

HAProxy 使用一套广泛的“样本获取函数”来支持信息采样。其原理是提取被称为样本的信息片段,以供立即使用。这用于粘性、构建条件、在日志中生成信息或丰富 HTTP 头部。 样本可以从各种来源获取: - 常量:整数、字符串、IP 地址、二进制块; - 进程:日期、环境变量、服务器/前端/后端/进程状态、字节/连接数/速率、队列长度、随机数生成器等; - 变量:会话变量、请求变量、响应变量; - 客户端连接:源和目标地址和端口,以及所有相关的统计计数器; - SSL 客户端会话:协议、版本、算法、密码套件、密钥大小、会话 ID、所有客户端和服务器证书字段、证书序列号、SNI、ALPN、NPN、客户端对某些扩展的支持; - 请求和响应缓冲区内容:任意偏移/长度的负载、数据长度、RDP cookie、SSL hello 类型的解码、TLS SNI 的解码; - HTTP(请求和响应):方法、URI、路径、查询字符串参数、状态码、头部值、位置性头部值、cookie、捕获、身份验证、主体元素; 一个样本然后可以经过一系列被称为“转换器”的运算符,以进行一些转换。转换器消耗一个样本并产生一个新的样本,可能类型完全不同。例如,一个转换器可以用来只返回输入字符串的整数长度,或者可以将一个字符串转换为大写。在最终使用之前,可以对一个样本串联应用任意数量的转换器。 在所有可用的样本转换器中,以下是最常用的: - 算术和逻辑运算符:它们使得可以对输入数据进行高级计算,例如计算比率、百分比或简单地从一个单位转换到另一个单位; - IP 地址掩码在需要按更大的网络对某些地址进行分组时很有用; - 数据表示:URL 解码、base64、十六进制、JSON 字符串、哈希; - 字符串转换:提取固定位置、固定长度的子字符串,提取某些分隔符周围的特定字段,提取某些单词,更改大小写,应用基于正则表达式的替换; - 日期转换:转换为 HTTP 日期格式,本地时间与 UTC 相互转换,添加或删除偏移量; - 在粘性表中查找条目以查找统计信息或分配的服务器; - 从文件中进行基于映射的键值转换(主要用于地理定位)。

3.4.2. 标准功能:映射(Maps)

映射是一种强大的转换器类型,它通过在启动时将一个两列文件加载到内存中,然后从第一列查找每个输入样本,如果找到条目,则返回第二列中的相应模式,否则返回默认值。输出信息也是一个样本,它可以经历其他转换,包括其他映射查找。映射最常用于将客户端的 IP 地址转换为 AS 号或国家代码,因为它们支持网络地址的最长匹配,但它们可以用于各种其他目的。它们的部分优势在于它们可以从 CLI 或通过其他样本的某些操作进行动态更新,从而使它们能够存储和检索后续访问之间的信息。另一个优势来自基于二进制树的索引,这使得它们即使包含数十万个条目也极其快速,从而使地理位置的设置非常便宜且容易。

3.4.3. 标准功能:ACL 和条件

HAProxy 中的大多数操作都可以是条件性的。条件是通过使用逻辑运算符(AND、OR、NOT)组合多个 ACL 来构建的。每个 ACL 都是一系列基于以下元素的测试: - 一个样本获取方法来检索要测试的元素; - 一个可选的转换器系列来转换该元素; - 一个要匹配的模式列表; - 一个匹配方法来指示如何将模式与样本进行比较。 例如,样本可以取自 HTTP 的“Host”头部,然后可以将其转换为小写,然后使用正则表达式匹配方法与多个正则表达式模式进行匹配。 从技术上讲,ACL 与映射(map)建立在相同的核心之上,它们共享完全相同的内部结构、模式匹配方法和性能。唯一真正的区别是,它们只返回“找到”或“未找到”,而不是返回一个样本。 在使用方面,ACL 模式可以在配置文件中内联声明,不需要单独的文件。ACL 可以被命名以便于使用或使配置易于理解。一个命名的 ACL 可以被多次声明,它将依次评估所有定义,直到有一个匹配为止。 提供了大约 13 种不同的模式匹配方法,其中包括 IP 地址掩码、整数范围、子字符串、正则表达式。它们像函数一样工作,就像任何编程语言一样,只有需要的部分才会被评估,所以当一个涉及 OR 的条件已经为真时,接下来的就不会被评估,同样当一个涉及 AND 的条件已经为假时,条件的其余部分也不会被评估。 声明的 ACL 数量没有实际限制,并且提供了一些常用的 ACL。然而,经验表明,使用大量命名 ACL 的设置很难进行故障排除,有时内联使用匿名 ACL 更容易,因为它需要的超出当前分析范围的引用更少。

3.4.4. 标准功能:内容切换

HAProxy 实现了一种称为基于内容的交换(content-based switching)的机制。其原理是,一个连接或请求到达一个前端,然后处理随此请求或连接携带的信息,此时可以编写基于 ACL 的条件,利用这些信息来决定哪个后端将处理该请求。因此,流量根据请求的内容被导向到一个或另一个后端。 最常见的例子是使用 Host 头部和/或路径中的元素(子目录或文件名扩展名)来决定一个 HTTP 请求是针对静态对象还是应用程序,并将静态对象流量路由到一个由快速轻量级服务器组成的后端,而将所有剩余流量路由到一个更复杂的应用程序服务器,从而构成一个细粒度的虚拟主机解决方案。这对于让多种技术共存以形成一个更全面的解决方案非常方便。 内容交换的另一个用例是根据各种标准使用不同的负载均衡算法。缓存可以使用 URI 哈希,而应用程序可以使用轮询。最后但同样重要的是,它允许多个客户通过强制执行每个后端(从而每个客户)的连接限制来使用一小部分公共资源。 内容交换规则的可扩展性非常好,尽管它们的性能可能取决于所用 ACL 的数量和复杂性。但也可以编写动态内容交换规则,其中样本值直接转换为后端名称,而完全不使用 ACL。据报道,这样的配置在生产环境中至少有 300,000 个后端也能正常工作。

3.4.5. 标准功能:粘性表(Stick-tables)

粘性表(Stick-tables)通常用于存储粘性信息,即,保留对某个特定访问者被导向到的服务器的引用。其键就是与该访问者相关联的标识符(其源地址、连接的 SSL ID、HTTP 或 RDP cookie、从 URL 或负载中提取的客户编号等),而存储的值则是服务器的标识符。粘性表可以使用 3 种不同类型的样本作为其键:整数、字符串和地址。一个代理中只能引用一个粘性表,并且在任何地方都用代理名称来指定它。最多可以并行跟踪 8 个键。服务器标识符在请求或响应处理期间,一旦键和服务器都已知时,就会被提交。 粘性表的内容可以以主动-主动模式与其他被称为“对等节点(peers)”的 HAProxy 节点进行复制,也可以在重载操作期间与新进程进行复制,以便所有负载均衡节点共享相同的信息,并在客户端请求分布在多个节点上时做出相同的路由决策。 由于粘性表是根据识别客户端的标识进行索引的,它们也经常被用来存储额外的信息,例如每个客户端的统计数据。额外的统计数据会占用一些额外的空间,需要明确声明。可以存储的统计数据类型包括输入和输出带宽、并发连接数、一段时间内的连接速率和计数、错误的数量和频率、一些特定的标签和计数器等。 为了支持保留这些信息而又不强制粘附到某个给定的服务器,实现了一个特殊的“跟踪(tracking)”功能,允许同时从不同的表中跟踪多达 3 个键,而不管粘性规则如何。 每个存储的统计数据都可以从 CLI 中搜索、转储和清除,并增加了实时故障排除的能力。虽然这种机制可以用来给回头客更高的优先级,或者根据良好或不良行为调整所提供的服务质量,但它主要用于对抗服务滥用,更普遍地是 DDoS,因为它允许构建复杂的模型以高处理速度检测某些不良行为。

3.4.6. 标准功能:格式化字符串

HAProxy 需要在许多地方处理字符串,例如日志、重定向、标头添加等。为了提供最大的灵活性,引入了“格式化字符串”的概念,最初是为了日志记录目的,因此它仍然被称为“log-format”。这些字符串包含转义字符,允许将各种动态数据(包括变量和样本获取表达式)引入字符串,甚至在结果被转换为字符串时调整编码(例如,添加引号)。这提供了一种强大的方法来构建标头内容、响应数据甚至响应模板,或自定义日志行。此外,为了保持大多数常见字符串的构建简单,提供了大约 50 个特殊标签作为日志中常用信息的快捷方式。

3.4.7. 标准功能:HTTP 重写和重定向

在一个从未为此设计的应用程序前安装负载均衡器,如果没有合适的工具,可能是一项具有挑战性的任务。在这种情况下,最常见的操作之一是调整请求和响应头部,使负载均衡器看起来像是源服务器,并修复硬编码的信息。这包括更改请求中的路径(强烈建议不要这样做)、修改 Host 头部字段、修改用于重定向的 Location 响应头部字段、修改 cookie 的路径和域属性等等。 同样,许多服务器在响应中往往会泄露过多的信息,使它们更容易受到针对性攻击。虽然理论上清理这些信息不是负载均衡器的职责,但实际上,它位于基础设施中最好的位置,可以保证所有东西都被清理干净。 类似地,有时负载均衡器将不得不拦截一些请求,并以重定向到新的目标 URL 的方式响应。虽然有些人倾向于混淆重定向和重写,但这是两个完全不同的概念,因为重写使客户端和服务器看到不同的东西(并且对正在访问的页面的位置意见不一),而重定向则要求客户端访问新的 URL,以便它看到与服务器相同的位置。 为了做到这一点,HAProxy 支持各种重写和重定向的可能性,其中包括: - 在请求和响应中基于正则表达式的 URL 和头部重写。正则表达式是修改头部值最常用的工具,因为它们易于操作且易于理解; - 头部也可以根据格式化字符串进行附加、删除或替换,以便在那里传递信息(例如客户端 TLS 算法和密码套件); - HTTP 重定向可以使用任何 3xx 代码重定向到一个相对、绝对或完全动态(格式化字符串)的 URI; - HTTP 重定向还支持一些额外的选项,例如设置或清除特定的 cookie、丢弃查询字符串、如果缺少则附加斜杠等等; - 一个强大的 "return" 指令允许使用动态内容甚至模板文件来自定义响应的每个部分,如状态、头部、主体。 - 所有操作都支持基于 ACL 的条件;

3.4.8. 标准功能:服务器保护

HAProxy 做了很多工作来最大化服务可用性,为此,它付出了巨大的努力来保护服务器免受过载和攻击。 第一个也是最重要的一点是,只有完整且有效的请求才会被转发到服务器。最初的原因是 HAProxy 需要找到它需要的协议元素以与字节流保持同步,第二个原因是在请求完成之前,无法知道是否有某些元素会改变其语义。这样做的直接好处是服务器不会暴露于无效或不完整的请求。这是对 slowloris 攻击非常有效的保护,这种攻击对 HAProxy 几乎没有影响。 另一个重要的点是 HAProxy 包含用于存储请求和响应的缓冲区,通过仅在请求完成后才将其发送到服务器,并从本地网络非常迅速地读取整个响应,服务器端连接的使用时间非常短,这最大限度地保留了服务器资源。对此的一个直接扩展是,HAProxy 可以人为地限制到服务器的并发连接数或未完成的请求数,这保证了即使在流量高峰期间服务器持续以 100% 的容量运行,也绝不会过载。所有超出的请求将简单地排队,等待一个槽位释放后进行处理。最终,这种巨大的资源节省通常能确保服务器响应时间好得多,以至于最终实际上比让服务器过载更快。排队的请求可以被重新分派到其他服务器,甚至当客户端中止时在队列中中止,这也保护了服务器免受“刷新效应”的影响,即访问者在加载缓慢的页面上每次点击“刷新”通常会引发一个新的请求,并使服务器保持在过载状态。慢启动机制还保护了正在重启的服务器免受高流量水平的影响,而它们仍在完成启动或编译一些类。 关于协议级别的保护,可以放宽 HTTP 解析器以接受不符合标准但无害的请求或响应,甚至修复它们。这使得有问题的应用程序在修复开发期间仍可访问。同时,有问题的消息会被完整捕获,并附有详细报告,帮助开发人员在应用程序中发现问题。最危险的协议违规行为会被正确检测、处理和修复。例如,带有两个 Content-Length 头部的格式错误的请求或响应,如果值完全相同则会被修复,如果不同则会被拒绝,因为这会成为一个安全问题。协议检查不仅限于 HTTP,也适用于其他协议,如 TLS 或 RDP。 当检测到协议违规或攻击时,有多种选项可以响应用户,例如返回常见的“HTTP 400 bad request”、用 TCP reset 关闭连接,或者在一个长延迟后伪造一个错误(“tarpit”)来迷惑攻击者。所有这些都有助于保护服务器,通过劝退违规客户端继续进行变得维护成本非常高的攻击。 HAProxy 还提供了一些更高级的选项来防止意外数据泄露和会话交叉。它不仅可以记录可疑的服务器响应,还会记录并可选地阻止可能影响特定访问者机密性的响应。一个这样的例子是,一个可缓存的 cookie 出现在一个可缓存的响应中,这可能导致中间缓存将其传递给另一个访问者,从而导致意外的会话共享。

3.5. 高级功能

3.5.1. 高级功能:管理

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:这是日志分析工具,它能极快地解析原生的 TCP 和 HTTP 日志(每秒 1 到 2 GB),并提取有用的信息和统计数据,例如每个 URL 的请求数、每个源地址的请求数、按响应时间或错误率排序的 URL、终止代码等。它被设计用于部署在生产服务器上,以帮助排查实时问题,因此它必须准备好随时使用; - tcpdump:强烈推荐使用它来捕获排查日志中可见问题所需的网络跟踪。总有某个时刻,应用程序和 haproxy 的分析会产生分歧,而网络跟踪是判断谁对谁错的唯一方法。借助 tcpdump 发现网络堆栈和虚拟机管理程序中的错误也相当常见; - strace:它是 tcpdump 的伙伴。它将报告 HAProxy 真正看到的内容,并将帮助区分操作系统负责的问题和 HAProxy 负责的问题。当怀疑 HAProxy 中存在错误时,通常会要求使用 Strace;

3.5.2. 高级功能:系统特定功能

根据 HAProxy 部署的操作系统,某些额外的功能可能是可用或需要的。虽然它在许多平台上都受支持,但 HAProxy 主要是在 Linux 上开发的,这解释了为什么某些功能仅在该平台上可用。 透明绑定和连接功能、将连接绑定到特定网络接口的支持,以及将多个进程绑定到相同 IP 地址和端口的能力仅在 Linux 和 BSD 系统上可用,尽管只有 Linux 在内核端对可用进程之间的传入请求进行负载均衡。 在 Linux 上,还有许多额外的功能和优化,包括对网络命名空间(也称为“容器”)的支持,允许 HAProxy 成为所有容器之间的网关,设置客户端连接的 MSS、Netfilter 标记和 IP TOS 字段的能力,监听端对 TCP FastOpen 的支持,TCP 用户超时以让内核在检测到客户端在配置的超时之前消失时快速终止连接,TCP 拼接(splicing)以让内核在连接的两侧之间转发数据从而避免多次内存复制,启用“defer-accept”绑定选项以仅在内核缓冲区中有数据可用时才收到传入连接的通知的能力,以及在确认连接时发送请求的能力(有时称为“piggy-back”),这通过“tcp-smart-connect”选项启用。在 Linux 上,HAProxy 还非常注意操纵 TCP 延迟 ACK,以在网络上尽可能多地节省数据包。 一些系统有一个不可靠的时钟,它会在过去和未来之间来回跳跃。这曾经发生在一些 NUMA 系统上,其中多个处理器看到的时间不完全相同,最近在虚拟化环境中变得更为普遍,其中虚拟时钟与真实时钟没有关系,导致巨大的时间跳跃(有时观察到高达 30 秒)。这通常会对超时执行造成很多麻烦。由于这些系统的这个缺陷,HAProxy 维护自己的单调时钟,该时钟基于系统时钟,但会测量和补偿漂移。这确保了即使系统时钟非常糟糕,计时器也能保持合理的准确性,超时也能继续工作。请注意,此问题影响在此类系统上运行的所有软件,并非 HAProxy 特有。常见的影响是虚假超时或应用程序冻结。因此,如果在系统上检测到此行为,则必须修复它,无论 HAProxy 是否会保护自己免受其影响。 在 Linux 上,一个新的启动进程可以与前一个进程通信,以重用其监听文件描述符,从而在进程替换期间监听套接字永远不会中断。

3.5.3. 高级功能:脚本

HAProxy 可以通过 Lua 嵌入式语言进行构建,这为复杂的请求或响应操作、路由决策、统计处理等领域开辟了广泛的新可能性。使用 Lua 甚至可以建立到其他服务器的并行连接以交换信息。这样,例如,开发身份验证系统就成为可能(尽管很复杂)。有关如何使用 Lua 的更多信息,请参阅文件“doc/lua-api/index.rst”中的文档。

3.5.4. 高级功能:跟踪

管理员可以在任何时候通过 CLI 连接并启用各种内部子系统的跟踪。默认提供不同级别的详细信息,因此实际上可以检索从每条请求一行到每条请求 500 行的内容。过滤器以及自动捕获开/关/暂停机制可用,因此实际上可以等待某个特定事件并详细观察它。这对于诊断故障服务器和客户端的协议违规或拒绝服务攻击非常方便。

3.6. 调整

典型的 CPU 使用数据显示,在 TCP 或 HTTP 关闭模式下,15% 的处理时间花费在 HAProxy 上,而 85% 在内核中;在 HTTP 保持连接(keep-alive)模式下,大约 30% 用于 HAProxy,70% 用于内核。这意味着操作系统及其调优对全局性能有很大影响。 用户的使用情况差异很大,一些人关注带宽,另一些人关注请求率,还有一些人关注连接并发性,还有一些人关注 SSL 性能。本节旨在提供一些有助于完成此任务的要素。 重要的是要记住,每个操作都有成本,因此每个单独的操作都会在其他操作之上增加其开销,这在某些情况下可能微不足道,而在其他情况下可能占主导地位。 当处理来自一个连接的请求时,我们可以说: - 转发数据比解析请求或响应头部的成本低; - 解析请求或响应头部比建立然后关闭到服务器的连接的成本低; - 建立和关闭连接比 TLS 会话恢复操作的成本低; - TLS 会话恢复操作比带有密钥计算的完整 TLS 握手的成本低; - 空闲连接比其缓冲区中持有数据的连接消耗的 CPU 少; - TLS 上下文比带有数据的连接消耗更多的内存; 因此,在实践中,处理有效负载字节比处理头部字节更便宜,因此用大对象(每单位体积请求少)比用小对象(每单位体积请求多)更容易实现高网络带宽。这解释了为什么最大带宽总是用大对象来测量,而请求率或连接率是用小对象来测量的。 一些操作在分布于多个 CPU 的多个进程上扩展得很好,而其他操作则扩展得不那么好。网络带宽扩展性不强,因为对于大对象来说,CPU 很少是瓶颈,主要是网络带宽和到达网络接口的数据总线。由于在处理本地端口表时系统中的一些锁,连接率在多个处理器上扩展得不好。持久连接上的请求率扩展得非常好,因为它不涉及太多内存或网络带宽,也不需要访问被锁定的结构。TLS 密钥计算扩展得非常好,因为它是完全受 CPU 限制的。TLS 会话恢复扩展得相当好,但在大约 4 个进程时达到极限,此时访问共享表的开销抵消了从更多处理能力中获得的微小收益。 一个经过良好调优的系统可以预期的性能数字在以下范围内。重要的是将它们视为数量级,并预期会根据处理器、IRQ 设置、内存类型、网络接口类型、操作系统调优等因素在任何方向上发生显著变化。以下数字是在运行于 3.7 GHz 的 Core i7 上获得的,该机器配备了双端口 10 Gbps 网卡,运行 Linux 内核 3.10、HAProxy 1.6 和 OpenSSL 1.0.2。HAProxy 作为单个进程在单个专用 CPU 内核上运行,另外两个内核专用于网络中断: - 对于 256 kB 或更大的对象,明文最大网络带宽为 20 Gbps,对于 41kB 或更大的对象为 10 Gbps; - 使用 AES256-GCM 密码套件的大对象,TLS 流量为 4.6 Gbps; - 每秒 83000 个从客户端到服务器的 TCP 连接; - 每秒 82000 个从客户端到服务器的 HTTP 连接; - 在服务器关闭模式下(客户端保持连接,服务器关闭连接)每秒 97000 个 HTTP 请求; - 在端到端保持连接模式下每秒 243000 个 HTTP 请求; - 每秒 300000 个过滤的 TCP 连接(反 DDoS) - 在持久 TLS 连接上保持连接模式下每秒 160000 个 HTTPS 请求; - 使用 TLS 恢复连接每秒 13100 个 HTTPS 请求; - 使用 RSA2048 重新协商的 TLS 连接每秒 1300 个 HTTPS 连接; - 每 GB 内存大约 20000 个并发饱和连接,包括系统缓冲区所需的内存;通过仔细调优可以做得更好,但这个结果很容易实现。 - 每 GB 内存大约 8000 个并发 TLS 连接(仅客户端侧),包括系统缓冲区所需的内存; - 每 GB 内存大约 5000 个并发端到端 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 恢复之间除以 10,在 TLS 恢复和 TLS 重新协商之间也除以 10,而在 HTTP 保持连接和 HTTP 关闭之间仅除以 3。另一个好的经验法则是,一个带有 AES 指令的高频核心每核可以处理大约 20 Gbps 的 AES-GCM。另一个好的经验法则是,在同一台服务器上,HAProxy 将能够饱和: - 大约 5-10 个静态文件服务器或缓存代理; - 大约 100 个反病毒代理; - 大约 100-1000 个应用程序服务器,具体取决于所使用的技术。

3.7. 如何获取 HAProxy

HAProxy 是一个遵循 GPLv2 许可的开源项目,这意味着任何人都可以在被要求时提供源代码的情况下重新分发它,尤其是在进行了任何修改的情况下。HAProxy 以一个名为“master”或“mainline”的主开发分支的形式发展,一旦代码被认为稳定,就会从中衍生出新的分支。很多网站自愿在生产环境中使用一些开发分支,或是为了参与项目,或是因为他们需要某个前沿功能。他们的反馈对于修复错误以及判断正在开发的版本的整体质量和稳定性非常有价值。当代码足够稳定时创建的新分支构成一个稳定版本,并且通常会维护数年,因此即使您没有使用最新的版本,也无需紧急迁移到更新的分支。一旦一个稳定分支发布,它可能只会接收错误修复,极少数情况下会进行次要功能更新,前提是这能让用户的生活更轻松。所有进入稳定分支的修复都必须来自 master 分支。这保证了在升级后不会丢失任何修复。因此,如果您修复了一个错误,请针对 master 分支制作补丁,而不是稳定分支。您甚至可能会发现这个错误已经被修复了。这个过程还确保了稳定分支中的回归问题极为罕见,所以没有任何理由不升级到您当前分支的最新版本。分支用两个由点分隔的数字编号,例如“1.6”。从 1.9 版本开始,第二个数字为奇数的分支主要关注敏感的技术更新,并且更多地面向高级用户,因为它们比其他分支更容易引发错误。它们只维护大约一年,并且不能部署在紧急情况下无法回滚的地方。一个完整的版本号包括一到两个子版本号,表示修复级别。例如,版本 1.5.14 是在 1.5.0 版本发布后,1.5 分支的第 14 个修复版本。它包含了对 126 个独立错误的修复、24 处文档更新以及 75 个其他向后移植的补丁,其中大部分是为了修复上述 126 个错误。为了保证在同一分支内的升级始终是无害的,稳定分支中已有的功能绝不会被修改或移除。HAProxy 可以从多个来源获取,发布节奏也不同: - 官方社区网站:https://haproxy.cn/ :该网站提供最新开发版本的源代码、所有稳定版本的源代码以及每个分支的每日快照。发布周期不快,稳定版本之间或开发快照之间相隔数月。非常旧的版本在那里仍然得到支持。所有内容都只以源代码形式提供,所以从那里获取的任何东西都需要重新构建和/或重新打包;- GitHub:https://github.com/haproxy/haproxy/ :这只是开发分支的镜像,它与问题跟踪器、持续集成和代码覆盖率工具有集成。这专为贡献者使用;- 许多操作系统,如 Linux 发行版和 BSD ports。这些系统通常提供长期维护的版本,这些版本不一定包含官方版本的所有修复,但至少包含了关键的修复。对于大多数不寻求高级配置、只想让更新变得简单的用户来说,这通常是一个不错的选择;- 来自 http://www.haproxy.com/ 的商业版本:这些是为各种操作系统构建的受支持的专业软件包,或以设备形式提供,它们基于最新的稳定版本,并包含一些从下一个版本向后移植的有强烈需求的功能。对于寻求最新功能、稳定分支的可靠性、最快错误修复响应时间,或者仅仅是在开源产品之上寻求支持合同的用户来说,这是最佳选择;为了确保您使用的版本是您所在分支的最新版本,您需要按以下方式操作: - 确认您正在运行的 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 企业版会以下列格式显示(<分支>-<最新提交>-<修订版>):HAProxy version 1.5.0-994126-357 2015/07/02 请注意,历史上 2.4 之前的版本在报告进程名称时,“HA”和“Proxy”之间会有一个连字符,包括上面为了只显示正确格式而调整过的示例,所以在脚本中最好忽略这个词或使用宽松匹配。此外,现代版本会添加一个链接到项目主页的 URL。最后,2.1 及以上版本会包含一个“Status”行,指示该版本是否可以安全用于生产环境,如果可以,会注明截止日期,以及一个指向影响此版本的已知错误列表的链接。- 对于系统特定的软件包,您必须通过您的供应商的软件包仓库或更新系统来检查,以确保您的系统仍受支持,并且您的分支仍在提供修复。对于来自 haproxy.org 的社区版本,只需访问该网站,验证您分支的状态,并将最新版本与您的版本进行比较,看您是否使用的是最新的。如果不是,您可以升级。如果您的分支不再被维护,那么您肯定已经非常落后了,需要考虑升级到一个更新的分支(这样做时请仔细阅读 README 文件)。HAProxy 的更新方式取决于其来源。通常,它遵循系统供应商升级软件包的方式。如果是从源代码获取的,请在解压源代码后阅读源代码目录中的 README 文件,并按照适用于您操作系统的说明进行操作。
HAProxy 与下面列出的一些产品集成得相当好,因此即使它们不直接与 HAProxy 相关,也在此处提及。

4.1. Apache HTTP 服务器

Apache 是事实上的标准 HTTP 服务器。它是一个非常完整和模块化的项目,既支持文件服务也支持动态内容。它可以作为某些应用服务器的前端。它甚至可以代理请求和缓存响应。在所有这些用例中,通常都需要一个前端负载均衡器。Apache 可以在多种模式下工作,其中一些模式比其他模式更重。某些模块仍然需要较重的预派生(pre-forked)模型,这将妨碍 Apache 在高连接数下进行良好扩展。在这种情况下,HAProxy 可以提供巨大的帮助,它能将每个服务器的连接数限制在一个安全的值,从而显著加快服务器速度并保护其资源,使这些资源能更好地被应用程序使用。Apache 可以使用“mod_rpaf”扩展从 X-Forwarded-For 头部提取客户端地址。当 HAProxy 的配置中指定了“option forwardfor”时,它会自动填充这个头部。当 Apache 暴露于互联网时,HAProxy 还可以为其提供良好的保护,它能更好地抵御多种类型的 DoS 攻击。

4.2. NGINX

NGINX 是第二个事实上的标准 HTTP 服务器。和 Apache 一样,它涵盖了广泛的功能。NGINX 的构建模型与 HAProxy 类似,因此处理数万个并发连接毫无问题。当用作某些应用程序的网关时(例如,使用附带的 PHP FPM),设置一些前端连接限制以减少 PHP 应用程序的负载通常是有益的。HAProxy 在这里显然很有用,既可以作为常规的负载均衡器,也可以作为流量调节器,通过为其减压来加速 PHP。此外,由于这两种产品都采用事件驱动架构,CPU 占用率很低,因此通常很容易将它们都安装在同一系统上。NGINX 实现了 HAProxy 的 PROXY 协议,因此 HAProxy 可以轻松地将客户端的连接信息传递给 NGINX,从而使应用程序获得所有相关信息。一些基准测试还表明,对于大型静态文件服务,在 NGINX 前端使用 HAProxy 实现一致性哈希可能是有益的,因为这可以优化操作系统的缓存命中率,该命中率基本上会乘以服务器节点的数量。

4.3. Varnish

Varnish 是一个智能的缓存反向代理,或许可以最好地描述为 Web 应用加速器。Varnish 不实现 SSL/TLS,并希望将其所有的 CPU 周期都投入到它最擅长的事情上。Varnish 也实现了 HAProxy 的 PROXY 协议,因此 HAProxy 可以非常容易地部署在 Varnish 前面,作为 SSL 卸载器和负载均衡器,并向其传递所有相关的客户端信息。此外,当服务器提供了压缩对象时,Varnish 自然支持从缓存中进行解压,但它自己并不进行压缩。因此,当后端服务器未实现压缩时,可以使用 HAProxy 来压缩传出的数据,尽管在负载均衡器上进行压缩通常不是一个好主意,除非流量很低。当在多个节点上构建大型缓存集群时,HAProxy 可以利用一致性 URL 哈希来智能地将负载分配到缓存节点,并避免缓存重复,从而使总缓存大小等于所有缓存节点之和。此外,在 HAProxy 上对非常小的简单对象进行短时缓存,有时可以节省网络往返次数,并减少 HAProxy 和 Varnish 节点的 CPU 负载。这只有在 Varnish 不对这些对象进行任何处理的情况下才可能实现(这通常被称为“favicon 缓存”的概念,通过这种方式有时可以避免相当大比例的无用下游请求)。但是,不要在任何其他缓存前面长时间(超过几秒钟)启用 HAProxy 缓存,这会显著增加故障排查的复杂性,而不会带来真正显著的节省。

4.4. 替代品

Linux 虚拟服务器(LVS 或 IPVS)是包含在 Linux 内核中的第 4 层负载均衡器。它在数据包级别工作,处理 TCP 和 UDP。在大多数情况下,它更多是一种补充而非替代方案,因为它完全不具备第 7 层的知识。Pound 是另一款知名的负载均衡器。它比 HAProxy 简单得多,功能也少得多,但对于许多非常基本的设置,两者都可以使用。其作者一直将代码可审计性放在首位,并希望保持功能集的精简。其基于线程的架构在高连接数下的扩展性较差,但它是一款不错的产品。Pen 是一款相当轻量的负载均衡器。它支持 SSL,并使用一个固定大小的客户端 IP 地址表来维持持久性。它支持一种面向数据包的模式,使其能够在一定程度上支持直接服务器返回(DSR)和 UDP。它适用于小负载(其持久性表只有 2048 个条目)。NGINX 可以在一定程度上进行负载均衡,尽管这显然不是其主要功能。它使用生产流量来检测服务器故障,负载均衡算法较为有限,粘性会话(stickiness)也非常有限。但在一些已经部署了 NGINX 的简单场景中,使用它也是合理的。好处是,由于它与 HAProxy 集成得很好,当达到其极限时,稍后添加 HAProxy 也没有任何问题。Varnish 也能对其后端服务器进行一些负载均衡,并支持真正的健康检查。但是,它不实现粘性会话,所以和 NGINX 一样,只要不需要粘性会话,用它来起步就足够了。同样地,由于 HAProxy 和 Varnish 集成得非常好,之后很容易将其加入进来以补充功能集。
如果您想就任何事情联系开发人员或任何社区成员,最好的方式通常是通过邮件列表,将您的邮件发送到 haproxy@formilux.org。请注意,此列表是公开的,其存档也是公开的,因此您应避免泄露敏感信息。这里有数千名不同经验水平的用户,即使是最复杂的问题通常也能相对较快地找到最佳答案。也欢迎提出建议。对于使用电子邮件有困难的用户,可以使用一个 Discourse 平台,地址是 http://discourse.haproxy.org/ 。但请记住,在那里阅读问题的人较少,而且大多数问题都由一个非常小的团队处理。在任何情况下,请对那些利用业余时间帮助他人的人保持耐心和尊重。如果您认为自己发现了一个错误但不确定,最好在邮件列表上报告。如果您非常确信自己发现了一个错误,您的版本在所在分支中是最新的,并且您已经有一个 GitHub 帐户,请随时直接访问 https://github.com/haproxy/haproxy/ 并提交一个 issue,附上所有可能获得的详细信息。再次强调,这是公开的,所以请注意不要发布您日后可能会后悔的信息。由于 issue 跟踪器本身就像一个很长的帖子,请避免粘贴非常长的转储内容(几百行或更多),而是将它们作为附件上传。如果您发现了一个您绝对确定可以被视为严重安全问题,并且在公共场所讨论会使许多用户陷入严重麻烦的问题,那么您可以将其连同复现方法发送到 security@haproxy.org。一个由受信任的开发人员组成的小团队将会收到它,并能够提出修复方案。我们通常不使用禁运期(embargoes),一旦修复可用,就会被合并。在极少数情况下,可能会与软件供应商协调发布。请注意,这个过程通常会打乱每个人的工作,而且仓促发布的版本有时会引入新的错误,所以除非绝对必要,否则最好避免;因此,对于那些不必要地造成这种额外负担的报告,通常不会得到太多考虑,而让您的工作得到认可的最好方式通常是提供一个可行的修复方案,这将会出现在变更日志中。


HAProxy 3.3.0 – 入门指南
,