Nginx反向代理与负载均衡

《老男孩Linux运维》
Nginx Documentation



集群简介

集群就是指一组(若干)相互独立的计算机,利用高速通信网络组成的一个较大的计算机服务系统,每个集群结点都是运行各自服务的独立服务器。这些服务器之间可以彼此通信,协同向用户提供应用程序、系统资源和数据,并以单一系统的模式加以管理。
当客户机请求集群系统时,集群给用户的感觉就是一个单一独立的服务器,而实际上用户请求的是一组集群服务器。

若用一句话描述集群,即一堆服务器合作做同一件事,这些机器可能需要整个技术团队架构、设计和统一协调管理;可能分布在一个机房,也可能分布在全国各地。


集群逻辑图


为什么要使用集群

高性能(Performance)
大型网站谷歌、淘宝、百度等,都不是几台大型机可以构建的,都是上万台服务器组成的高性能集群,分布于不同的地点。

只有当并发或总请求数量超过单台服务器的承受能力时,服务器集群的优势才会体现出来。

价格有效性(Cost-effectiveness)
在达到同样性能的需求下,采用计算机集群架构比采用同等运算能力的大型计算机具有更高的性价比。

可伸缩性(Scalability)
当服务负载、压力增长时,针对集群系统进行较简单的扩展即可满足需求,且不会降低服务质量。

高可用(Availability)
单一计算机发生故障时,就无法正常提供服务;而集群架构技术可以是得系统在若干硬件设备发生故障时仍可以继续工作。
集群系统在提高系统可靠性的同时,也大大减小了系统故障带来的业务损失,目前几乎100%的网站都要求7x24h提供服务。

透明性(Transparency)
多个独立计算机组成的耦合集群系统构成一个虚拟服务器。用户访问集群系统时,就像访问一台高性能、高可用的服务器一样,集群中一部分服务器的上线、下线不会中断整个系统服务,这对用户也是透明的。

可管理性(Manageability)
这个系统可能在物理上很大,但其实很容易管理,就像管理一个单一映像系统一样。

可编程性(Programmability)
在集群系统上,容易开发及修改各类应用程序。


集群的分类

集群的常见分类

计算机集群架构按功能和结构可分为以下几类:

  • 负载均衡集群(Load balancing clusters),LB;
  • 高可用性集群(High-availability clusters), HA;
  • 高性能计算集群(High-performance cluster),HP;
  • 网格计算集群(Grid computing);

负载均衡集群(LB)和高可用性集群(HA)是互联网行业常用的集群架构模式


不同种类的集群介绍

负载均衡集群

负载均衡集群可以把很多客户集中的访问请求负载压力尽可能平均分摊在计算机集群中处理。
集群中每个节点都可以一定的访问请求负载压力,并且可以实现访问请求在各节点之间动态分配,以实现负载均衡。

负载均衡集群运行时,一般是通过一个或多个前端负载均衡器(Director)将客户访问请求分发到后端的一组服务器上,从而达到整个系统的高性能和高可用性。
一般高可用性集群和负载均衡集群会使用类似的技术,或同时具有高可用性与负载均衡的特点。

载均衡集群的作用:

  • 分担用户访问请求及数据流量(负载均衡);
  • 保持业务连续性,即7x24h服务(高可用);
  • 应用于Web业务及数据库从库等服务器的业务;

负载均衡集群典型的开源软件包括:LVS、Nginx、Haproxy 等。

高可用性集群

一般是指集群中任意一个节点失效的情况下,该节点上的所有任务会自动转移到其他正常的节点上。此过程不会影响整个集群的运行。

不同的业务会有若干秒的切换时间,DB业务明显长于Web业务切换时间。

