nginx 反向代理(proxy)与负载均衡(upstream)应用实践

集群介绍

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

集群特点

1. 高性能

一些国家重要的计算密集型应用(天气预报,核试验模拟等),需要计算机有很强的运算处理能力。以现在的技术水平,即使是大型机,其计算能力也是有限的,很难单独完成此任务。因为计算时间可能会相当长,也许几天,甚至几年或更久。因此,对于这类复杂的计算业务,便使用了计算机集群技术,集中几十上百台,甚至成千上万台计算机进行计算。

假如你现在有一个lnmp环境,每次只需要服务10个并发请求,那么单台服务器一定会比多个服务器集群要快,只有当并发或总请求数量超过单台服务器的承受能力时,服务器集群才会体现出优势。

2. 价格有效性

一套系统集群架构,只需要几台或者树十台服务器主机即可,与动辄价值上百万元的专用超级计算机相比便宜了很多。在达到同样性能需求的条件下,采用计算机集群架构比采用同等运算能力的大型计算机具有更高的性价比。

3. 可伸缩性

当服务负载,压力增大时,针对集群系统进行简单的扩展即可满足需求,且不会降低服务质量。

通常情况下,硬件设备若想扩展性能,不得不增加新的cpu和存储设备,如果加不上去了,就不得不增加新的cpu和存储设备,如果加不上去了,就不得不购买更高性能的服务器。如果采用集群技术,则只需要将新的单个服务器加入到现有集群架构中即可,从访问的客户角度来看,系统服务无论是连续性还是性能上都几乎没有变化,系统在不知不觉中完成了升级,加大了访问能力,轻松地实现了扩展。集群系统中的节点树木可以增长到几千乃至上万个,其伸缩性远超过单台超级计算机。

4. 高可用性

单一的计算机系统总会面临设备损毁的问题,如cpu,内存,主板,电源,硬盘等,只要一个部件坏掉,这个计算机系统就可能会宕机,无法正常提供服务,在集群系统中,尽管部分硬件和软件也还是会发生故障,但整个系统的服务可以是7*24可用的。

集群架构技术可以使得系统在若干硬件设备故障发生时仍可以继续工作,这样就将系统的停机时间减少到了最小。集群系统在提高系统可靠性的同时,也大大减小了系统故障带来的业务损失。

5. 透明性

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

6. 可管理性

整个系统可能在物理上很大,但其实容易管理,就像管理一个单一映像系统一样,可通过ssh服务管理

7. 可编程性

在集群系统上,容易开发及修改各类应用程序

集群的分类

集群架构按功能和结构可以分成如下几类
负载均衡集群 load balancing clusters,简称lbc或者lb
高可用性集群 high-availability clusters,简称hac
高性能计算集群 high-performance clusters,简称hpc
网络计算 grid computing集群

1. 负载均衡集群

负载均衡群集为企业提供了更为实用,性价比更高的系统架构解决方案,负载均衡集群可以把很多客户集中的访问请求压力尽可能平均地分摊在计算机集群中处理,客户访问请求负载通常包括应用程序处理负载和网络流量负载。这样的系统非常适合使用同一组应用程序为大量用户提供服务的模式,每个节点都可以承担一定的访问请求负载压力,并且可以实现访问请求在各节点之间动态分配,以实现负载均衡。

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

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

 

2. 高可用集群 

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

当集群中的一个节点系统发生故障时,运行着的集群服务会迅速作出反应,将该系统的服务分配到集群中其他正在工作的系统上运行。考虑到计算机硬件和软件的容错性,高可用性集群的主要目的是使整体服务尽可能可用。如果高可能性集群中的主节点发生了故障,那么这段时间内将由备节点代替它。备节点会完全接管主节点(包括ip地址及其他资源)提供服务。

