Nginx负载均衡

前言

早期的网站流量和业务功能都比较简单,单台服务器足以满足基本的需求,但是随着互联网的发展,业务流量越来越大并且业务逻辑也跟着越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器进行性能的水平扩展及避免单点故障出现。

负载均衡就是一种将不同用户的请求流量分发到不同的服务器上的技术

具体作用:

1、解决服务器的高并发压力,提高应用程序的处理性能。

2、提供故障转移,实现高可用。

3、通过添加或减少服务器数量,增强网站的可扩展性。

4、在负载均衡器上进行过滤,可以提高系统的安全性。

实现方式

手动选择

这种方式比较原始,只要实现的方式就是在网站主页上面提供不同线路、不同服务器链接方式,让用户来选择自己访问的具体服务器,来实现负载均衡。

DNS轮询

大多域名注册商都支持对同一个主机名添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就能完成简单的负载均衡。DNS轮询的成本非常低,在一些不重要的服务器,被经常使用。

相当于一个域名绑定多个ip,让DNS进行轮询。

但是这种方式有两个缺点:

  1. 可靠性低
  2. 负载均衡不均衡

四/七层负载均衡

所谓四层负载均衡指的是OSI七层模型中的传输层,主要是基于IP+PORT的负载均衡

1
2
3
实现四层负载均衡的方式:
硬件:F5 BIG-IP、Radware等
软件:LVS、Nginx、Hayproxy等

所谓的七层负载均衡指的是在应用层,主要是基于虚拟的URL或主机IP的负载均衡

1
2
实现七层负载均衡的方式:
软件:Nginx、Hayproxy等

四层和七层负载均衡的区别:

  • 四层负载均衡数据包是在底层就进行了分发,而七层负载均衡数据包则在最顶端进行分发,所以四层负载均衡的效率比七层负载均衡的要高。
  • 四层负载均衡不识别域名,而七层负载均衡识别域名。

Nginx七层负载均衡

Nginx要实现七层负载均衡需要用到proxy_pass代理模块配置。Nginx默认安装支持这个模块,我们不需要再做任何处理。Nginx的负载均衡是在Nginx的反向代理基础上把用户的请求根据指定的算法分发到一组【upstream虚拟服务池】。

1590248160635

相关指令
upstream指令

upstream:该指令是用来定义一组服务器,它们可以是监听不同端口的服务器,并且也可以是同时监听TCP和Unix socket的服务器。服务器可以指定不同的权重,默认为1。

upstream name {...}

server指令

server:该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端口或者unix socket

server name [paramerters]

具体实现
  1. 设置三台服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    server {
    listen 9001;
    server_name localhost;
    default_type text/html;
    location /{
    return 200 '<h1>192.168.200.146:9001</h1>';
    }
    }
    server {
    listen 9002;
    server_name localhost;
    default_type text/html;
    location /{
    return 200 '<h1>192.168.200.146:9002</h1>';
    }
    }
    server {
    listen 9003;
    server_name localhost;
    default_type text/html;
    location /{
    return 200 '<h1>192.168.200.146:9003</h1>';
    }
    }
  2. 配置负载均衡器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    upstream backend{
    server 192.168.200.146:9091;
    server 192.168.200.146:9092;
    server 192.168.200.146:9093;
    }
    server {
    listen 8083;
    server_name localhost;
    location /{
    proxy_pass http://backend;
    }
    }

    代理服务器在负责均衡调度中的状态设置有以下几个:

    状态 概述
    down 将该服务器标记为永久不可用,那么该代理服务器将不参与负载均衡。(该状态一般会对需要停机维护的服务器进行设置。)
    backup 预留的备份服务器,默认为10秒,写法fail_timeout=time
    max_fails 允许请求失败的次数,默认为1,写法max_fails=number
    fail_timeout 经过max_fails失败后, 服务暂停时间
    max_conns 限制最大的接收连接数,默认为0,写法max_conns=number

    负载均衡策略配置

    算法名称 说明
    轮询 每个请求会按时间顺序逐个分配到不同的后端服务器。轮询不需要额外的配置。默认方式
    weight 用来设置服务器的权重,默认为1,权重数据越大,被分配到请求的几率越大;写法weight=number:
    ip_hash 依据ip分配方式,将某个客户端IP的请求通过哈希算法定位到同一台后端服务器上(但是使用ip_hash指令无法保证后端服务器的负载均衡,可能导致有些后端服务器接收到的请求多,有些后端服务器接收的请求少,而且设置后端服务器权重等方法将不起作用。)
    least_conn 依据最少连接方式,把请求转发给连接数较少的后端服务器
    url_hash 依据URL分配方式,按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。
    fair 可以根据页面大小、加载时间长短智能的进行负载均衡(需要添加nginx-upstream-fair
配置案例
案例一:对所有请求实现一般轮询规则的负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
upstream backend{
server 192.168.200.146:9001;
server 192.168.200.146:9002;
server 192.168.200.146:9003;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}
案例二:对所有请求实现加权轮询规则的负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
upstream backend{
server 192.168.200.146:9001 weight=7;
server 192.168.200.146:9002 weight=5;
server 192.168.200.146:9003 weight=3;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}
案例三:对特定资源实现负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
upstream videobackend{
server 192.168.200.146:9001;
server 192.168.200.146:9002;
}
upstream filebackend{
server 192.168.200.146:9003;
server 192.168.200.146:9004;
}
server {
listen 8084;
server_name localhost;
location /video/ {
proxy_pass http://videobackend;
}
location /file/ {
proxy_pass http://filebackend;
}
}
案例四:对不同域名实现负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
upstream itcastbackend{
server 192.168.200.146:9001;
server 192.168.200.146:9002;
}
upstream itheimabackend{
server 192.168.200.146:9003;
server 192.168.200.146:9004;
}
server {
listen 8085;
server_name www.baidu.cn;
location / {
proxy_pass http://baidubackend;
}
}
server {
listen 8086;
server_name www.tenxun.cn;
location / {
proxy_pass http://tenxunbackend;
}
}
案例五:实现带有URL重写的负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upstream backend{
server 192.168.200.146:9001;
server 192.168.200.146:9002;
server 192.168.200.146:9003;
}
server {
listen 80;
server_name localhost;
location /file/ {
rewrite ^(/file/.*) /server/$1 last;
}
location / {
proxy_pass http://backend;
}
}

Nginx四层负载均衡

Nginx在1.9之后,增加了一个stream模块,用来实现四层协议的转发、代理、负载均衡等。stream模块的用法跟http的用法类似,允许我们配置一组TCP或者UDP等协议的监听,然后通过proxy_pass来转发我们的请求,通过upstream添加多个后端服务,实现负载均衡。

添加stream模块的支持

Nginx默认是没有编译这个模块的,需要使用到stream模块,那么需要在编译的时候加上--with-stream

完成添加--with-stream的实现步骤:

  • 将原有/usr/local/nginx/sbin/nginx进行备份
  • 拷贝nginx之前的配置信息
  • 在nginx的安装源码进行配置指定对应模块 ./configure –with-stream
  • 通过make模板进行编译
  • 将objs下面的nginx移动到/usr/local/nginx/sbin下
  • 在源码目录下执行 make upgrade进行升级,这个可以实现不停机添加新模块的功能
Nginx四层负载均衡的指令
stream指令

stream:该指令提供在其中指定流服务器指令的配置文件上下文。和http指令同级。

stream { ... }

upstream指令

upstream:该指令和http的upstream指令是类似的。

配置案例

nginx.conf配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stream {
upstream redisbackend {
server 192.168.200.146:6379;
server 192.168.200.146:6378;
}
upstream tomcatbackend {
server 192.168.200.146:8080;
}
server {
listen 81;
proxy_pass redisbackend;
}
server {
listen 82;
proxy_pass tomcatbackend;
}
}

问题

1604495169905

很明显我们需要两台以上的Nginx服务器对外提供服务,这样的话就可以解决其中一台宕机了,另外一台还能对外提供服务,但是如果是两台Nginx服务器的话,会有两个IP地址,用户该访问哪台服务器,用户怎么知道哪台是好的,哪台是宕机了的?

这里使用Keepalived来解决该问题

Keepalived

Keepalived 软件由 C 编写的,最初是专为 LVS 负载均衡软件设计的,Keepalived 软件主要是通过 VRRP 协议实现高可用功能。

具体实现

  • keepalived的安装
  1. 从官方网站下载keepalived,官网地址https://keepalived.org/

  2. 将下载的资源上传到服务器

    keepalived-2.0.20.tar.gz
    
  3. 创建keepalived目录,方便管理资源

    mkdir keepalived
    
  4. 将压缩文件进行解压缩,解压缩到指定的目录

    tar -zxf keepalived-2.0.20.tar.gz -C keepalived/
    
  5. 对keepalived进行配置,编译和安装

    cd keepalived/keepalived-2.0.20
    ./configure --sysconf=/etc --prefix=/usr/local
    make && make install
    

    安装完成后,有两个文件需要我们认识下,一个是 /etc/keepalived/keepalived.conf(keepalived的系统配置文件,我们主要操作的就是该文件),一个是/usr/local/sbin目录下的keepalived,是系统配置脚本,用来启动和关闭keepalived

    Keepalived配置文件介绍:

    里面会分三部,第一部分是global全局配置、第二部分是vrrp相关配置、第三部分是LVS相关配置(这里没有用到)。

    • global全局部分:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      global_defs {
      #通知邮件,当keepalived发生切换时需要发email给具体的邮箱地址
      notification_email {
      1369281736@qq.com
      }
      #设置发件人的邮箱信息
      notification_email_from 315850054@qq.com
      #指定smpt服务地址
      smtp_server 192.168.200.1
      #指定smpt服务连接超时时间
      smtp_connect_timeout 30
      #运行keepalived服务器的一个标识,可以用作发送邮件的主题信息
      router_id LVS_DEVEL

      #默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)
      vrrp_skip_check_adv_addr
      #严格遵守VRRP协议。
      vrrp_strict
      #在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0
      vrrp_garp_interval 0
      #在一个网卡上每组na消息之间的延迟时间,默认为0
      vrrp_gna_interval 0
      }
    • VRRP部分(该部分可以包含以下四个子模块:vrrp_script、vrrp_sync_group、garp_group、vrrp_instance):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      #设置keepalived实例的相关信息,VI_1为VRRP实例名称
      vrrp_instance VI_1 {
      state MASTER #有两个值可选MASTER主 BACKUP备
      interface ens33 #vrrp实例绑定的接口,用于发送VRRP包[当前服务器使用的网卡名称]
      virtual_router_id 51#指定VRRP实例ID,范围是0-255
      priority 100 #指定优先级,优先级高的将成为MASTER
      advert_int 1 #指定发送VRRP通告的间隔,单位是秒
      authentication { #vrrp之间通信的认证信息
      auth_type PASS #指定认证方式。PASS简单密码认证(推荐)
      auth_pass 1111 #指定认证使用的密码,最多8位
      }
      virtual_ipaddress { #虚拟IP地址设置虚拟IP地址,供用户访问使用,可设置多个,一行一个
      192.168.200.222
      }
      }
  • 对两个nginx服务器的keepalived进行配置

    主服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    global_defs {
    notification_email {
    1369281736@qq.com
    }
    notification_email_from 315850054@qq.com
    smtp_server 192.168.200.1
    smtp_connect_timeout 30
    router_id keepalived1
    vrrp_skip_check_adv_addr
    vrrp_strict
    vrrp_garp_interval 0
    vrrp_gna_interval 0
    }

    vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
    auth_type PASS
    auth_pass 1111
    }
    virtual_ipaddress {
    192.168.200.222
    }
    }

    副服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    global_defs {
    notification_email {
    1369281736@qq.com
    }
    notification_email_from 315850054@qq.com
    smtp_server 192.168.200.1
    smtp_connect_timeout 30
    router_id keepalived2
    vrrp_skip_check_adv_addr
    vrrp_strict
    vrrp_garp_interval 0
    vrrp_gna_interval 0
    }

    vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
    auth_type PASS
    auth_pass 1111
    }
    virtual_ipaddress {
    192.168.200.222
    }
    }
  • 分别启动两台服务器的keepalived

    1
    2
    cd /usr/local/sbin
    ./keepalived

    搭建后的结构:

1604495442179

这样虚拟IP(VIP)会在MASTER节点上,当MASTER节点上的keepalived出问题以后,因为BACKUP无法收到MASTER发出的VRRP状态通过信息,就会直接升为MASTER。VIP也会”漂移”到新的MASTER。

问题优化:

keepalived只能做到对网络故障和keepalived本身的监控,即当出现网络故障或者keepalived本身出现问题时,进行切换。但是这些还不够,我们还需要监控keepalived所在服务器上的其他业务,比如Nginx,如果Nginx出现异常了,仅仅keepalived保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。

实现:

  1. 在keepalived配置文件中添加对应的配置像
1
2
3
4
5
6
vrrp_script 脚本名称
{
script "脚本位置"
interval 3 #执行时间间隔
weight -20 #动态调整vrrp_instance的优先级
}
  1. 编写脚本

ck_nginx.sh

1
2
3
4
5
6
7
8
9
#!/bin/bash
num=`ps -C nginx --no-header | wc -l`
if [ $num -eq 0 ];then
/usr/local/nginx/sbin/nginx
sleep 2
if [ `ps -C nginx --no-header | wc -l` -eq 0 ]; then
killall keepalived
fi
fi

Linux ps命令用于显示当前进程 (process) 的状态。

-C(command) :指定命令的所有进程

–no-header 排除标题

  1. 为脚本文件设置权限
1
chmod 755 ck_nginx.sh
  1. 将脚本添加到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vrrp_script ck_nginx {
script "/etc/keepalived/ck_nginx.sh" #执行脚本的位置
interval 2 #执行脚本的周期,秒为单位
weight -20 #权重的计算方式
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 10
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.111
}
track_script {
ck_nginx
}
}