当集群中的一个节点系统发生故障时,运行着的集群服务器会迅速做出反应,将该系统的服务分配到集群中其他正在工作的系统上运行。考虑到计算机硬件和软件的容错性,高可用性集群的主要目的是使局群的整体服务尽可能可用。
如果高可用集群中的主节点发生了故障,那么这段时间内将由备节点代替它。备节点通常是主节点的镜像。当它代替主节点时,它可以完全接管主节点(包括Ip和其他资源)提供服务,因此,使集群系统环境对系统环境来说是一致的,既不会影响用户的访问。

高可用性集群使服务器系统的运行速度和响应速度会尽可能的快。它们经常利用在多台机器上运行的冗余节点和服务来相互跟踪。
如果某个节点失败,它的替补者将在几秒钟或更多时间内接管它的职责。因此,对于用户来说,集群里的任意一台机器宕机,业务都不会受影响。

高可用性集群的作用为:

  • 当一台机器宕机后,另外一台机器接管宕机的机器的Ip资源和服务资源,提供服务;
  • 常用于不易实现负载均衡的应用,如负载均衡器、主数据库、主存储对之间;

高可用性集群常用开源软件包括:Keepalived、Heartbeat 等。

高性能计算集群

高性能计算集群也称并行计算。通常,高性能计算集群涉及为集群开发的并行应用程序,以解决复杂的科学问题。

高性能计算集群对外就好像一个超级计算机,这种超级计算机内部由数万个独立服务器组成,并且在公共消息传递层上进行通信以运行并行应用程序。

在互联网网站运维中,常用的是负载均衡集群和高可用性集群


常用集群软硬件介绍及选型

企业运维中常见集群软硬件产品
互联网企业常用开源集群软件: Nginx,LVS,Haproxy,Keepalived,Heartbeat
互联网企业常用的商业集群硬件: F5,Netscaler,Radware,A10 等。

对于集群软硬件产品选择

  • 当企业业务重要,技术力量又薄弱,并且希望出钱购买产品以获得更好的服务时,可选择硬件负载均衡产品;
  • 并用软件以及硬件产品负载均衡产品来分担单一产品的风险;
  • 中小型互联网企业,会希望通过使用开源免费的方案来解决问题。

如何选择开源集群软件产品

  • 网站在并发访问和总访问量不是很大的情况下,建议首选Nginx负载均衡,Nginx配置简单使用方便安全稳定。 另一个实现负载均衡的产品为Haproxy;
  • 如果要考虑Nginx负载均衡的高可用功能,建议首选Keepalived软件,因为安装配置简单方便稳定。类似高可用软件还有Heartbeat,但比较复杂;
  • 如果是大型企业,负载均衡可以使用 LVS+Keepalived 在前端做四层转发,后端使用Nginx或Haproxy做七层转发,再后面是应用服务器。如果是数据库与存储的负载均衡和高可用,可选值 LVS+Heartbeat;
高可用+负载均衡




Nginx负载均衡集群介绍

反向代理与负载均衡概念简介

严格地说,Nginx仅仅是作为Nginx Proxy反向代理使用的,因为这个反向代理功能表现的效果是负载均衡集群的效果,所以本文称之为Nginx负载均衡。

普通的负载均衡软件,如LVS,其实现的功能只是对请求数据包的转发、传递,从负载均衡下的节点服务器来看,接收到的请求还是来自访问负载均衡器的客户端的真实用户;
而反向代理就不一样了,反向代理服务器在接收访问用户请求后,会代理用户 重新发起请求代理下的节点服务器,最后把数据返回给客户端用户。在节点服务器看来,访问的节点服务器的客户端用户就是反向代理服务器,而非真实的网站访问用户。

即,LVS等负载均衡是转发用户请求的数据包,而Nginx反向代理是接收用户请求后重新发起请求后端节点。

这里我去看了一下Nginx的Access.log,客户端的访问日志全在代理节点上(Nginx-upstream),而后端节点的Access.log的来源是前端代理节点的IP

实现Nginx负载均衡的组件

实现Nginx负载均衡的组件主要有两个:"proxy"和"upstream":

