Nginx负载均衡

  • 来源:新网
  • 更新日期:2018-04-18

摘要:跨多个应用程序实例的负载平衡是一种常用的优化资源利用、最大化吞吐量、减少延迟和保证容错的技术。

1 概述
 
跨多个应用程序实例的负载平衡是一种常用的优化资源利用、最大化吞吐量、减少延迟和保证容错的技术。
t01c2f8168e28df16e8.jpg
 
2 代理一组服务器通信
 
为了使用带有一组服务器的Nginx,首先,你需要定义带有upstream指令的组。指令放置在http上下文中。
 
组中的服务器使用server指令配置(不要和定义虚拟主机的server块混淆)。例如,以下配置定义一组名为backend由三个服务器组成的配置:
 
http {
 
upstream backend {
 
server backend1.example.com weight=5;
 
server backend2.example.com;
 
server 192.0.0.1 backup;
 
}
 
}
 
为了传递请求给服务器组,可以在proxy_pass(或fastcgi_pass、memcached_pass、uwsgi_pass、scgi_pass)指令中配置组名。在下面的一个例子中,虚拟主机运行在Nginx上通过所有请求到前面定义的backend服务器组:
 
server {
 
location / {
 
proxy_pass http://backend;
 
}
 
}
 
总配置:
 
http {
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com;
 
server 192.0.0.1 backup;
 
}
 
server {
 
location / {
 
proxy_pass http://backend;
 
}
 
}
 
}
 
3 选择负载均衡方法
 
Nginx提供四种负载均衡方法,Nginx+有五种:
 
轮询:在考虑服务器权重的情况下,请求被均匀的分发给服务器。默认使用:
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com;
 
}
 
least_conn(最少连接):在考虑服务器权重的情况下,请求被分配给活跃连接最少的服务器:
 
upstream backend {
 
least_conn;
 
server backend1.example.com;
 
server backend2.example.com;
 
}
 
ip_hash:服务器分发请求通过客户的IP地址决定。在这种情况下,IPv4地址的前三个八位或整个IPv6地址用于计算哈希值。该方法保证来自相同地址的请求分发给相同的服务器除非服务器无法提供服务。
 
upstream backend {
 
ip_hash;
 
server backend1.example.com;
 
server backend2.example.com;
 
}
 
如果一台服务器需要临时删除,为了保证当前客户端IP地址的哈希,它能使用down参数标记。请求被该自动发送到该组中该服务器的下一个服务器处理:
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com;
 
server backend3.example.com down;
 
}
 
普通哈希:服务器通过用户自定义的键决定分配请求给那台服务器,该值可以是文本、变量或文本和变量的组合。例如,键可以是源IP地址和端口或URI:
 
upstream backend {
 
hash $request_uri consistent;
 
server backend1.example.com;
 
server backend2.example.com;
 
}
 
hash指令的consistent可选参数启用ketama一致性哈希负载均衡。如果服务器组中添加或删除服务器,只有少数键需要重写映射,将最小化缓存丢失。
 
least_time(Nginx+):Nginx+选择平均延迟最低和活跃连接数最少的服务器: header – Time to receive the first byte from the server last_byte – Time to receive the full response from the server
 
upstream backend {
 
least_time header;
 
server backend1.example.com;
 
server backend2.example.com;
 
}
 
4 服务器权重
 
默认,Nginx使用轮询方法分发请求给组中的服务器依赖于它们权重。通过server指令的weight参数设置服务器的权重,默认是1:
 
upstream backend {
 
server backend1.example.comweight=5;
 
server backend2.example.com;
 
server 192.0.0.1 backup;
 
}
 
在例子中,backend1.example.com的权重是5,其它两个默认是1,但有一个IP地址192.0.0.1标记为备机并且不接收请求除非其它服务器都不可用。使用该权重配置,每六个请求,五个发送给backend1.example.com,一个发送给backend2.example.com。
 
5 服务器慢启动
 
服务器慢启动特性放置最近恢复的服务器被高负荷请求,这可能会超时导致服务器再次标记为失败。
 
慢启动允许upstream服务器在它恢复或变得可用时,从零到正常值逐渐恢复它的权重。这可以使用server指令的slow_start参数完成:
 
upstream backend {
 
server backend1.example.com slow_start=30s;
 
server backend2.example.com;
 
server 192.0.0.1 backup;
 
}
 
注意,如果组中只有一个服务器,max_fails、fail_timeout和slow_start参数将被忽略并且这个服务器不被认为是可用的。
 
6 启动回话持久化
 
回话持久化意味着Nginx标识用户回话并路由请求到相同的upstream服务器。
 
