NGINX完全指南(二):高性能负载均衡

2 阅读10分钟

高性能负载均衡

NGINX能够以多种方式满足负载均衡需求,例如HTTP、传输控制协议(TCP)和用户数据报协议(UDP)负载均衡,本章会对这些内容做详细介绍

一、HTTP负载均衡

如果希望将负载分发到两台或多台HTTP服务器,可以在NGINX的HTTP模块内使用upstream代码块对HTTP服务器实施负载均衡:

upstream backend{
    server 10.19.12.45:80 weight=1;
    server app.example.com:80 weight=2;
    server spare.example.com:80 backup;
}
server {
    localtion / {
        proxy_pass http://backend;
    }
}

该配置对端口80的两台HTTP服务器实施负载均衡,然后再将另一台服务器定义为backup,以便在两台主服务器不可用时发挥作用。可选地weight参数指示NGINX向第二台服务器传输两倍的请求。未使用时,默认值为1。

详解

HTTP的upstream模块控制着HTTP请求负载均衡。该模块定义了一个目标池,它可以是Unix套接字、IP地址和服务器主机名的任意组合,也可以是他们的混合使用配置。upstream模块还定义了如何将任一个请求分发给上游服务器。

每个上游目标都通过server指令在上游池中进行定义。除了上游服务器地址以外,server指令还接收可选参数。可选参数能够增强对请求路由的控制。这包括均衡算法中服务器的weight参数(无论服务器处于待机模式、可用还是不可用),以及确定服务器是否不可用的参数。NGINX Plus还提供了许多其他好用的参数,例如对服务器的连接限制、高级DNS解析控制以及服务器启动后缓慢增加与服务器的连接等等。

二、TCP负载均衡

希望将负载分发到两台或多台TCP服务器,可以在stream模块内使用upstream代码块对TCP服务实施负载均衡:

stream{
    upstream mysql_read {
        server read1.example.com:3306 weight=5;
        server read2.example.com:3306;
        server 10.10.12.34:3306 backup;
    }
    
    server {
        listen 3306;
        proxy_pass mysql_read;
    }

}

此示例中的server代码块指示NGINX侦听TCP端口3306,并在两个MYSQL数据库读取副本之间实施负载均衡,同时将另一台服务器定义为backup,以便在主服务器崩溃时接管流量。

该配置不会被添加到conf.d文件夹中,因为默认情况下,该文件夹包含在http代码块中; 应该另行创建名为stream.conf.d的文件夹,打开nginx.conf文件中的stream代码块,添加新文件夹以支持stream配置。示例见下。

在/etc/nginx/nginx.conf配置文件中:

user nginx;
worker_processes auto;
pid /run/nginx.pid;