Nginx_http模块 模块说明
ngx_http_proxy_module proxy代理模块,用于把请求后抛给服务器节点或upstream服务器池
ngx_http_upstream_module 负载均衡模块,可以实现网站的负载均衡功能即节点的健康检查


Nginx负载均衡核心组件介绍

Nginx upstream模块

Module ngx_http_upstream_module

Syntax:    upstream name { ... }
Default:    —
Context:    http

Nginx模块介绍

Nginx的负载均衡功能依赖于ngx_http_upstream_module模块,所支持的代理方式包括:proxy_pass, fastcgi_pass, memcached_pass, uwsgi_pass, scgi_pass 等,这里主要讲解proxy_pass方式。

upstream模块允许Nginx定义一组或多组节点服务器组,使用时可通过 proxy_pass 代理方式把网站的请求发送到事先定义好的对应 Upstream组 的名字上。

upstream模块语法

upstream模块内容放置于http{ }内:

upstream upstream_name {
    server  address  [ parameters ]
}

#address可以是主机名、域名、ip或Unix Socket,也可以指定端口号

#parameters代表可选参数, 有如下:
#backup,表示当前server是备用服务器,只有其它非backup后端服务器都挂掉了或很忙才会分配请求给它;
#weight,表示当前server负载权重,权重越大几率愈高;
#max_fails 和 fail_timeout 一般会关联使用,如果某台server在 fail_timeout 时间内出现了 max_fails 次连接失败,那么Nginx会认为其已经挂掉,从而在 fail_timeout 时间内不再去请求它,fail_timeout默认是 10s,max_fails默认是1,即默认情况只要是发生错误就认为服务器挂了,如果将max_fails设置为0,则表示取消这项检查;
#down,标志服务器永远不可用,可配合ip_hash使用。
upstream模块内参数 参数说明
weight 服务器权重
max_fails Nginx尝试连接后端主机失败的此时,这是值是配合 proxy_next_upstream、fastcgi_next_upstream和memcached_next_upstream这三个参数来使用的。当Nginx接收后端服务器返回这三个参数定义的状态码时,会将这个请求转发给正常工作的的后端服务器。如404、503、503,max_files=1
fail_timeout max_fails 和 fail_timeout 一般会关联使用,如果某台server在 fail_timeout 时间内出现了 max_fails 次连接失败,那么Nginx会认为其已经挂掉,从而在 fail_timeout 时间内不再去请求它,fail_timeout默认是 10s,max_fails默认是1,即默认情况只要是发生错误就认为服务器挂了,如果将max_fails设置为0,则表示取消这项检查
backup 表示当前server是备用服务器,只有其它非backup后端服务器都挂掉了或很忙才会分配请求给它
down 标志服务器永远不可用,可配合ip_hash使用

举个栗子:

upstream test {
  server 191.168.1.11 weight=5 ;
  server 191.168.1.22:82;
  server example.com:8080 max_fails=2 fail_timeout=10s backup;
  #域名的话需要解析的哦,内网记得hosts
}

如果是两台Web服务器做高可用,常规方案就需要Keepalived配合,那么这里使用Nginx的backup参数通过负载均衡功能就可以实现Web服务器集群了。

upstream模块调度算法

调度算法一般分为静态调度算法动态调度算法

静态调度算法: 即负载均衡器根据自身设置的规则进行分配,不需要考虑后端节点服务器的情况。如 轮询(rr)、权重(wrr)、ip_hash

1. rr轮询(默认调度算法):
按照客户端请求顺序把请求逐一分配到不同的后端节点服务器,相当于LVS中的rr算法。如果后端服务器宕机,宕机的服务器会被自动从节点服务器池中剔除,以使客户端的用户访问不受影响,新的请求分配给正常的服务器;

2. wrr权重轮询
权重越大,被转发的请求也就越多。可以根据服务器的配置和性能指定权重大小,有效解决新旧服务器性能不均带来的请求分配问题;

upstream weight {
  server 191.168.1.11 weight=1;
  server 192.168.1.22 weight=2;
}