Nginx支持三种回话持久化方法。
 
粘性cookie方法。Nginx添加回话cookie到第一个来自upstream组的响应并标识发送响应的服务器。当客户端发送下一个请求,它将包含cookie值而Nginx将路由请求到相同的upstream服务器:
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com;
 
sticky cookie srv_id expires=1h domain=.example.com path=/;
 
}
 
在例子中,srv_id参数设置将要设置或检查的cookie的名称。可选的expires参数设置浏览器保存cookie的时间。可选的domain参数设置cookie的域名。可选的参数path参数定义cookie的路径。这是最简单的回话持久化方法。
 
粘性路由方法。当客户端第一次请求Nginx,Nginx分配一个“route”给客户端。所有后续请求将比较server指令的route参数。Route信息来自cookie或URI
 
upstream backend {
 
server backend1.example.com route=a;
 
server backend2.example.com route=b;
 
sticky route $route_cookie $route_uri;
 
}
 
cookie学习方法。Nginx第一次通过检查请求和响应查找回话标识符。然后Nginx学习那个upstream服务器相当于那个回话标识符。通常,这些标识符防止在HTTP cookie中。如果请求包含一个回话标识符已经“学习”,Nginx将转发请求给相应的服务器:
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com;
 
sticky learn
 
create=$upstream_cookie_examplecookie
 
lookup=$cookie_examplecookie
 
zone=client_sessions:1m
 
timeout=1h;
 
}
 
在例子中,其中一台upstream服务器通过在响应中设置cookie“EXAMPLECOOKIE”创建回话。
 
The obligatory parameter create specifies a variable that indicates how a new session is created. In our example, new sessions are created from the cookie “EXAMPLECOOKIE” sent by the upstream server.
 
The obligatory parameter lookup specifies how to search for existing sessions. In our example, existing sessions are searched in the cookie “EXAMPLECOOKIE” sent by the client.
 
The obligatory parameter zone specifies a shared memory zone where all information about sticky sessions is kept. In our example, the zone is named client_sessions and has the size of 1 megabyte.
 
This is a more sophisticated session persistence method as it does not require keeping any cookies on the client side: all info is kept server-side in the shared memory zone.
 
7 限制连接数
 
使用max_conns参数限制连接数。
 
如果已经达到max_conns的限制值,请求会被放置到队列中,为了进一步处理提供queue指令。可以设置队列存放的最大请求数:
 
upstream backend {
 
server backend1.example.com max_conns=3;
 
server backend2.example.com;
 
queue 100 timeout=70;
 
}
 
如果队列填满请求或upstream服务器不能选择,在指定的超时时间后,客户端将接收一个错误。
 
注意,如果有空闲的keepalive连接在其它worker进程中打开,max_conns限制可以忽略。因此,连接服务器的总数可能大于max_conns。
 
8 被动健康检查
 
当Nginx认为服务器不可用,它临时停止发送请求给服务器,直到它认为服务器再次可用。Server指令的以下参数配置认为服务器不可用的条件:
 
fail_timeout参数设置时间内大量失败发生并,一直认为服务器不可用。话句话说,fail_timeout设置服务器不可用的间隔。
 
max_fails参数设置在指定时间内尝试失败的次数,一直认为服务器不可用。
 
默认值是10秒和1次。因此,如果Nginx发送请求给一个服务器失败或不能从该服务器接收响应至少一次,它立即认为服务器10秒内不可用。以下例子显示如何设置这些参数:
 
upstream backend {
 
server backend1.example.com;
 
server backend2.example.com max_fails=3 fail_timeout=30s;
 
server backend3.example.com max_fails=2;
 
}
 
9 主动健康检查
 
定期发送特定请求给每个服务器并检查,检查响应在一定条件下满足检测服务器可用性。
 
为了启用该类型的健康检查,在你的nginx.conf文件传入请求的location包括health_check指令。此外,服务器组也应该使用zone指令动态可配置:
 
http {
 
upstream backend {
 
zone backend 64k;
 
server backend1.example.com;
 
server backend2.example.com;
 
server backend3.example.com;
 
server backend4.example.com;
 
}
 
server {
 
location / {
 
proxy_pass http://backend;
 
health_check;
 
}
 
}
 
}
 
该配置定义一个服务器和一个虚拟服务器,使用一个location传入所有请求给服务器组。它使用默认参数也启用健康检查。在这种情况下,每5秒Nginx发送”/”请求给每个后端服务器。如果任意通信错误或超市发生(或代理服务器响应状态码大于2xx或3xx)健康检查该代理服务器失败。任意服务器失败,健康检查认为服务器不健康,Nginx停止发送客户端请求给它直到再次通过健康检查。
 
