Nginx 高可用负载均衡策略

了解如何通过 Nginx 负载均衡实现 Web 应用的高可用性。本指南深入探讨了 Nginx 的核心负载均衡策略,包括轮询、加权轮询、最少连接和 IP 哈希。通过实际配置示例、健康检查机制解析以及最佳实践,确保您的应用在不同流量负载下保持可访问性和高性能。

Nginx 高可用负载均衡策略

Nginx 负载均衡通常是在遇到第一个棘手瓶颈后引入的:单一应用服务器过载、需要维护,或因故障导致整个网站瘫痪。在多个后端前部署 Nginx,可以分散请求、从容下线服务器,并应对常见故障。

但这本身并非魔法般的高可用方案。开源版 Nginx 能在连接失败后停止向某后端发送流量,但它无法深入判断您的结账页面、API 依赖或数据库连接是否健康。一套完善的方案需要结合 Nginx 上游配置、合理的超时设置、应用级健康检查端点、外部监控,以及能快速移除故障后端的部署流程。

理解负载均衡

负载均衡的核心是智能地将客户端请求分发到服务器池。不再是单台服务器处理所有流量,而是多台服务器协同工作。这带来了几个关键优势:

  • 高可用性:当一台服务器以可检测方式失效时,其他服务器可继续处理请求。
  • 可扩展性:流量增加时,可向池中添加更多服务器来分担负载。
  • 性能:流量分发防止单台服务器过载,从而缩短响应时间。
  • 可靠性:消除单点故障,使应用更加健壮。

在负载均衡架构中,Nginx 充当反向代理。它接收客户端请求,并根据配置的算法将其转发到可用的后端服务器,再将后端响应返回给客户端,整个过程对终端用户透明。

Nginx 负载均衡指令

Nginx 通过配置文件(通常是 nginx.conf 或其包含的文件)中的特定指令来定义上游服务器组及其负载均衡行为。

upstream

upstream 块用于定义一组服务器,Nginx 将在它们之间均衡流量。该块通常放置在 http 上下文中。

http {
    upstream my_backend_servers {
        # 服务器配置写在这里
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://my_backend_servers;
        }
    }
}

upstream 块内部,使用 server 指令列出后端服务器,指定其 IP 地址或主机名及端口。

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server 192.168.1.100:8080;
}

proxy_pass 指令

proxy_pass 指令用于 location 块内,指向您定义的 upstream 组。Nginx 将根据配置的负载均衡算法为每个请求选择该组中的一台服务器。

Nginx 负载均衡算法

Nginx 支持多种负载均衡算法,各有不同的流量分发方式。默认算法是轮询

1. 轮询(默认)

轮询模式下,Nginx 按顺序将请求依次分发到 upstream 组中的每台服务器。随着时间的推移,每台服务器接收到的请求量相等。它简单有效,适用于性能相同的服务器,也是最常用的方法。

配置:

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

优点:

  • 实现和理解简单。
  • 若服务器容量相近,能均匀分配负载。

缺点:

  • 不考虑服务器负载或响应时间。慢速服务器仍可能收到请求。

2. 加权轮询

加权轮询允许您为每台服务器分配权重。权重越高的服务器将获得更大比例的流量。当服务器容量不同时(例如硬件性能更强),此方法非常有用。

配置:

upstream my_backend_servers {
    server backend1.example.com weight=3;
    server backend2.example.com weight=1;
}

在此示例中,backend1.example.com 将接收三倍于 backend2.example.com 的请求。

优点:

  • 可根据服务器容量进行负载均衡。

缺点:

  • 仍不考虑实时服务器负载。

3. 最少连接

最少连接算法将请求定向到当前活动连接数最少的服务器。此方法更具动态性,因为它考虑了每台服务器的当前负载。

配置:

要启用最少连接,只需在 upstream 块中添加 least_conn 参数:

upstream my_backend_servers {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

优点:

  • 通过考虑当前服务器负载,更智能地分配负载。
  • 适用于连接持续时间变化较大的应用。

缺点:

  • 若连接数波动频繁,管理可能稍显复杂。

4. IP 哈希

使用 IP 哈希时,Nginx 根据客户端 IP 地址的哈希值决定由哪台服务器处理请求。这确保来自同一客户端 IP 的请求始终被发送到同一后端服务器。这对于依赖会话持久性(粘性会话)且未使用共享会话存储的应用至关重要。

配置:

upstream 块中添加 ip_hash 参数:

upstream my_backend_servers {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
}

优点:

  • 开箱即用地提供会话持久性。

缺点:

  • 如果许多客户端共享同一 IP 地址(例如通过 NAT 网关),可能导致负载不均。
  • 如果某台服务器故障,所有哈希到该服务器的客户端将受到影响,直到服务器恢复或哈希重新计算(尽管 Nginx 会尝试重新路由)。

5. 通用哈希

与 IP 哈希类似,通用哈希允许您指定用于哈希的键。该键可以是变量,如 $request_id$cookie_jsessionid 或变量组合。这为会话持久性或基于特定请求属性的路由提供了更大的灵活性。

配置:

upstream my_backend_servers {
    hash $remote_addr consistent;
    server backend1.example.com;
    server backend2.example.com;
}

hash 中使用 consistent 可实现一致性哈希,从而在服务器集合变化时最小化键的重新分配。

优点:

  • 对自定义路由逻辑高度灵活。
  • 支持一致性哈希,在服务器变更时更稳定。

缺点:

  • 需要仔细选择哈希键。

健康检查与服务器状态

为了实现有效的高可用性,Nginx 需要避开故障后端。开源版本主要通过被动方式实现:在代理真实流量时记录失败的尝试。这有助于处理宕机主机、连接拒绝以及某些超时情况。但它与主动健康检查不同——主动健康检查会在用户访问服务前每隔几秒调用 /healthz 端点。

max_failsfail_timeout

这些参数添加到 upstream 块内的 server 指令中,控制 Nginx 如何处理故障服务器。

  • max_fails:在指定的 fail_timeout 时间内,与服务器通信失败的最大次数。达到 max_fails 后,该服务器被标记为不可用。
  • fail_timeout:服务器被视为不可用的持续时间。超时后,Nginx 将再次尝试检查其状态。

配置:

upstream my_backend_servers {
    server backend1.example.com max_fails=3 fail_timeout=30s;
    server backend2.example.com max_fails=3 fail_timeout=30s;
}

在此示例中,如果 backend1.example.com 在故障窗口内出现三次失败尝试,Nginx 将暂时避开它。超时后,Nginx 可能会再次尝试。失败基于连接/代理尝试,而非自定义的应用健康响应——除非您使用额外的工具或 Nginx Plus 功能。

backup 参数

backup 参数将服务器标记为备用。仅当 upstream 组中所有其他活动服务器都不可用时,它才会接收流量。

配置:

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server backup.example.com backup;
}