3. ip_hash
每个请求按客户端IP的hash结果分配,当新的请求到达时,先将其客户端ip通过哈希算法得出一个值,在随后的客户端请求中,客户IP的哈希值只要相同,就会被分配到同一台服务器。
该调度算法可以解决动态网页的session共享问题,但有时会导致请求分配不均,因为国内大多数都是NAT上网模式,多个客户端对应一个外部IP,所以这些客户端都会被分配到同一个节点服务器,从而导致请求分配不均。
LVS负载均衡的-p参数、Keepalived配置里的persisitence_timeout 50参数都类似Nginx的ip_hash参数,其功能都可以解决动态网页的session共享问题。

upstream iphash {
  ip_hash;
  server 192.168.1.11;
  server 192.168.1.22:8080;
  #ip_hash中,后端服务器在负载均衡调度中的状态不能有 weight 和 backup,有也不会生效
}



动态调度算法: 即负载均衡器会根据后端节点的当前状态来决定是否分发请求,如连接数少或响应时间短的优先获得请求。如 fair、least_conn

4. fair
根据后端节点服务器的响应时间来分配请求,响应时间短的优先分配。这是更加智能的调度算法。
Nginx本身不支持这种算法,需要upstream_fair模块;

upstream fair {
  server 192.168.1.11;
  server 192.168.1.22;
  fair;
}

5. least_conn
根据后端节点的连接数来决定分配情况,那个机器少就分发;

6.url_hash
根据访问URL的hash结果来分配请求的,让每个URL定向到同一个后端服务器,后端服务器为缓存服务器时效果显著。
Nginx本身不支持url_hash,需要hash模块软件包;

upstream urlhash {
  server hahaha1:5678;
  server hahaha2:5678;
  hash $request_uri;
  hash_method md5;
  #同样不能使用 weight、backup
}

7. 一致性hash
一致性hash算法一般用于代理后端业务为缓存服务器(如Memcached)的场景,通过将用户请求的URI或者指定字符串进行计算,然后调度到后端的服务器上,此后任何用户查找同一个URI货值指定字符串都会被调度到这一台服务器上,因此后端的每个节点缓存的内容都是不同的;

upstream {
  consistent_hash $request_uri;
  server xxx;
  server xxx;
}


Nginx proxy模块

ngx_http_proxy_pass

Syntax:proxy_pass URL;
Default:—
Context:location, if in location, limit_except

proxy_pass介绍

proxy_pass指令属于ngx_http_proxy_module模块,此模块可以将请求转发到另一台服务器,在实际的反向代理工作中,会通过location功能匹配指定的URI,然后把接收到服务匹配URI的请求通过proyx_pass抛给定义好的upstream节点池。

location /download/ {
  proxy_pass http://download/vedio/;
}
#这是前端代理节点的设置
#交给后端upstream为download的节点

location /name/ {
  rewrite /name/([^/]+) /users?name=$1 break;
  proyx_pass http://127.0.0.1;
}

http_proyx模块参数

ngx_http_proxy_module参数
Nginx的代理功能是通过http_proxy模块来实现的。http_proxy模块参数举例:

proxy模块参数 说明
proxy_next_upstream 什么情况下将请求传递到下一个upstream
proxy_limite_rate 限制从后端服务器读取响应的速率
proyx_set_header 设置http请求header传给后端服务器节点,如:可实现让代理后端的服务器节点获取访问客户端的这是ip
client_body_buffer_size 客户端请求主体缓冲区大小
proxy_connect_timeout 代理与后端节点服务器连接的超时时间
proxy_send_timeout 后端节点数据回传的超时时间
proxy_read_timeout 设置Nginx从代理的后端服务器获取信息的时间,表示连接成功建立后,Nginx等待后端服务器的响应时间
proxy_buffer_size 设置缓冲区大小
proxy_buffers 设置缓冲区的数量和大小
proyx_busy_buffers_size 用于设置系统很忙时可以使用的proxy_buffers大小,推荐为proxy_buffers*2
proxy_temp_file_write_size 指定proxy缓存临时文件的大小