高可用性集群使服务器系统的运行速度和响应速度会尽可能的快。他们经常利用在多台机器上运行的冗余节点和服务来相互跟踪,如果某个节点失败,它的替补者将在几秒钟或更短时间内接管它的职责。

  • 当一台机器宕机时,另外一台机器接管宕机的机器的ip资源和服务资源,提供服务
  • 常用于不易实现负载均衡的应用,比如负载均衡器,主数据库,

3. 高性能计算集群

高性能计算集群也称并行计算。通常,高性能计算集群涉及为集群开发的并行应用程序,以解决复杂的科学问题(天气预报,石油勘探,核反应模拟等)。高性能计算集群对外就好像一个超级计算机,这种超级计算机内部由数十至上万个独立服务器组成,并且在公共消息传递层上进行通信以运行并行应用程序。

4. 网络计算集群

很少用

开源集群软件产品的选择

nginx负载均衡集群介绍

负载均衡集群提供了一种廉价,有效,透明的方法,来扩展网络设备和服务器的负载,带宽和吞吐量,同时加强了网络数据处理能力,提高了网络灵活性和可用性。

在负载均衡集群中,同组集群的所有计算机节点都应该提供相同的服务,集群负载均衡器会截获所有对该服务的入站请求,然后将这些请求尽可能的平均分配在所有集群节点上。

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

nginx仅仅是作为nginx proxy反向代理使用的,因为这个反向代理功能表现的效果是负载均衡集群的效果,我们就称为nginx负载均衡。

lvs,其实现的功能只是对请求数据包的转发,传递,其中dr模式明显的特征是从负载均衡下面的节点服务器来看,接收到的请求还是来自访问负载均衡器的客户端的真实用户,而反向代理就不一样了,反向代理接收访问用户的请求后,会代理用户重新发起请求代理下的节点服务器,最后把数据返回给客户端用户,在节点服务器看来,访问的节点服务器的客户端用户就是反向代理服务器了,而非真实的网站访问用户。

lvs是转发,仅仅只是帮用户转发,不会替代用户请求,而nginx是接收用户的请求然后重新发起请求去请求后面的节点。

实现nginx负载均衡的组件说明

nginx的功能就是 缓存 反向代理 负载均衡,而其本身就是一个反向代理的软件。lvs是4层,nginx在1.9版本之前是7层,而1.9以上4 7层,haproxy 4 7层。

快速搭建nginx负载均衡环境

编译安装配置文件参考

yum install pcre pcre-devel -y
yum install openssl openssl-devel -y
注意:一般安装软件-devel都是必须安装的
useradd www -s /sbin/nologin -M
cd /joker/
mkdir tools
cd tools/
wget -q http://nginx.org/download/nginx-1.6.3.tar.gz
cd nginx-1.6.3
./configure --user=www --group=www --with-http_ssl_module --with-http_stub_status_module --prefix=/application/nginx-1.6.3/
make
make install
cd /application/nginx-1.6.3/
ln -s /application/nginx-1.6.3/ /application/nginx
/application/nginx/sbin/nginx -t      # 检 查语法
/application/nginx/sbin/nginx         # 启动
/application/nginx/sbin/nginx -V      # 版本
/application/nginx/log/error.log      # 错误日志
netstat -ntlp                         # 查看启动是否成功