如果 backend1backend2 宕机,backup.example.com 将接管。

Nginx Plus 健康检查

Nginx Plus(商业版)包含内置的主动健康检查功能。它可以定期向后端发送请求、评估响应,并在用户流量路由到那里之前移除不健康的服务器。如果您使用开源版 Nginx,仍然可以构建一个可靠的系统,但通常需要配合外部监控、服务发现或自动化工具来编辑/移除上游目标。

实际配置示例

让我们通过常见场景将这些概念付诸实践。

场景 1:简单轮询负载均衡

在两台相同的 Web 服务器之间分发流量。

配置:

http {
    upstream web_servers {
        server 10.0.0.10;
        server 10.0.0.11;
    }

    server {
        listen 80;
        server_name yourdomain.com;

        location / {
            proxy_pass http://web_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

说明:

  • upstream web_servers:定义一个名为 web_servers 的组。
  • server 10.0.0.10;server 10.0.0.11;:指定后端服务器。
  • proxy_pass http://web_servers;:将流量导向 web_servers 上游组。
  • proxy_set_header:这些指令对于向后端服务器传递原始客户端信息至关重要,日志记录或应用逻辑通常需要这些信息。

场景 2:带会话持久性的负载均衡(IP 哈希)

确保用户始终连接到同一后端服务器,适用于本地存储会话数据的应用。

仅在您理解其权衡时使用。如果许多用户通过同一办公室 NAT、移动运营商网关或企业代理访问,IP 哈希可能会向一个后端发送过多流量。共享会话存储、签名无状态 Cookie 或应用级会话复制通常比依赖客户端 IP 粘性更清晰。

配置:

http {
    upstream app_servers {
        ip_hash;
        server 192.168.1.50:8000;
        server 192.168.1.51:8000;
    }

    server {
        listen 80;
        server_name api.yourdomain.com;

        location / {
            proxy_pass http://app_servers;
            # ... 其他 proxy_set_header 指令 ...
        }
    }
}

场景 3:带故障转移的加权负载均衡

将更多流量导向性能更强的服务器,并准备一台备用服务器。

配置:

http {
    upstream balanced_app {
        server app_server_1.local weight=5;
        server app_server_2.local weight=2;
        server app_server_3.local backup;
    }

    server {
        listen 80;
        server_name staging.yourdomain.com;

        location / {
            proxy_pass http://balanced_app;
            # ... 其他 proxy_set_header 指令 ...
        }
    }
}

这里,app_server_1.local 获得 5 份流量,app_server_2.local 获得 2 份,而 app_server_3.local 仅在其他两台不可用时才提供服务。

最佳实践与技巧

  • 使用 proxy_set_header:始终设置 HostX-Real-IPX-Forwarded-ForX-Forwarded-Proto 等头部,以便后端应用了解原始客户端的详细信息。
  • 保持 Nginx 更新:确保运行稳定且最新的 Nginx 版本,以获得安全性和性能改进。
  • 监控后端服务器:除 Nginx 的内部健康检查外,实施外部监控工具。Nginx 只知道能否到达服务器,不一定知道服务器上的应用是否正常运行。
  • 考虑 Nginx Plus:对于关键任务应用,Nginx Plus 提供主动健康检查、会话持久性和实时活动监控等高级功能,可简化管理并提高弹性。
  • DNS 负载均衡:对于跨区域或多 Nginx 入口点的流量分发,DNS 可以提供帮助,但 DNS 故障转移取决于解析器行为和 TTL。不要将其视为即时故障转移。
  • SSL 终止:通常可以在负载均衡器(Nginx)处终止 SSL,以减轻后端服务器的 SSL 处理负担。

实用的起点

对于两台或三台相同的应用服务器,从简单的轮询开始,设置保守的代理超时和清晰的上游日志记录。添加 max_failsfail_timeout,然后测试停止一个后端时会发生什么。不要等到真实事故发生时才知道 Nginx 的行为。

如果请求耗时差异很大,尝试 least_conn。如果某台服务器比其他服务器强大,使用权重。如果应用在本地存储会话状态,尽可能修复会话设计;仅在需要实用过渡方案时使用 ip_hash

最佳的 Nginx 负载均衡策略是匹配应用故障方式的策略。死机虚拟机、慢速后端、故障发布和数据库中断,从代理的角度来看都不同。配置算法后,通过小规模测试验证故障行为,然后再称该设置为高可用。