Nginx负载均衡配置

配置后端节点

这儿是upstream后端节点:

vi /etc/nginx/nginx.conf

server {
  listen 80;
  root /path/xxx;

  location / {
    xxxx;
  }
}

Nginx负载均衡反向代理节点

这是代理节点:

upstream test {
  server test1 weight=1;
  server test2 weight=1;
  #域名做hosts
}
vi /etc/nginx/nginx.conf

server {
  listen 8888;
  server_name www.test.com www.xx.com;

  location  / {
    proxy_read_timeout 10s;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_404;
    proyx_pass http://test;#把用户的请求反向代理定义的upstream服务器池

    #proyx_set_header Host $host;在代理后端服务器发送的http请求头中加入host字段信息
    #proxy_set_header $remote_addr;后端节点服务器日志获取客户端真实ip,否则全都是代理节点的ip
    #proyx_connect_timeout 30s;
    #proxy_buffers_size 4m;
    #xxx    
}
  xxxxx
}
与反向代理配置相关的更多参数

除了具有多虚拟主机代理以及节点服务器记录真实用户ip的功能外,Nginx还提供了相当多的作为反向代理和后端节点服务器对话的相关控制参数。

由于参数众多,建议把这些参数都写到另外一个配置文件里,然后用 include 方式包含到虚拟主机配置文件里。其他Nginx参数也同样可以使用此方法。

proxy.conf

proxy_set_header Host $host;
proxy_set_header $remote_addr;
proxy_connect_timeout 60s;
proxy_read_timeout 10s;
proxy_send_timeout 20s;
proxy_buffer_size 4m;
proxy_buffer_size 4 2m;
proxy_temp_file_write_size 2m;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_404

virtual.conf

server {
  listen 80;
  server_name www.test.com www.xxx.com;
  location / {
    include /etc/nginx/proxy.conf;
  }
}

proxy_next_upstream参数补充

当Nginx接收后端服务器发返回的proxy_next_upstream参数定义的状态码时,会将这个请求转发给正常工作的后端服务器,如500、502、503,此参数可以提升用户访问体验。

proyx_next_upstream error timeout invalid_header http_500 http_503 http_502 http_504;


根据URL中的目录地址实现代理转发

根据URL中的目录地址实现代理转发说明

通过Nginx实现动静分离,即通过Nginx反向代理配置规则实现让动态资源和静态资源及其他业务分别由不同的服务器解析,已解决网站性能、安全、用户体验等重要问题。


动静态分离网站集群架构


动静态分离配置文件

配置upstream.conf

upstream static {
  server 192.168.1.11;
  #或server static.com----hosts:static.com 192.168.1.11
}

upstream upload {
  server 192.168.1.22;
}

upstream default {
  server 192.168.1.33;
}


#在http中加入,注意位置
http {
  include upstream.conf;
}

配置virtual.conf

#方案1:利用location实现
location  /static/ {
  proyx_pass http://static;
  include proyx.conf;
}

location /upload/ {
  proxy_pass http://upload;
  include proxy.conf;
}

location / {
  proxy_pass http://default;
  include proxy.conf;
}

========================================
#方案2:利用if语句实现
if ($request_uri ~* "^/static/(.*)$")
{
      proxy_pass http://static/$1;
}

if ($request_uri ~* "^/upload/(.*)$")
{
    proxy_pass http://upload/$1;
}

location / {
  proxy_pass http://default;
  include  proyx.conf;
}


URL目录地址转发的应用场景

根据HTTP的URL进行转发的应用情况,被称为 第7层(应用层)的负载均衡;而LVS的负载均衡一般用于TCP等的转发,因此被称为第四层(传输层)的负载均衡

有时因为需求,需要在代理服务器上通过配置规则,使得匹配不同规则的请求会交给不同的服务器池处理。