[root@Poppy conf]# cat nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
# 关键字,日志文件,错误日志级别
error_log logs/error.log error;
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';   
    # 访问日志,main就是读http里面的main日志变量
    # access_log logs/www_access.log main;
    include extra/*.conf;
}

[root@Poppy extra]# cat bbs.conf 
    server {
        listen       80;
        server_name  bbs.joker.com;
        location / {
            root   html/bbs;
            index  index.html index.htm;
        }
    }

yum安装的nginx配置文件修改参考

[root@joker nginx]# tree 
.
├── conf.d
│   ├── walle.conf.bak
│   └── www.conf
├── default.d
├── fastcgi.conf
├── fastcgi.conf.default
├── fastcgi_params
├── fastcgi_params.default
├── koi-utf
├── koi-win
├── mime.types
├── mime.types.default
├── nginx.conf       配置文件,将conf到conf.d
├── nginx.conf.default
├── scgi_params
├── scgi_params.default
├── uwsgi_params
├── uwsgi_params.default
└── win-utf


[root@joker conf.d]# cat bbs.conf 
server {
    listen       80;
    server_name  bbs.joker.com;

    location / {
        root /data/bbs; # 根目录为web
        index index.html;
    }
}
[root@joker conf.d]# cat blog.conf 
server {
    listen       80;
    server_name  blog.joker.com;

    location / {
        root /data/blog; # 根目录为web
        index index.html;
    }
}

[root@joker data]# cat blog/index.html 
blog sfdsf
[root@joker data]# cat bbs/index.html 
bbs fdfd

作为nginx负载的服务器配置如下,红色区域

[root@Poppy extra]# cat ../nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
# 关键字,日志文件,错误日志级别
error_log logs/error.log error;
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';   
    # 访问日志,main就是读http里面的main日志变量
    # access_log logs/www_access.log main;
    upstream www_server_pools{       # 定义负载名称
        server IP1:80 weight=1;      # 后端服务器地址,后面会详解
        server IP2:80 weight=1;
    }
    include extra/bbs.conf;
    include extra/blog.conf;
}
[root@Poppy extra]# cat bbs.conf 
    server {
        listen       80;
        server_name  bbs.joker.com;
        location / {
           root html/bbs;
           index index.html;
        }
    }
[root@Poppy extra]# cat blog.conf 
    server {
        listen       80;
        server_name  blog.joker.com;
        location / {
            proxy_pass http://www_server_pools;  # 通过proxy_pass模块抛给upstream模块
        }
    }

客户端访问,即使后端服务器有宕机的,也不会影响业务,会自动剔除有故障的后端服务器,基于端口的健康检查

[root@Poppy extra]# curl bbs.joker.com
bbs       访问自己的web服务,没有进行负载
[root@Poppy extra]# curl blog.joker.com
bbs fdfd

nginx负载均衡核心组件介绍

1. nginx upstream模块

ginx的负载均衡功能依赖于ngx_http_upstream_module模块,所支持的代理方式包括proxy_pass,fastcgi_pass,memcached_pass等,新版nginx软件支持的方式有所增加
ngx_http_upstream_module模式允许nginx定义一组或多组节点服务器组,使用可以通过porxy_pass代理方式把网站的请求发送到事先定义好的对应upstream组的名字上。

2. upstream语法

upstream www_server_pools{
upstream是关键字必须要有,后面的www_server_pools为一个upstream集群组的名字,可以自己起名,调用时就用这个名字
server 10.0.0.1 weight=1;
server关键字是固定的,后面可以接域名或ip,如果不指定端口,默认是80端口。weight代表权重,数值越大被分配的请求越多,结尾有分号。
server 10.0.0.2 weight=2;
server 10.0.0.3 weight=3;
}

    upstream www_server_pools{
        server ip1:80 weight=1;
        server ip2:80 weight=2 max_fails=1 fail_timeout=10s backup;   
bakcup就是热备机器 1此连续连续检测失败,间隔10秒再次检测,除了backup都是默认设置的
server www.joker.com:8080 weight=3; 域名
server unix:/tmp/socketfile socket文件 }

特别注意,如果nginx代理的是cache服务,可能需要使用hash算法,此时若宕机,可通过设置down参数确保客户端确保客户端用户按照当前的hash算法访问,这一点很重要。

    upstream www_server_pools{
        ip_hash;
        server ip1:80 down;
        server ip2:80 ;
    }  
hash算法无法使用权重和backup,即使有也不生效

upstream模块的内容应放于nginx.conf配置的http{}标签内,默认调度算法是wrr,即权重轮询

upstream模块调度算法

调度算法一般分为两类,第一类为静态调度算法,即负载均衡器根据自身设定的规则进行分配,不需要考虑后端节点服务器的情况,例如 rr,wrr,ip_hash等都属于静态调度算法

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

1. rr轮询,默认调度算法

接收客户端请求顺序把客户端的请求逐一分配到不同的后端节点服务器

2. wrr权重轮询

权重和用户访问成正比,权重值越大,被转发的请求也就越多,可根据服务器配置来判断权重值

server 10.0.0.1 weight=1;
server 10.0.0.2 weight=2;

如果有30个请求,其中20个会向10.0.0.2访问,剩下的10个向10.0.0.1访问

3. ip_hash

每个请求按客户端ip的hash结果分配,当新的请求到达时,先将其客户端ip通过哈希算法哈希出一个值,在随后的客户端请求中,客户ip的哈希值只要相同,就会被分配至同一台服务器,该调度算法可以解决动态网页的session共享问题,但有时会导致请求分配不均,即无法保证1:1的负载均衡,因为在国内大多数公司都是nat上网模式,多个客户端会对应一个外部ip,所以,这些客户端会被分配到同一个节点服务器,从而导致请求分配不均。

 upstream www_server_pools{
        ip_hash;
        server ip1:80 down;
        server ip2:80 ;
    }  
    hash算法无法使用权重和backup,即使有也不生效

4. fair动态调度算法

此算法会根据后端节点服务器的响应时间来分配请求,响应时间短的优先分配。这是更加智能的调度算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。nginx本身不支持fair调度算法,需下载nginx的相关模块upstream fair。

upstream joker_lb{
server 1.0.0.1;
server 1.0.0.2;
fair;
}

5. least_conn

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

还有2个是url_hash和一致性hash算法,后端服务为缓存服务效果显著。这里就不说了

http_proxy_module模块

nginx代理功能是通过http proxy模块来实现的,默认在安装nginx时已经安装了http proxy模块,因此可以直接使用http proxy模块

 

当用户访问域名时携带www.joker.com请求nginx反向代理服务器,但是反向代理向下面节点重新发起请求时,默认并没有在请求头里告诉节点服务器要找哪台虚拟主机,所以,web节点服务器接收请求后发现没有主机头信息,因此,节点服务器就把第一个虚拟主机发给了反向代理。解决这个问题的方法就是当反向代理向后台重新发送请求时,要携带主机头信息,以明确告诉节点服务器要找哪个虚拟主机。

[root@Poppy extra]# cat blog.conf 
    server {
        listen       80;
        server_name  blog.joker.com;
        location / {
            proxy_pass http://www_server_pools;
            proxy_set_header Host $host;   将携带用户的主机头信息
        }
    }

我们发现,节点的访问日志存储的是代理服务器的ip,也就是负载均衡的机子,我们如何将用户的真实ip获取到呢,如下配置

[root@Poppy extra]# cat blog.conf 
    server {
        listen       80;
        server_name  blog.joker.com;
        location / {
            proxy_pass http://www_server_pools;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr; 注意要与携带请求头设置主机头分开写
        }
    }

注意,反向代理这块配好了,节点服务器上需要的访问日志要记录用户的真实ip,必须进行日志格式配置,这样才能把代理传过来的x-forwarded-for头信息记录下来

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

当然,默认是有的

配置文件可以通过include包含设置,这样看上去跟工整,目录需要注意下

[root@Poppy ~]# cat /application/nginx/conf/extra/blog.conf   
    server {
        listen       80;
        server_name  blog.joker.com;
        location / {
            proxy_pass http://www_server_pools;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
            include proxy.conf;
        }
    }

[root@Poppy ~]# cat /application/nginx/conf/extra/proxy.conf
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_connect_timeout 60;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64;

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

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

此架构图适合网站前端只使用同一个域名提供服务的场景,例如访问www.joker.com,然后当用用户请求www.joker.com/static地址的时候,代理会分配请求到静态服务器池请求数据;当用户请求www.joker.com/upload地址的时候,代理会分配请求到上传服务器池处理数据;当用户请求www.joker.com地址的时候,即不包含上述指定的目录地址路径时,代理会分配请求到默认的动态服务器池请求数据 

  • 用户请求www.joker.com/upload地址时,实现由upload上传服务器池处理请求
  • 用户请求www.joker.com/static地址时,实现由静态服务器池处理请求
  • 除此之外,对于其他的访问请求,全部由默认的动态服务器池处理请求

我们来配置下upstream服务器池,http标签中如下配置

    upstream static_pools {
            server 1:80 weight=1;
        }
        upstream upload_pools {
            server 2:80 weight=1;
        }
        upstream default_pools {
            server 3:80 weight=1;
        }

下面是location的配置,server标签

    server {
        listen       80;
        server_name  blog.joker.com;
        location /static/ {
            proxy_pass http://static_pools;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
        location /upload/ {
            proxy_pass http://upload_pools;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
        location / {
            proxy_pass http://default_pools;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
    
    
    通过用if来判断请求路径然后反向代理,其效果跟上面是一样的
    server {
        listen       80;
        server_name  blog.joker.com;
            
        if ($request_uri ~* "/^static/(.*)$")
            {
                proxy_pass http://static_pools/$1;
            }
        if ($request_uri ~* "/^upload/(.*)$")
            {
                proxy_pass http://upload_pools/$1;
            }
        location / {
                proxy_pass http://default_pools;
                include proxy.conf;
            }
     }

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

1. 根据客户端设备(user_ange)转发实践需求

为了让不同的客户端设备用户有更好的体验,需要在后端架设不同的服务器来满足不同的客户端访问,例如 移动客户端访问网站,就需要部署单独的移动服务器及程序,体验才能更好,而且移动端还分apple,ipad等

在常规4层负载架构下,可以使用不同的域名来实现这个需求,例如 移动端用户访问wap.joker.com,pc端用户访问www.joker.com,此解决方案最大的问题就是不同的客户端需要记住域名,而绝大多数用户只会记住www.joker.com,这样一来就会导致用户体验不是很好。

 

在7层负载均衡架构下,就可以不需要拆分域名了,对外只需要一个域名即可,例如 www.joker.com,然后通过获取用户请求中的设备信息(利用 $http_user_agent获取),根据这些信息转给后端合适的服务器处理。

2. 根据客户端设备(user_ange)转发请求实践

        server {
            listen       80;
            server_name  blog.joker.com;
            location / {
                if ($http_user_agent ~* "MSIE"){
                    proxy_pass http://static_pools;
                }
                if ($http_user_agent ~* "Chrome"){
                    proxy_pass http://upload_pools;
                }
                proxy_pass http://default_pools;
                include proxy.conf
            }
        }

需要特别说明的是,当你无法知道客户端设备字符串名字(例如Chrome,MSIE,Firefox这样的字符串)时,可以先用这些浏览器访问对应的节点服务器,然后根据访问日志中的$http_user_agent格式记录的日志确认。

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

除了根据url路径及user_agent转发外,还可以实现根据文件扩展名进行转发。

        server {
             listen       80;
             server_name  blog.joker.com;
             location ~ .*.(gif|jpg|jpeg|png|bmp|swf|css|js)${
                proxy_pass http://static_pools;
                include proxy.conf
            }
          }
             
        server {
            listen       80;
            server_name  blog.joker.com;
            location / {
            if ($request_uri ~* ".*\.(php|php5)$")
                {
                    proxy_pass http://php_server_pools;
                }
            if ($request_uri ~* ".*\.(jsp|jsp*|do|do*)$")
                {
                    proxy_pass http://java_server_pools;
                }

         }

可以根据扩展名实现资源的动静分离,如图片视频等请求静态服务器池,php,jsp等请求动态服务器池。

节点故障监控

可以通过zabbix等监控节点服务器的web服务状态,起到预警的作用。

 

posted @ 2018-06-14 10:19  liqianlong  阅读(1324)  评论(0编辑  收藏  举报