Nginx集群

精品教程 2019-04-29 14 次浏览 0 条评论

Nginx负载均衡

一、集群介绍

1、传统web访问模型

传统WEB模型

(1)传统web访问模型完成一次请求的步骤

1)用户发起请求
2)服务器接受请求
3)服务器处理请求(压力最大)
4)服务器响应请求

(2)传统模型缺点

单点故障;
单台服务器资源有限(客户端则是无限的);
单台服务器处理耗时长(客户等待时间过长);

(3)传统模型优化——单点故障解决方案

  • 优化方案一:部署一台备份服务器,宕机直接切换
    该方案可以有效解决服务器故障导致的单点故障,但且服务器利用率低、成本高,切换不及时,且无法解决服务器业务压力问题。
  • 优化方案二:部署多台服务器,根据DNS的轮询解析机制去实现用户分发
    优势是用户处理速度得到了提升,但是当其中一台故障,dns并不会知道它故障了,依然将请求分给这个服务器,导致一部分用户访问不了业务。

2、并行处理解决方案

1)DNS轮询解析方案
2)多机阵列——集群模式

阵列集群

图中,前面两台服务器负责接受请求和分发请求,它自己并不处理请求,将请求分发给后面的业务服务器来处理。业务服务器处理完请求后,将请求发还给分发器,再由分发器将请求发送给客户,因此分发器还承担了响应请求的任务。
由此可见之前传统模型中服务器端需要承担的服务器接收请求和响应请求都交给分发器处理了,而业务压力最大的处理请求则交给业务服务器完成。
分发器和dns虽然都是进行了分发的工作,但不同点在于分发器是自己部署的服务器,而DNS都是使用的运营商的,因此可以调整分发器的逻辑判断规则。

3、集群

  • 计算机集群简称集群,是一种计算机系统, 它通过一组松散集成的计算机软件或硬件连接起来高度紧密地协作完成计算工作。在某种意义上,他们可以被看作是一台计算机。 (百度解释)
  • 将多个物理机器组成一个逻辑计算机,实现负载均衡和容错。

组成要素:
1)VIP: 给分发器的一个虚IP
2)分发器:nginx
3)数据服务器:web服务器

4、Nginx集群原理

在Nginx集群中Nginx扮演的角色是:分发器。
任务:接受请求、分发请求、响应请求。
功能模块:
1)ngx_http_upstream_module:基于应用层(七层)分发模块
2)ngx_stream_core_module:基于传输层(四层)分发模块(1.9开始提供该功能)

(1)Nginx集群的实质

Nginx集群其实是:虚拟主机+反向代理+upstream分发模块组成的。
虚拟主机:负责接受和响应请求。
反向代理:带领用户去数据服务器拿数据。
upstream:告诉nginx去哪个数据服务器拿数据。

(2)数据走向(请求处理流程)

1)虚拟主机接受用户请求
2)虚拟主机去找反向代理(问反向代理去哪拿数据)
3)反向代理让去找upstream
4)upstream告诉一个数据服务器IP
5)Nginx去找数据服务器,并发起用户的请求
6)数据服务器接受请求并处理请求
7)数据服务器响应请求给Nginx
8)Nginx响应请求给用户

二、使用Nginx分发器构建一个WEB集群

1、环境准备

实验机 : Vmware 虚拟机 2核4G
网卡:桥接
系统:centos7.5
防火墙:关闭
Selinux:关闭
网段:192.168.31.0/24
准备四台实验机:都安装nginx服务,两台当作分发器,两台当作web服务器。

主机名 IP 角色
Master.ayitula.com 192.168.31.40 主分发器
Backup.ayitula.com 192.168.31.41 备分发器
Web01.ayitula.com 192.168.31.42 数据服务器1
Web02.ayitula.com 192.168.31.43 数据服务器2

2、配置web业务机器

(1)nginx安装脚本

#!/bin/bash

nginx_pkg='nginx-1.5.1.tar.gz'
nginx_prefix=/usr/local/nginx
html=/var/nginx
log=/var/log/nginx