根据客户端的设备(user_agent)转发

为了让不同客户端设备用户有更好的访问体验,需要在后端架设不同服务器来满足不同的客户端访问。如PC端和移动端,移动端又有安卓、苹果、Pad等。

1. 常规4层负载均衡解决方案架构
在常规4层负载均衡架构下,可以使用不同的域名来实现这个需求。
如,分配移动端访问 wap.xxx.com,PC端访问www.xxx.com
通过不同域名来引导用户到指定后端服务器,但是这样就分别得记住不同的域名。

2. 第7层负载均衡解决方案
在7层负载均衡架构下,对外只需要用一个域名,如www.xxx.com,然后通过获取用户请求中的设备信息$http_user_agent,根据此信息转给后端合适的服务器处理。

根据$user_agent转发配置

栗子:

location / {
  if ($http_user_agent ~* "android")
      {
          proxy_pass http://android;
      }

if ($http_user_agent ~* "iphone")
      {
          proxy_pass http://iphone;
      }
proxy_pass http://default;
include proyx.conf;


根据文件扩展名实现代理转发

栗子:

#locaiton
location ~*  .*\.(gif|jpg|png|css|js)$ {
  proyx_pass http://static;
  include proxy.conf;
}

#if
if ($request_uri ~* ".*\.php$")
  {
      proxy_pass http://php;
  }
if ($request_uri ~* ".*\.(jpg|png|css|js)$")
  {
      proxy_pass http://static;
  }

在开发无法通过程序实现动静分离的时候,运维可以根据资源实体进行动静分离,根据不同实现策略制定后端服务器不同的组。在前端代理服务器上通过路径、扩展名等进行规则匹配,从而实现请求的动态分离。




Nginx负载均衡检测节点状态

淘宝技术团队开发了一个Tengine(Nginx分支)模块nginx_upstream_check_module
https://github.com/yaoweibin/nginx_upstream_check_module,用于提供主动式后端服务器健康检查。
通过它检测后端realserver的健康状态,如果后端节点不可用,则所有的请求就不会转发到该节点上。

Nginx需要通过打补丁的方式将该模块添加进去。源码安装才行的哈。

安装:

wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master
unzip master
cd nginx_upstream_check_module-master    #解压后的文件夹

cd  nginx源码安装包(我是 /usr/local/nginx-1.12.1)
patch -p1 < ../nginx_upstream_check_module-master/check_1.12.1+.patch    #选择对应的Nginx版本号,我的是1.12.1    #打补丁

#编译,注意以前的编译参数
./configure --prefix=/usr/local/nginx \
--user=nginx --group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-pcre \
--add-module=../nginx_upstream_check_module-master

make    
#给已经安装的Nginx系统打补丁不用执行make install
#make是重新生成Nginx二进制启动命令

#备份
mv /usr/local/nginx/sbin/nginx{,.bak}

#经打过补丁的Nginx二进制程序复制到/usr/local/nginx/sbin/ 下
cp /usr/local/nginx-1.12.1/objs/nginx /usr/local/nginx/sbin/

nginx -t

配置nginx_upstream_check

配置upstream.conf:

upstream zhang {
    server 192.168.1.7:5678 weight=1;
    server 192.168.0.99:5678 weight=1;
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
    #每个3秒对负载均衡中所有节点检测一次,请求2次正常标记realserver状态为up;
    #如果检测5次都失败,则标记realserver状态为down,超时时间为1秒;
    #检查的协议为HTTP;
}

配置/status:

location /status {
    check_status;
    access_log off;
    allow 192.168.1.0/24;
    deny all;
        }

效果图:

效果图

而我的前端节点中 check interval=3000 rise=2 fall=5 timeout=1000 type=http; 这个使用的话,我的upstream无法生效,注释后就没问题。
不知道是不是因为我的upstream后端节点是通过yum安装的nginx,没有安装http_upstream_check_module。
所以此处我的Status是down。