stream {
    include /etc/nginx/stream.conf.d/*.conf
}


名为/etc/nginx/stream.conf.d/mysql_reads.conf的文件可能包含以下配置:

upstream mysql_read {
    server read1.example.com:3306 weight=5;
    server read2.example.com:3306;
    server 10.10.12.34:3306 backup;
}

server {
    listen 3306;
    proxy_pass_mysql_read;
}

详解

http和stream上下文之间的区别主要在于他们在OSI模型的不同层上运行。http上下文在应用层(七层)运行,stream在传输层(四层)运行。这并不意味着stream上下文不能通过一些巧妙地脚本获得应用感知能力,而是说http上下文是专门为了完全理解HTTP协议而设计的,stream上下文默认情况下只能对数据包进行路由和负载均衡。

TCP负载均衡由NGINX的stream模块定义。stream模块与HTTP模块一样,允许定义上游(upstream)的服务器池并配置侦听服务器。在配置服务器侦听给定端口时,必须定义待侦听的端口或者地址加端口(可选)。然后必须配置目标服务,无论这是连接另一个地址的直接反向代理还是上游资源池。

配置中有许多选项可以改变TCP连接反向代理的属性,包括SSL/TLS验证限制、超时和keepalive等。这些代理选项的一些值可以是(或包含)变量,例如下载速率、验证SSL/TLS证书时使用的名称等。

TCP与HTTP负载均衡中的upstream指令非常相似,它们均将上游资源定义为服务器,配置格式同样为Unix套接字、IP或FQDN,此外服务器weight参数、最大连接数、DNS解析器、连接数缓增期以及判断服务器是激活状态、故障状态还是备用模式的参数也都相似。

三、UDP负载均衡

希望将负载分发到两台或多台UDP服务器,可以在NGINX的stream模块内使用upstream代码块(定义为udp)对UDP服务器实施负载均衡

stream{
    upstream ntp{
        server ntp1.example.com:123 weight=2;
        server ntp2.example.com:123;
    }
    
    server {
        listen 123 udp;
        proxy_pass ntp;
    }
}

这部分配置对使用UDP协议的两台上游(upstream)网络时间协议(NTP)服务器实施了负载均衡。UDP负载均衡的指定非常简单,只需要使用listen指令中的udp参数便可。

如果进行负载均衡的服务需要在客户端和服务器之间来回发送多个数据包,则可以指定reuseport参数。例如,OpenVPN、互联网语音协议(VoIP)、虚拟桌面解决方案和数据包传输层安全(DTLS)都是这种类型的服务。下面举例说明了如何使用NGINX处理OpenVPN连接并将其代理到本地运行的OpenVPN服务:

stream{
    server {
        listen 1195 udp reuseport;
        proxy_pass 127.0.0.1:1194;
    }
}

详解

你可能会问:“我的DNS A或服务记录(SRV记录)中已经有多个主机了,又何必再要负载均衡器呢?”原因是NGINX不仅有备选的负载均衡算法,我们还可以对DNS服务器本身实施负载均衡。UDP服务构成了我们在网络系统中以来的许多服务,例如DNS、NTP、QUIC、HTTP/3和VoIP。UDP负载均衡可能对某些企业来说不太常见,但在大型环境中十分有用。

与TCP类似,你可以在stream模块中找到UDP负载均衡,并以同样的方式完成大部分配置。两者的主要区别在于,UDP负载均衡的listen指令指定打开的套接字用于处理UDP数据报(datagram)。此外,在处理数据报时,UDP负载均衡还提供了TCP所没有的一些其他指令,例如proxy_response指令,它向NGINX指定了可以从上游服务器发送多少预期的响应。默认情况下,除非达到proxy_timeout限制,否则这一数量是无限的。proxy_timeout指令设置了在连接关闭之前,客户端或代理服务器连接连续进行两次读取或写入操作之间的时间。

reuseport参数指示NGINX为每个worker进程创建一个单独的侦听套接字。这允许内核在worker进程之间分发传入的连接,以处理在客户端和服务器之间发送的多个数据包。reuseport功能仅适用于Linux kernels3.9及更高版本、DragonFly BSD、FreeBSD 12及更高版本。

四、负载均衡方式

在有异构工作负载或服务器池时,轮询负载均衡可能不适合,可以使用NGINX的其他负载均衡方法之一,例如最少连接、最短时间、通用哈希、随机算法或IP哈希。此示例将后端上游(upstream)池的负载均衡算法设为了选择连接数最少的服务器:

upstream backend {
    least_conn;
    server backend.example.com;
    server backend1.example.com;
}

除了通用哈希、随机算法和最短时间外,所有其他负载均衡算法都是独立的指令,如上例所示。下面的“详解”一节解释了这些指令的参数。

下一个示例使用通用哈希算法和$remote_addr变量。它采用了与IP哈希相同的路由算法;不过通用哈希可以在stream上下文中使用,而IP哈希仅在http上下文中可用。你可以替换所用的变量,或者添加更多变量来改变通用哈希算法分配负载均衡的方式。在下面的示例中,upstream代码块被配置为使用客户端IP地址和通用哈希算法:

upstream backend {
    hash $remote_addr;
    server backend.example.com;
    server backend1.example.com;
}

详解

并非所有请求或数据包都有相同的权重。有鉴于此,轮询甚至上例使用的加权轮询豆都将无法满足所有应用或流量的需求。NGINX提供了多种负载均衡算法,你可以根据特定的用例进行选择,也可以对你选择的算法进行配置。以下负载均衡方法(IP哈希除外)可用于HTTP、TCP和UDP上游池:

轮询

轮询是默认的负载均衡方法,它按照上游池中服务器列表的顺序分发请求。当上游服务器的容量变化时,还可以考虑使用加权轮询。权重的整数值越高,服务器在轮询中的优势就越大。权重背后的算法只是加权平均值的统计概率。

最少连接

此方法通过将当前请求代理到打开连接数最少得上游服务器实现负载均衡。与轮询一样,在决定将连接发送到哪台服务器时,最少连接也会考虑权重。其指令名称是least_conn。

通用哈希

管理员使用请求或运行时给定的文本、变量或两者的组合定义哈希值。NGINX 能够为当前请求生成哈希值并将其放在上游服务器上,从而在这些服务器之间分发负载。当您希望更好地控制请求的发送位置或确定哪台上游服务器最有可能缓存数据时,此方法就会派上用场。请注意,当从池中添加或删除服务器时,将重新分发哈希请求。此算法有一个可选的参数:consistent,它能够将重新分发带来的影响最小化。其指令名称是hash。

随机

该方法用于指示 NGINX 从组中随机选择一台服务器,同时考虑服务器的权重。可 选的 two [method] 参数指示 NGINX 随机选择两台服务器,然后使用提供的负载 均衡方法对两者均匀地分发请求。默认情况下,如果传输的参数只有 two,没有 method,则使用 least_conn 方法。随机负载均衡的指令名称是 random。

IP哈希

此方法仅适用于HTTP。IP哈希算法使用客户端IP地址作为哈希。IP哈希与通用哈希存在细微的不同,前者使用IPv4地址的前三个八进制位或整个IPv6地址,而后者使用的是远程变量。当会话状态十分重要,但又无法通过应用的共享内存进行处理时,此方法可以确保客户端始终被代理到同一上游服务器(主要服务器可用)。此方法在分发哈希值时也考虑了weight参数。其指令名称是ip_hash。

五、被动健康检查

如果你想检查上游服务器的健康状况,确保其成功提供代理流量,可以通过NGINX健康检查和负载均衡确保只使用健康的上游服务器:

upstream backend {
    server backend1.example.com:1234 max_fails=3 fail_timeout=3s;
    server backend2.example.com max_fails=3 fail_timeout=3s;
}

详解

NGINX开源版提供了被动健康检查功能,并使用了相同的server参数来实施HTTP、TCP和UDP负载均衡。当客户端发出请求时,被动监控功能可以监测通过NGINX的失效或超时连接。默认情况下启用被动健康检查;此外提到的参数允许您调整它们的行为。max_fails的默认值为1,fail_timeout的默认值为10s。健康监控在所有类型的负载均衡中都很重要,这不仅是为了保障客户体验,也是为了实现业务连续性。NGINX能够被动监控上游HTTP、TCP和UDP服务器,确保他们健康、高效地运行。