check13 () {
   [ $UID -ne 0 ] && echo "need to be root to that" && exit 1
   [ ! -f $nginx_pkg ]  && echo "not found source packager" && exit 1
   [ ! -d $html ] && mkdir -p $html
   [ ! -d $log ] && mkdir -p $log
}

nginx_install () {
   source_pkg=`echo $nginx_pkg|awk -F ".tar" '{print $1}'` 
   [ -d /usr/src/$source_pkg ]&&rm -rf /usr/src/$source_pkg
   tar xf $nginx_pkg -C /usr/src
   cp nginxd /usr/src/$source_pkg
    if [ $? -eq 0 ];then
        cd /usr/src/$source_pkg
        if [ $? -eq 0 ];then
            yum -y install gcc-* pcre pcre-devel zlib zlib-devel openssl-* &> /dev/null
            [ $? -ne 0 ]&&"YUM set error" && exit 1
            ./configure --prefix=$nginx_prefix
            if [ $? -eq 0 ];then
                make
                if [ $? -eq 0 ];then
                    make install
                    if [ $? -eq 0 ];then
                        ln -s -f $nginx_prefix/conf/nginx.conf /etc/
                        ln -s -f $nginx_prefix/logs/ $log/logs
                        ln -s -f $nginx_prefix/html $html/html
                        ln -s -f $nginx_prefix/sbin/ /usr/sbin/
                        cp nginxd /etc/init.d/nginx;chmod 755 /etc/init.d/nginx
                    else
                                            exit 1
                    fi
                else
                    exit 1
                fi
            else    
                exit 1
            fi
        else
            exit 1
        fi
    else
        exit 1
fi
 [ $? -eq 0 ]&&clear||exit
   echo -e "\n\033[32m Nginx Install Success: \033[0m"
   echo -e "\n"
   echo -e "\tNginx_conf: /etc/nginx.conf"
   echo -e "\tNginx_html: $html/html"
   echo -e "\tNginx_access_log: $log/logs/access.log"
   echo -e "\tNginx_error_log: $log/logs/error.log\n\n\n\n"
   read -n1 -p "press any key and exit...."
   echo 
}

check13
nginx_install

(2)配置web服务器操作

[[email protected] ~]# sh nginx_install     # 脚本安装nginx
[[email protected] ~]# echo web02 > /usr/local/nginx/html/index.html    # 写入页面
[[email protected] ~]# yum -y install elinks &>/dev/null    # 安装文本浏览器
[[email protected] ~]# /usr/local/nginx/sbin/nginx      # 启动nginx
[[email protected] ~]# elinks http://localhost -dump
    web02

3、配置分发器(轮询方式分发)

# 清除空行和注释项
$ sed -i '/#/d'  nginx.conf
$ sed -i '/^$/d'  nginx.conf

# 配置nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    upstream web{    # 名为web的反向代理群组
        server 192.168.31.42;
        server 192.168.31.43;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web;   # 去找反向代理
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

4、集群分发测试(默认轮询)

客户端访问分发器地址,默认按照轮询的方式来进行分发。