zone指令定义一个内存区域在worker进程之间共享,用于存储服务器组的配置。This enablesthe worker processes to use the same set of counters to keep track of responses from the servers in the group. The zone directive also makes the group dynamically configurable.
 
这种行为能使用health_check指令的参数覆盖:
 
location / {
 
proxy_pass http://backend;
 
health_check interval=10 fails=3 passes=2;
 
}
 
这里,两次连续的健康检查间隔10秒。此外,一台服务器连续失败三次之后认为是不健康的。一个服务器需要连续通过两次检查再次健康。
 
健康检查可以设置特定的URI:
 
location / {
 
proxy_pass http://backend;
 
health_check uri=/some/path;
 
}
 
提供URI将附着到upstream指令中服务器的域名或IP地址。例如,第一个服务器的健康检查的URI是http://backend1.example.com/some/path。
 
最后可以设置自定义满足健康响应的条件。条件通过match块指定:
 
http {
 
...
 
match server_ok {
 
status 200-399;
 
body !~ "maintenance mode";
 
}
 
server {
 
...
 
location / {
 
proxy_pass http://backend;
 
health_check match=server_ok;
 
}
 
}
 
}
 
这里,如果响应状态从200到399并且响应体不匹配提供的正则表达式健康检查合格。
 
match指令允许Nginx检查响应的状态、头字段和体。使用该指令可以验证是否状态在指定范围,是否响应包括一个头或是否头或体匹配一个正则表达式。Match指令能包含一个状态条件、一个体条件和多个头条件。对应于match块,响应必须指定的所有条件。
 
例如,下面的match指令查找响应有状态码200,包含Content-Type头精确值为text/html,响应体中有“Welcome to nginx!”文本:
 
match welcome {
 
status 200;
 
header Content-Type = text/html;
 
body ~ "Welcome to nginx!";
 
}
 
在下面的例子中,使用感叹号表示取反。
 
match not_redirect {
 
status ! 301-303 307;
 
header ! Refresh;
 
}
 
健康检查也支持费HTTP协议,例如FastCGI、uwsgi、SCGI和memcached。
 
10 多个worker进程共享数据
 
如果upstream指令不包括zone指令,每个worker进程保存自己的服务器组配置备份并维持它们自己的计数器。计数器包括组中没有服务器当前连接数和尝试传递请求到服务器的失败数。因此,服务器组配置不能改变。
 
如果upstream指令包括zone指令,服务器组的配置放置在所有worker进程共享的内存区域。这种情况下是动态可配置的,因为worker进程访问相同的组配置备份和利用相同的计数器。
 
zone指令是服务器组的健康检查和在线重配置必须的。然而,服务器组的其它特性也能受益于该指令。
 
例如,如果组的配置不共享,每个worker进程维护它自己的计数器。在这种情况下,每个请求只有一个worker进程。当worker进程选择处理一个请求失败传输请求给服务器,其它worker进程什么也不知道。然而有些worker进程认为服务器不可用,其它可能一直发送请求到该服务器。
 
least_conn负载均衡方法不可用在没有zone指令的环境下工作。
 
11 使用DNS配置HTTP负载均衡
 
Nginx能监听服务器的域名相应IP地址的改变并自动应用这些改变到Nginx不需要重新启动。这可用使用resolver指令,必须指定在http块,在服务器组中的server指令的resolve参数。
 
http {
 
resolver 10.0.0.1 valid=300s ipv6=off;
 
resolver_timeout 10s;
 
server {
 
location / {
 
proxy_pass http://backend;
 
}
 
}
 
upstream backend {
 
zone backend 32k;
 
least_conn;
 
...
 
server backend1.example.com resolve;
 
server backend2.example.com resolve;
 
}
 
}
 
在该例子中,server指令的resolve参数定期的重新解析backend1.example.com和backend2.example.com为IP地址。默认,Nginx重解析DNS记录基于TTL,但TTL的值使用resolver指令的valid参数覆盖,在我们的例子中是5分钟。
 
可选参数ipv6=off参数只允许解析为IPv4地址。
 
如果域名解析多个IP地址,地址将保存到upstream配置和负载均衡。在我们的例子中,服务器负载均衡依赖于least_conn负载均衡方法。如果一个或多个IP地址改变或添加/删除,那么服务器将重新均衡。
 
12 微软Exchange服务器的负载均衡
 
Nginx能代理微软Exchange,与一台服务器或一组服务器通信和负载均衡。
 
在location中,配置代理微软Exchange upstream服务器组
 
location / {
 
proxy_pass https://exchange;
 
...
 
}
 
为了能连接到upstream服务器,在location块设置proxy_http_version指令值为1.1,proxy_set_header指令为“Connection “””,就像一个keepalive连接:
 
location / {
 
...
 
proxy_http_version 1.1;
 
proxy_set_header Connection "";
 
...
 
}
 
在http块中,使用微软Exchange服务器配置负载均衡组。使用之前proxy_pass指令指定的服务器组名指定upstream指令。然后指定ntlm指令允许组接收NTLM验证请求:
 
http {
 
...
 
upstream exchange {
 
zone exchange 64k;
 
ntlm;
 
...
 
}
 
}
 
添加微软Exchange服务器到upstream组,可以指定一个http负载均衡方法:
 
http {
 
...
 
upstream exchange {
 
zone exchange 64k;
 
ntlm;
 
server exchange1.example.com;
 
server exchange2.example.com;
 
...
 
}
 
}
 
12.1 NTLM例子
 
http {
 
...
 
upstream exchange {
 
zone exchange 64k;
 
ntlm;
 
server exchange1.example.com;
 
server exchange2.example.com;
 
}
 
server {
 
listen 443 ssl;
 
ssl_certificate /etc/nginx/ssl/company.com.crt;
 
ssl_certificate_key /etc/nginx/ssl/company.com.key;
 
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 
location / {
 
proxy_pass https://exchange;
 
proxy_http_version 1.1;
 
proxy_set_header Connection "";
 
}
 
}
 
}
 
13 动态配置
 
服务器组的配置可以使用HTTP界面动态修改。配置命令能用于浏览组中所有的服务器或特定服务器,修改特定服务器参数,添加或删除服务器。
 
13.1 设置动态配置
 
包括zone指令在upstream块中。zone指令配置一个共享内存区域并设置区域名和大小。服务器组的配置保存在区域中,因此,所有worker进程使用相同配置:
 
http {
 
...
 
upstream appservers {
 
zone appservers 64k;
 
server appserv1.example.com weight=5;
 
server appserv2.example.com:8080 fail_timeout=5s;
 
server reserve1.example.com:8080 backup;
 
server reserve2.example.com:8080 backup;
 
}
 
}
 
放置upstream_conf指令在单独的location中:
 
server {
 
location /upstream_conf {
 
upstream_conf;
 
...
 
}
 
}
 
强烈建议严格控制访问这个位置,例如,只允许本地访问:
 
server {
 
location /upstream_conf {
 
upstream_conf;
 
allow 127.0.0.1;
 
deny all;
 
}
 
}
 
完整的配置:
 
http {
 
...
 
# 服务器组的配置
 
upstream appservers {
 
zone appservers 64k;
 
server appserv1.example.com weight=5;
 
server appserv2.example.com:8080 fail_timeout=5s;
 
server reserve1.example.com:8080 backup;
 
server reserve2.example.com:8080 backup;
 
}
 
server {
 
# 代理请求到组
 
location / {
 
proxy_pass http://appservers;
 
health_check;
 
}
 
# 配置请求
 
location /upstream_conf {
 
upstream_conf;
 
allow 127.0.0.1;
 
deny all;
 
}
 
}
 
}
 
在例子中,访问第二个location只允许从127.0.0.1IP地址访问。
 
13.2 动态持久化
 
上面的配置允许存储动态改变共享内存中的配置。这些改变在Nginx重新加载配置文件的时候都会失效。
 
为了让这些改变在配置重新加载时持久化,你需要将upstream服务器从upstream块中移动到保存upstream服务器状态的特定文件中。文件的路径使用state指令设置。推荐,Linux路径是/var/lib/nginx/state/,FreeBSD路径是/var/db/nginx/state:
 
http {
 
...
 
upstream appservers {
 
zone appservers 64k;
 
state /var/lib/nginx/state/appservers.conf;
 
}
 
}
 
请记住,该文件只能使用upstream_conf API界面通过配置命令修改,避免直接修改文件。
 
13.3 动态配置upstream服务器
 
通过发送HTTP请求传入配置命令给Nginx。请求有一个合适的URI获取upstream_conf指令。请求也包括upstream参数设置服务器组的名称。
 
例如,浏览组中所有的备机:
 
http://127.0.0.1/upstream_conf?upstream=appservers&backup=
 
添加新服务器到组:
 
http://127.0.0.1/upstream_conf?add=&upstream=appservers&server=appserv3.example.com:8080&weight=2&max_fails=3
 
删除服务器:
 
http://127.0.0.1/upstream_conf?remove=&upstream=appservers&id=2
 
修改服务器参数:
 
http://127.0.0.1/upstream_conf?upstream=appservers&id=2&down=