Nginx七层负载均衡
# Nginx负载均衡基本概述
# 什么是负载均衡
负载均衡Load Balance
,指的是将用户访问请求所产生的流量,进行平衡,分摊到多个应用节点处理。
负载均衡扩展了应用的服务能力,增强了应用的可用性。
# 为什么需要负载均衡
当我们的 Web
服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web
服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的流量分发。从而提升整体性能、以及系统的容灾能力。
# 负载均衡与代理区别
Nginx
负载均衡与Nginx
反向代理不同地方在于:- Nginx代理仅代理一台服务器。
- Nginx负载均衡则是将客户端请求通过
proxy_pass
代理至一组upstream
资源池。
# Nginx负载均衡应用场景
# 四层负载均衡
四层负载均衡指的是 OSI
七层模型中的传输层,四层仅需要对客户端的请求进行TCP/IP
协议的包转发就可以实现负载均衡。
四层负载均衡的性能极好,因为只需要底层进行转发处理,而不需要进行一些复杂的逻辑。
# 七层负载均衡
七层负载均衡工作在应用层,它可以完成很多应用方面的协议请求,比如我们说的http
应用负载均衡,它可以实现 http
头信息的改写、安全应用规则控制、URI
匹配规则控制、及rewrite
等功能,所以在应用层里面可以做的内容就更多了。
# 四层与七层区别
- 四层负载均衡:传输层
- 优点:性能高,数据包在底层就进行了转发
- 缺点:仅支持
ip:prot
转发,无法完成复杂的业务逻辑应用
- 七层负载均衡︰应用层
- 优点:贴近业务,支持
URI
路径匹配、Header
改写、Rewrite
等 - 缺点:性能低,数据包需要拆解到顶层才进行转发
- 优点:贴近业务,支持
# Nginx负载均衡配置场景
Nginx
实现负载均衡需要两个模块:proxy_pass
代理模块upstream
虚拟资源池模块
Syntax : upstream name { ... }
Default: -
Context: http
#upstream例
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.example.com:8080 backup;
}
server {
location / {
proxy_pass http://backend;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 负载均衡场景环境规划
- 负载均衡场景架构图规划
- 负载均衡场景地址规划
角色 | 主机名 | 外网IP(NAT) | 内网IP(LAN) |
---|---|---|---|
Proxy | proxy01 | eth0:10.0.0.5 | eth1:172.16.1.5 |
web01 | web01 | eth0:10.0.0.7 | eth1:172.16.1.7 |
web02 | web02 | eth0:10.0.0.8 | eth1:172.16.1.8 |
# 后端Web节点配置实例
1.Web01
服务器上配置为应用服务节点,创建对应html
文件
[root@web01 ~]# cat /etc/nginx/conf.d/web.birenchong.cn.conf
server {
listen 80;
server_name web.birenchong.cn;
root /web;
location / {
index index.html;
}
}
[root@web01 ~]# mkdir/web
[root@web01 ~]# echo "web01..." > /node/index.html
[root@web01 ~]# systemctl restart nginx
2
3
4
5
6
7
8
9
10
11
12
13
2.Web02
服务器上配置为应用服务节点,创建对应html
文件
[root@web02 ~]# cat /etc/nginx/conf.d/web.birenchong.cn.conf
server {
listen 80;
server_name web.birenchong.cn;
root /web;
location /{
index index.html;
}
}
[root@web02 ~]# mkdir /web
[root@web02 ~]# echo "web02..." > /node/index.html
[root@web02 ~]# systemctl restart nginx
2
3
4
5
6
7
8
9
10
11
12
13
# 前端接入Nginx负载均衡
1.将proxy01配置为负载均衡,将所有请求代理至虚拟资源池
[root@proxy01 ~]# cat /etc/nginx/conf.d/proxy_web.birenchong.cn.conf
upstream web {
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location /{
proxy_pass http://web;
include proxy_params;
}
}
[root@proxy01 ~]# systemctl restart nginx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.准备Nginx
负载均衡需要使用的proxy_params
文件
[root@Nginx ~]# vim /etc/nginx/proxy_params
proxy_http_version 1.1;
peoxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffering on;
proxy_buffer_size 64k;
proxy_buffers 4 64k;
2
3
4
5
6
7
8
9
10
11
12
13
# 浏览器访问测试负载效果
使用浏览器访问web.birenchong.cn
,然后进行刷新测试。
# Nginx负载均衡调度算法
调度算法 | 概述 |
---|---|
轮询 | 按时间顺序逐一分配到不同的后端服务器(默认) |
weight | 加权轮询, weight值越大,分配到的访问几率越高 |
ip_hash | 每个请求按访问IP的hash结果分配,这样来自同一IP的固定访问一个后端服务器 |
least_conn | 将请求传递到活动连接数最少的服务器。 |
# 轮询调度算法
轮询调度算法的原理是将每一次用户的请求,轮流分配给内部中的服务器。
轮询算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。
upstream load_pass {
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 加权轮询调度算法
轮询调度算法没有考虑每台服务器的处理能力,在实际情况中,由于每台服务器的配置、安装的业务应用等不同,其处理能力会不一样。所以,我们根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。
upstream load_pass {
server 172.16.1.7:80 weight=5;
server 172.16.1.8:80 weight=1;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# ip_hash调度算法
ip_hash
是基于用户请求的IP
,对该IP
进行hash
运算,根据hash
运算的值,将请求分配到后端特定的一台节点进行处理。ip_hash
算法实现公式:hash(ip) % node_counts = index
如何配置ip_hash
调度算法
upstream load_pass {
ip_hash;
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ip_hash
调度算法会带来两个问题- 1.如果有大量来自于同一IP的请求会造成某个后端节点流量过大,而其他节点无流量
- 2.如果临时下线一台节点,会出现重新计算hash值,官方建议将下线节点标记为
down
状态,以保留客户端IP地址的当前哈希值。(如下图所示:)
如果有大量的用户调度到某一节点,而该节点刚好故障,则该算法会重新计算结果,从而造成大量的用户被转移其他节点处理,而需要重新建立会话。
# 一致性hash调度算法
为了规避上述hash
情况,一致性hash
算法就诞生,一致性Hash
算法也是使用取模的方法,但不是对服务器节点数量进行取模,而是对2的32方
取模。即,一致性Hash
算法将整个Hash
空间组织成一个虚拟的圆环, Hash
函数值的空间为0~2^32 - 1
,整个哈希环如下:
- —致性Hash参考地址 (opens new window)
- Hash算法原理
- Hash算法增加节点
- Hash算法减少节点
- Hash算法数据倾斜问题
# url_hash调度算法
根据用户请求的URL
进行hash
取模,根据hash
运算的值,将请求分配到后端特定的一台节点进行处理。URL
算法使用场景如下: client-->nginx-->url_hash-->cache1-->app
- 1.用户请求
nginx
负载均衡器,通过url
调度算法,将请求调度至Cache1
- 2.由于
Cache1
节点没有对应的缓存数据,则会请求后端获取,然后返回数据,并将数据缓存起来; - 3.当其他用户再次请求此前相同的
URL
时,此时调度器依然会调度至Cache1
节点处理; - 4.由于
Cache1
节点已存在该URL
资源缓存,所以直接将缓存数据进行返回;能大幅提升网站的响应;
1.配置后端节点
# web1节点
[root@web01 ~]# echo "web1 Url1" >/web/url1.html
[root@web01 ~]# echo "web1 Url2" >/web/url2.html
# web2节点
[root@web02 ~]# echo "web2 Url1" >/web/url1.html
[root@web02 ~]# echo "web2 Url2" > /web/url2.html
2
3
4
5
6
7
2.负载均衡配置url_hash调度算法
upstream load_pass {
# 请求同一个url,会始终定向到同一个服务器节点,consistent表示使用一致性hash算法
hash $request_uri consistent;
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3.Client
测试,会发现请求相同的 URL
,始终会被定向至某特定后端节点。
[root@client ~]# curl -HHost:web.birenchong.cn http://10.0.0.5/url1.html
web2 Url1
[root@client ~]# curl -HHost:web.birenchong.cn http://10.0.0.5/url1.html
web2 Url1
[root@client ~]# curl -HHost:web.birenchong.cn http://10.0.0.5/url1.html
web2 Url1
2
3
4
5
6
# least_conn调度算法
least_conn
调度算法实现原理,哪台节点连接数少,则将请求调度至哪台节点。
假设∶A节点有1000个连接、b节点有500连接,如果此时新的连接进入会分发给b节点
upstream load_pass {
least_conn;
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Nginx负载均衡后端状态
后端 Web
节点在前端Nginx
负载均衡调度中的状态
状态 | 概述 |
---|---|
down | 当前的server暂时不参与负载均衡 |
backup | 预留的备份服务器 |
max_fails | 允许请求失败的次数 |
fail_timeout | 经过max_fails失败后,服务暂停时间 |
max_conns | 限制最大的接收连接数 |
# max_conns限制连接数
max_conns
用来限制每个后端节点能够接收的最大TCP
连接数,如果超过此连接则会抛出错误。
[root@proxy01 ~]# cat proxy_web.birenchong.cn.conf
upstream load_pass {
server 172.16.1.7:80 max_conns=2;
server 172.16.1.8:80 max_conns=2;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过jmter
压力测试发现,当后端节点处理的连接数非常多的时候,当大于4个连接,其余的连接则会抛出异常,也就是每次仅能满足4个连接。
# down标识关闭状态
down
将服务器标记为不可用状态。
[root@proxy01 ~]# cat proxy_web.birenchong.cn.conf
upstream load_pass {
server 172.16.1.7:80 down; # —般用于停机维护
server 172.16.1.8:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# backup标识备份状态
backup
将服务器标记为备份服务器。当主服务器全部不可用时,将请求传递至备份服务器处理。
[root@proxy01 ~]# cat proxy_web.birenchong.cn.conf
upstream load_pass {
server 172.16.1.7:80 backup;
server 172.16.1.8:80;
server 172.16.1.9:80;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# max_fails与fail_timeout
max_fails=2
服务器通信失败尝试2次,仍然失败,认为服务器不可用;
fail_timeout=5s
服务器通信失败后,每5s探测一次节点是否恢复可用;
在fail_timeout
设定的时间内,与服务器连接失败达到max_fails
则认为服务器不可用;
[root@proxy01 ~]# cat proxy_web.birenchong.cn.conf
upstream load_pass {
server 172.16.1.7:80 max_fails=2 fail_timeout=5s;
server 172.16.1.8:80 max_fails=2 fail_timeout=5s;
}
server {
listen 80;
server_name web.birenchong.cn;
location / {
proxy_pass http://load_pass;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Nginx负载均衡会话共享
# 什么是会话保持
当用户登陆一个网站服务器,网站服务器会将用户的登陆信息存储下来(存储下来的内容叫Session
) ,以保证我们能够一直处于”登陆在线
“状态。
# 为什么需要会话保持
由于我们使用的是负载均衡轮询机制,会导致用户请求分散在不同的节点,从而造成会话无法保持。
假设用户A,通过负载均衡登陆了网站,此时会话信息存储在A节点,那么当它一刷新,负载均衡会将请求分发给B节点,那么B节点没有用户A的登陆信息,就会提示用户A登陆,当A用户点击登陆时又会将请求分发给C节点,从而造成用户A无法实现会话保持。
# 如何实现会话保持
- 1.粘性session:指Ngnix每次都将同一用户的所有请求转发至同一台服务器上,既Nginx的IP_hash;
- 2.session复制:每次session发生变化,就广播给集群中的服务器,使所有的服务器上的session相同;
- 3.session共享:缓存session至内存数据库中,使用redis,memcached实现;
- 4.session持久化:将session存储至数据库中,像操作数据一样操作session;
# 会话保持场景演示
# 配置web节点
1.首先安装并配置phpmyadmin
[root@web01 ~]# cd /code
[root@web01 code]# wget https://files.phpmyadmin.net/phpMyAdmin/5.1.1/phpMyAdmin-5.1.1-all-languages.zip
[root@web01 code]# unzip phpMyAdmin-5.1.1-all-languages.zip
2
3
2.修改phpmyadmin
连接远程的数据库
[root@web01 code]# cd phpMyAdmin-5.1.1-all-languages/
[root@web01 phpMyAdmin-5.1.1-all-languages]# cp config.sample.inc.php config.inc.php
[root@web01 phpMyAdmin-5.1.1-all-languages]# vim config.inc.php
/*Server parameters*/
$cfg['Servers'][$i]['host'] = '172.16.1.51';
2
3
4
5
3.在多台web
上准备phpmyadmin
的nginx
配置文件
[root@web01 ~]# cat /etc/nginx/conf.d/php.conf
server {
listen 80;
server_name php.birenchong.cn;
root /code/phpMyAdmin-5.1.1-all-languages;
location / {
index index.php index.html;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
#重启Nginx服务
[root@web01 ~]# systemctl restart nginx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 配置负载均衡
1.编写一份proxy
负载均衡的配置文件,将请求调度到后端web
节点
[root@proxy01 ~]# cat /etc/nginx/conf.d/proxy_php.com.conf
upstream php {
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name php.birenchong.cn;
location /{
proxy_pass http://php;
include proxy_params;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2检查语法并重载nginx
[root@proxy01 ~]# nginx -t
[root@proxy01 ~]# systemctl restart nginx
2
# 配置Redis服务
1.安装redis
内存数据库
[root@db01 ~]# yum install redis -y
2.配置redis
监听在本地的内网网卡上
[root@db01 ~]# sed -i '/^bind/c bind 127.0.0.1 172.16.1.51' /etc/redis.conf
3.启动redis
[root@db01 ~]# systemctl start redis
[root@db01 ~]# systemctl enable redis
2
# 配置php连接Redis
1.修改/etc/php.ini
文件。[所有节点都需要操作]
[root@web ~]# vim /etc/php.ini
session.save_handler = redis
session.save_path = "tcp://172.16.1.51:6379"
;session.save_path = "tcp://172.16.1.51:6379?auth=123”#如果redis存在密码,则使用该方式
2
3
4
2.注释php-fpm.d/www.conf
里面的两条内容,否则session
内容会一直写入/var/lib/php/session
目录中,从而造成会话共享失败。[所有节点都需要操作]
[root@web ~]# vim /etc/php-fpm.d/www.conf
;php_value[session.save_handler] = files
;php_value[session.save_path]= /var/lib/php/session
2
3
3.重启php-fpm
服务。[所有节点都需要操作]
[root@web ~]# php-fpm -t
[root@web ~]# systemctl restart php-fpm
2
# 测试集群会话共享
1.使用浏览器登陆网站,获取对应的cookie
信息
2.检查redis
中是否存在cookie
对应的session信息
[root@db01 ~]# redis-cli
172.16.1.51:6379> keys*
1)"PHPREDIS_sESSION:393ff522ed2a7e26ba44f6d925f991f2"
172.16.1.51:6379>
2
3
4
3.此时用户的 cookie
始终都不会发生任何变化,无论请求被负载调度到那一台后端web
节点服务器都不会出现没有登陆情况。