[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web01
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web01
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02

三、Nginx分发算法

集群分发算法:如何将用户请求按照一定的规律分发给业务服务器。主要分为Nginx集群默认算法和基于请求头分发算法。

1、Nginx集群默认算法

upstream module
nginx的upstream 目前支持4种方式的分配
(1)轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
(2)weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
(3)ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务,好处是可以解决session的问题。
因此前两种只能处理静态页面,而这种方式可以处理动态网站。
(4)fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
(5)url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务 ,后端服务器为缓存时比较有效。

2、Nginx业务服务器状态

每个设备的状态设置参数:

  • down
    表示当前的server暂时不参与负载;
  • weight
    默认为1,weight越大,负载的权重就越大;
  • max_fails
    允许请求失败的次数默认为1,当超过最大次数时,返回proxy_next_upstream模块定义的错误;
  • fail_timeout
    失败超时时间,在连接Server时,如果在超时时间之内超过max_fails指定的失败次数,会认为在fail_timeout时间内Server不可用,默认为10s
  • backup
    其他所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。

3、Nginx集群默认算法测试

集群环境与之前完全相同。

主机名 IP 角色 系统 配置
Master.ayitula.com 192.168.31.40 主分发器 centos7.5 2核4G
Backup.ayitula.com 192.168.31.41 备分发器 centos7.5 2核4G
Web01.ayitula.com 192.168.31.42 数据服务器1 centos7.5 2核4G
Web02.ayitula.com 192.168.31.43 数据服务器2 centos7.5 2核4G

(1)轮询算法分发

upstream web {
    server 192.168.31.42; 
    server 192.168.31.43;
}
server {
    listen 80;
    server_name localhost; 
    location / {
        proxy_pass http://web;
    } 
}

前面已经测试验证了轮询算法分发。配置backup参数如下所示:

upstream web {
    server 192.168.31.42 weight=1; 
    server 192.168.31.43 weight=1 backup;
}
server {
    listen 80;
    server_name localhost; 
    location / {
        proxy_pass http://web;
    } 
}

关停第一个节点情况,访问尝试:

[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02

(2)基于权重的分发

upstream web {
    # 设置权重比例1:2
    server 192.168.31.42 weight=1; 
    server 192.168.31.43 weight=2;
}
server {
    listen 80;
    server_name localhost; 
    location / {
        proxy_pass http://web;
    } 
}

访问测试验证:

[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web01
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web01
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02

通过权重比例的分配,可以让性能更强的服务器承担处理更多的请求。

(3)基于ip_hash分发

ip_hash算法能够保证来自同样源地址的请求都分发到同一台主机。
需要注意:ip_hash算法不支持backup、weight设置。默认权重为1。

upstream web { 
    ip_hash;    # 指定ip_hash即可,默认weight权重比例1: 1
    server 192.168.31.42;
    server 192.168.31.43; 
}
server { 
    listen 80;
    server_name localhost; 
    location / {
        proxy_pass http://web;
    } 
}

访问测试验证:

# 源ip固定
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
[[email protected] ~]# elinks http://192.168.31.40 -dump 
    web02
MacBook-Pro:~ hqs$ elinks http://192.168.31.40 -dump 
    web01
MacBook-Pro:~ hqs$ elinks http://192.168.31.40 -dump 
    web01
MacBook-Pro:~ hqs$ elinks http://192.168.31.40 -dump 
    web01

四、Nginx基于请求头的分发

前面的分发方式都是基于一个集群分发的,而基于请求头分发一般都是用于多集群分发的。
浏览器开发者工具network工具下,获取请求的请求头信息如下所示:

Request URL: http://192.168.31.43/     # 请求的URL
Request Method: GET              # 请求的方法
Status Code: 200 OK
Remote Address: 192.168.31.43:80
Referrer Policy: no-referrer-when-downgrade         # 请求的策略

Response headers    # 响应头
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 6
Content-Type: text/html
Date: Fri, 26 Oct 2018 12:43:02 GMT
ETag: "5bd3014d-6"
Last-Modified: Fri, 26 Oct 2018 11:58:05 GMT
Server: nginx/1.15.5

Request Headers    # 请求头
GET /index.php HTTP/1.1      # 请求方法是GET;域名后面的部分就是路径,默认是‘/’;使用的HTTP协议是1.1 
Host: 192.168.31.43    # 访问的域名(域名或IP均可)
Connection: keep-alive    # 长连接
Upgrade-Insecure-Requests: 1    
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36   # 用户浏览器的类型
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8   # 可以接受的数据类型
Accept-Encoding: gzip, deflate      # 压缩
Accept-Language: zh-CN,zh;q=0.9     # 语言

1、基于host分发

基于host分发这种分发方式适用于多集群分发。例如:一个公司有多个网站,每个网站就是一个集群。

http {
    upstream web1 {   # 名为web1的反向代理群组
        server 192.168.31.42;
        server 192.168.31.52;
    }
    upstream web2 {   # 名为web2的反向代理群组
        server 192.168.31.43;
        server 192.168.31.53;
    }
    server {    # web1虚拟主机
        listen 80;
        server_name www.web1.com;    # 基于域名分发必须有域名
        location / {
            proxy_pass http://web1; 
        }
    }
    server {    # web2虚拟主机
        listen 80;
        server_name www.web2.com;    # 基于域名分发必须有域名 
        location / {
            proxy_pass http://web2; 
        }
    }
}

基于域名的分发测试:

[[email protected] ~]# elinks http://www.web1.com -dump 
    web01
[[email protected] ~]# elinks http://www.web1.com -dump 
    web01
[[email protected] ~]# elinks http://www.web1.com -dump 
    web01
[[email protected] ~]# elinks http://www.web2.com -dump 
    web02
[[email protected] ~]# elinks http://www.web2.com -dump 
    web02
[[email protected] ~]# elinks http://www.web2.com -dump 
    web02

2、基于开发语言分发

这种分发方式适用于混合开发的网站,某些大型网站既有php也有jsp,就可以基于开发语言分发。

# 192.168.31.40分发器上nginx配置
http {
    upstream php {
        server 192.168.31.42; 
    }
    upstream html {
        server 192.168.31.43;
    }
    server {
        location ~* \.php$ {    # 以php结尾的
            proxy_pass http://php;
        } 
        location ~* \.html$ {   # 以html结尾的
            proxy_pass http://html;
        }
    }
}

测试验证:

# 安装php环境
$ yum install httpd  php   # 安装apache和php
# 启动apache,自带php
$ systemctl start httpd
# 编写php文件
$  echo "<?php phpinfo(); ?>" > /var/www/html/index.php
# 访问192.168.31.40/index.php 可以看到php-info信息页面
# 访问192.168.31.40/index.html 可以看到web02

3、基于浏览器的分发

这种基于浏览器的分发,常应用于PC端和移动端区分或浏览器适配。

(1)由于没有第三个机器,在42这台业务服务器上,启动一个虚拟主机。

$ vim /usr/local/nginx/conf/nginx.conf
http {
    server {
        listen   80;
        server_name localhost;
        location / {
            root  html;
            index  index.html  index.htm;
        }
    }
    server {
        listen  81;
        server_name  localhost;
        location / {
            root  web3;
            index  index.html  index.htm
        }
    }
}
$ mkdir /usr/local/nginx/web3
$ echo web03 > /usr/local/nginx/web3/index.html
$ /user/local/nginx/sbin/nginx

(2)基于浏览器分发的分发器配置

upstream elinks { server 192.168.31.42; }
upstream chrome { server 192.168.31.43; }
upstream any { server 192.168.31.42:81; }
server {
    listen 80;
    server_name www.web1.com;
    location / {
        proxy_pass http://any;
        if ( $http_user_agent ~* Elinks ) {
            proxy_pass http://elinks;
        }
        if ( $http_user_agent ~* chrome ) {
            proxy_pass http://chrome;
        }
    }
}

(3)访问测试

任何机器访问,只要是使用elinks访问的都将访问web01;只要使用chrome浏览器访问都将访问web02;而使用firefox或者safari则将访问web03.

4、基于源IP分发

像腾讯新闻、58同城等等网站,往往在什么地方登陆则获取哪个地方的数据。服务器通过源IP匹配判断,从对应的数据库中获取数据。

(1)geo模块

Nginx的geo模块不仅可以有限速白名单的作用,还可以做全局负载均衡,可以要根据客户端ip访问到不同的server。
geo指令是通过ngx_http_geo_module模块提供的。默认情况下,nginx安装时是会自动加载这个模块,除非安装时人为的手动添加--without-http_geo_module。
ngx_http_geo_module模块可以用来创建变量,其值依赖于客户端IP地址。

(2)基于源IP分发配置

upstream bj.server {
    server 192.168.31.42;    # web01
}
upstream sh.server {
    server 192.168.31.43;      # web02
}
upstream default.server {
    server 192.168.31.42:81;      # web03
}
geo $geo {       # IP库
    default default;
    192.168.31.241/32 bj;    # 北京
    192.168.31.242/32 sh;   # 上海
}
server {
    listen  80;
    server_name   www.web1.com;

    location / {
        proxy_pass http://$geo.server$request_uri;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *