Nginx负载均衡
Nginx负载均衡基本概述
Nginx负载均衡在生产环境中的其他名称:
负载 负载均衡 调度 load balance LB
公有云的其他名称:
SLB 阿里云负载均衡
QLB 青云负载均衡
CLB 腾讯负载均衡
ULB ucloud负载均衡
为什么需要使用负载均衡
- 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到我们后端服务器集群中,实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾。
- 往往我们接触的最多的就是SLB负载均衡,实现最多的也是SLB,那么SLB它的调度节点和服务节点通常是在一个地域里面。那么它在这个小的逻辑地域里面决定了它对部分服务的实时性、响应性是非常好的。
- 所以说,当海量的用户请求过来以后,它同样是请求调度节点,调度节点将用户的请求转发给后端对应的服务节点。服务节点处理完请求后再转发给调度节点,调度节点最后响应给用户节点。这样也能实现一个均衡的作用,Nginx是一个典型的SLB。
负载均衡分类
- 四层负载均衡
所谓四层负载均衡指的是OSI七层模型中的传输层,传输层的Nginx已经能支持TCP/IP的控制,所以只需要对客户端的请求进行TCP/IP协议的包转发就可以实现负载均衡,那么它的好处是性能非常快,只需要底层进行应用处理,而不需要进行一些复杂的逻辑。 - 七层负载均衡
七层负载均衡它是在应用层,那么它可以完成很多应用方面的协议请求,比如我们说的HTTP应用的负载均衡,它可以实现HTTP信息的改写、头信息的改写、安全应用规则控制、URL匹配规则控制,以及转发、rewrite等等的规则,所以在应用层的服务里面,我们可以做的内容就更多,那么Nginx则是一个典型的七层负载均衡SLB。
四层负载均衡和七层负载均衡的区别
- 四层负载均衡数据在底层就进行了分发,而七层负载均衡数据包则是在最顶层进行分发,由此可以看出,七层负载均衡效率没有四层负载均衡高。
- 但是七层负载均衡更贴近于服务,如HTTP协议就是七层协议,我们可以用Nginx可以作会话保持,URL路径规则匹配,head头改写等等,这些都是四层负载均衡无法实现的。
Nginx负载均衡配置场景
Nginx要实现负载均衡需要用到proxy_pass代理模块配置
Nginx负载均衡于Nginx代理不同的地方在于,Nginx代理一个location仅能代理一台后端主机,而Nginx负载均衡则是将客户端请求的代理转发至一组upstream虚拟服务池。
环境规划
角色 外网IP(NAT) 内网IP(LAN) 主机名
lb01 eth0:192.168.15.5 eth1:172.16.1.5 lb01
web01 eth0:192.16.15.7 eth1:172.16.1.7 web01
web02 eth0:192.16.15.8 eth1:172.16.1.8 web02
- web01服务器配置Nginx静态资源
vim /etc/nginx/conf.d/web.nana.com.conf
server {
listen 80;
server_name web.nana.com;
root /web;
location / {
index index.html;
}
}
echo "web01..." > /web/index.html
systemctl restart nginx
- web02服务器配置Nginx静态资源
vim /etc/nginx/conf.d/web.nana.com.conf
server {
listen 80;
server_name web.nana.com;
root /web;
location / {
index index.html;
}
}
echo "web02.." > /web/index.html
systemctl restart nginx
- lb01服务器配置Nginx负载均衡
1. 创建参数配置文件
vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_connect_timeout 30s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
2. 创建Nginx负载均衡配置文件
vim /etc/nginx/conf.d/proxy_web.conf
upstream web { # 创建虚拟服务池,定义虚拟服务池的名称为web
server 172.16.1.7:80; # 将服务器web01添加至定义好的虚拟服务池web
server 172.16.1.8:80; # 将服务器web02添加至定义好的虚拟服务池web
}
server {
listen 80;
server_name web.nana.com;
location / {
proxy_pass http://web; # Nginx代理指向是定义好的虚拟服务池web
# Nginx代理指向的是一台服务器,Nginx负载均衡,指向的是一个虚拟服务池。
include proxy_params;
}
}
systemctl restart nginx
测试Nginx负载均衡
在本机添加域名解析
C:\Windows\System32\drivers\etc
在hosts文件添加域名解析
192.168.15.5 web.nana.com
- 我们打开浏览器输入
web.nana
,浏览器显示内容为web01...
;刷新页面,浏览器的显示内容为web02...
。 - 我们也可以同时打开web01和web02的终端,输入
tail -f /var/log/nginx/access.log
。不断地刷新页面,观察Nginx日志内容的变化。 - 如果日志记录的内容是来回切换的,那么说明客户的请求是被 lb01代理服务器服务器分别发放给两台web服务器做处理。
搭建Wordpress服务和WeCenter服务的负载均衡
关注我博客的小伙伴们,如果想搭建这个服务,需要先要搭建好LNMP架构服务。
如果有拆分LNMP架构的小伙伴们。需要确保数据库web01和web02是正常可以访问到数据库的数据的,NFS是正常挂载在Web01和Web02服务器上的。
lb01负载均衡服务器
1. 配置Nginx负载均衡
vim /etc/nginx/conf.d/proxy_nana.com.conf
upstream node { # 定义一个虚拟服务池node
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name blog.nana.com; # 1. 通过不同的域名匹配不同的项目(浏览器-->代理服务器)
location / {
proxy_pass http://node; # 2. 代理服务器-->虚拟服务池node-->请求平均分发给web服务器
include proxy_params; # 导入常用参数配置文件(默认加上Host头信息[域名]),通过域名找到web服务器中对应的文件。
}
}
server {
listen 80;
server_name zh.nana.com; # 1. 通过不同的域名匹配不同的项目(浏览器-->代理服务器)
location / {
proxy_pass http://node; # 2. 代理服务器-->虚拟服务池node-->请求平均分发给web服务器
include proxy_params; # 导入常用参数配置文件(默认加上Host头信息[域名]),通过域名找到web服务器中对应的文件。
}
}
2. 重启Nginx
systemctl restart nginx
测试Nginx负载均衡特性
测试Nginx负载均衡
在本机添加域名解析
C:\Windows\System32\drivers\etc
在hosts文件添加域名解析
192.168.15.5 zh.nana.com blog.nana.com
- 我们打开浏览器输入
zh.nana.com
或者blog.nana.com
,浏览器是可以正常访问到网站页面的。 - 我们也可以同时打开web01和web02的终端,输入
tail -f /var/log/nginx/access.log
。不断地刷新页面,观察Nginx日志内容的变化。 - 如果日志记录的内容是来回切换的,那么说明客户的请求是被lb01代理服务器分别发放给两台web服务器做处理。
测试Nginx高容灾
- 我们直接停用Web01服务器的Nginx服务
systemctl stop nginx
,刷新浏览器页面。我们发现浏览器还是可以正常访问到网站页面的。这也就说明,在虚拟服务池中,如果一台服务器的Nginx服务崩掉了,并不会影响整个架构的运行。
后端连接请求超时问题
如果我们停止Web02服务器PHP服务systemctl stop php-fpm
,打开浏览器输入zh.nana.com
或者blog.nana.com
,会发现浏览器报错502。我们再刷新一次,会发现页面是可以正常访问的。不断地刷新页面,会发现502报错页面和正常的浏览器页面是会来回切换的。
- 如果后台服务连接超时,Nginx是本身有机制的,如果出现一个节点down掉的时候,Nginx会根据你具体负载均衡的设置,将请求转移到其他节点上。但是,如果后台服务连接没有down掉,但是返回错误异常了,如504、502、500等。这个时候我们需要在Nginx代理服务器上加一个负载均衡设置。
vim /etc/nginx/proxy_params # Nginx负载均衡,参数配置文件
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
# 当Nginx虚拟服务池其中任意一台服务器返回错误码500,502,503,504等错误时,可以分配到下一台服务器程序继续处理,提高平台访问的成功率
proxy_http_version 1.1;
proxy_connect_timeout 30s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
systemctl restart nginx
upstream虚拟服务池模块
Nginx负载均衡调度算法
调度算法 概述
轮询 按时间顺序逐一分配到不同的后端服务器(默认的算法)
weight 加权轮询,weight值越大,分配到的访问几率越高
ip_hash 每个请求按访问IP的hash结果分配,这样来自同一个IP的固定访问一个后端服务器
url_hash 按照访问的URL的hash结果来分配请求,是每一个URL定向到同一个后端服务器
least_conn 最少链接数,哪个机器链接数最少就分发
- weight 加权轮询
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
server 172.16.1.7:80 weight=5;
server 172.16.1.8:80 weight=1;
# 配置权重,用户端请求访问5次web01主机后,再访问1次web02主机
}
...
- ip_hash 不能和加权轮询一起使用
ip_hash 能解决会话登陆问题,但是会造成负载不均衡,导致某一台主机流量过大,而另一台主机没什么流量。
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
ip_hash;
# 根据来源的IP自动分配主机,记录在内存里面。
# 如果我们第一次用浏览器访问web服务器,请求发送给了web01主机,那么请求会一直发送给web01主机。
# 除非web01主机down掉了,才会把请求发送给web02主机。如果web01主机恢复运行了,那么请求还是会发送给web01主机。
server 172.16.1.7:80;
server 172.16.1.8:80;
}
如果在后端Web主机配置一样的情况下,负载默认使用轮询的算法比较适用。
Nginx负载均衡后端状态
状态 概述
down 当前的server暂时不参与负载均衡
backup 预留的备份服务器
max_fails 允许请求失败的次数
fail_timeout 经过max_fials失败后,服务暂停时间
max_conns 限制最大的接收链接数
- down 一般用户停机维护时使用
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
server 172.16.1.7:80 down; # web01不参与负载均衡,跟注释web01效果一样
server 172.16.1.8:80;
}
- backup 当所有server不可用,才会启用backup
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
server 172.16.1.7:80 backup; # web02和web03都down机了,使用web01当作备用服务器
server 172.16.1.8:80;
server 172.16.1.9:80;
}
- max_conns 最大链接数
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
server 172.16.1.7:80 max_conns=3; # 如果同时来了10个连接,web01最大接受连接数为3个,剩下的全都给web02处理
server 172.16.1.8:80 ;
}
- fail_timeout 允许请求失败的次数
- max_conns 经过max_fails失败后,服务暂停的时间
这两个参数,一般用于检查后端服务器的健康情况,但是无法看到具体的指标
vim /etc/nginx/conf.d/proxy_web.conf
upstream web {
server 172.16.1.7:80 max_fails=2 fail_timeout=10s; # 最多允许客户端向服务端发送请求失败2次,超过2次,暂停服务10秒。
server 172.16.1.8:80 max_fails=2 fail_timeout=10s; # 最多允许客户端向服务端发送请求失败2次,超过2次,暂停服务10秒。
}
fail_timeout 、 max_conns 参数测试
我们可以先关闭web02的Nginx服务。不断刷新浏览器界面,观察lb01代理错误日志的记录的时间。
代理服务器的错误日志error.log,每隔10秒种记录一次日志,但是日志的记录内容并没有显示这两个参数配置的信息。
Nginx负载均衡健康检查
- 在Nginx官方提供的模块中,没有对负载均衡后端节点的健康检查模块,但是可以使用第三方模块
nginx_upstream_check_module
来检查后端服务的健康状态。
我原本使用的是nginx1.20.0版本,但是当前第三方模块网址网址不支持没有nginx1.20.0版本的补丁!!!
所以这里我只能重新创建一台虚拟机,重新去官网下载了nginx-1.16.1版本来做实验。
这个实验的重点是需要了解,如何在不更改Nginx当前服务配置的情况下,给已经安装好的Nginx软件打补丁。
- 配置nginx-1.16.1版本epel源
vim /etc/yum.repos.d/nginx16.repo
[nginx1.16]
name=nginx
baseurl=https://repo.huaweicloud.com/epel/7/x86_64/
enabled=1
gpgcheck=0
- 添加Nginx健康检查模块
1. 安装需要的依赖软件
yum install -y gcc glibc gcc-c++ pcre-devel openssl-devel patch
2. 下载源件包
wget http://nginx.org/download/nginx-1.16.1.tar.gz
wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/master.zip
3. 解压nginx源码包以及nginx_upstream_check_module第三方模块
tar -xf nginx-1.16.1.tar.gz
unzip master.zip
4. 进入nginx目录,打补丁(p1代表在nginx目录内,p0代表不在nginx目录内)
cd nginx-1.16.1/
patch -p1 <../nginx_upstream_check_module-master/check_1.16.1+.patch
# 把已经解压好的补丁包导入当前目录下
5. 在已有的负载均衡上增加健康检查的功能 # 添加健康检查的模块--add-module=/root/nginx_upstream_check_module-master
nginx-V # 查看当前nginx的默认参数
- 添加健康检查的新模块,并且进行编译前的系统检查
./configure --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --add-module=/root/nginx_upstream_check_module-master --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'
6. 下载编译检查缺少的依赖关系软件
yum -y install libxslt-devel
yum -y install gd gd-devel
yum -y install perl-devel perl-ExtUtils-Embed
7. 开始编译并且安装
make && make install
8. 修改配置文件,添加健康检查模块
vim /etc/nginx/conf.d/proxy.conf
upstream web {
server 172.16.1.7:80 ;
server 172.16.1.8:80 ;
check interval=3000 rise=2 fall=3 timeout=1000 type=tcp; # 设置nginx健康检查模块显示在浏览器的内容
# interval:检查的间隔时间,单位为毫秒
# rise:请求2次正常,标记后端的状态为up
# fall:请求3次失败,标记后端状态为down
# timeout:超时时间,单位为毫秒
}
server {
listen 80;
server_name web.nana.com;
location / {
proxy_pass http://web;
include proxy_params;
}
location /upstream_check {
check_status; # 调用健康检查功能模块的网址web.nana.com/upstream_check
}
}
9. 重启nginx服务
nginx -t
systemctl restart nginx
- 测试
浏览器输入域名web.nana.com/upstream_check,可以通过图形界面的形式,查看当前后端服务器(web01和web02)的状态。
Nginx负载均衡解决后端session共享概述
模拟无法会话共享问题
第一步. web01主机
1. 在web01服务器安装phpmyadmin服务 # phpmyadmin服务是把session_id直接写入本地的浏览器缓存中的
cd /code
wget https://files.phpmyadmin.net/phpMyAdmin/5.1.0/phpMyAdmin-5.1.0-all-languages.zip
2. 解压phpmyadmin服务压缩包
unzip phpMyAdmin-5.1.0-all-languages.zip
3. 配置phpmyadmin配置文件服务连接远程的数据库
cp /code/phpMyAdmin-5.1.0-all-languages/config.sample.inc.php /code/phpMyAdmin-5.1.0-all-languages/config.inc.php # config.sample.inc.php这是一个模板文件,需要复制改名之后再进行修改
vim /code/phpMyAdmin-5.1.0-all-languages/config.inc.php
...
$cfg['Servers'][$i]['host'] = '172.16.1.51'; # 30行,修改连接数据库的ip地址
...
4. 配置php服务授权
chown -R www:www /var/lib/php/
ll /var/lib/php/
# total 0
# drwxrwx--- 2 www www 6 Aug 4 2019 session
# drwxrwx--- 2 www www 6 Aug 4 2019 wsdlcache
5. 修改Nginx服务的配置文件
vim /etc/nginx/conf.d/php.conf
server {
listen 80;
server_name php.nana.com;
root /code/phpMyAdmin-5.1.0-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;
}
}
6. 重启Nginx服务
systemctl restart nginx
7. 在本机添加域名解析
C:\Windows\System32\drivers\etc
在hosts文件添加域名解析
192.168.15.7 php.nana.com
- 测试
- 我们在浏览器用数据管理员的身份登陆phpmyadmin客户端,成功登陆之后,查看浏览器Cookie中缓存的session_id。
- 查看web01服务器中的session_id值,我们会发现是一样的。
ll /var/lib/php/session/ 查看/var/lib/php/session/目录下系统默认生成的文件名===>session_id
# total 4
# -rw------- 1 www www 2521 May 12 23:33 sess_05557fe7dcb3e2da4288ce9ca53069cf
- 如果我们清除浏览器缓存之后,重新登陆phpmyadmin客户端,会发现浏览器和web01服务器中的session_id都会发生变化。
第二步. web02主机
1. 在web02服务器安装phpmyadmin服务 # phpmyadmin服务是把session_id直接写入本地的浏览器缓存中的
cd /code
wget https://files.phpmyadmin.net/phpMyAdmin/5.1.0/phpMyAdmin-5.1.0-all-languages.zip
2. 解压phpmyadmin服务压缩包
unzip phpMyAdmin-5.1.0-all-languages.zip
3. 将web01配置好的phpmyadmin以及nginx的配置文件推送到web02主机上
scp -rp /code/phpMyAdmin-5.1.0-all-languages root@172.16.1.8:/code/ # 在web01上操作
scp /etc/nginx/conf.d/php.conf root@172.16.1.8:/etc/nginx/conf.d/ # 在web01上操作
4. 配置php服务授权
chown -R www:www /var/lib/php
5. 在web02上重启Nginx服务
systemctl restart nginx
第三步. lb01主机
1. 将web01和web02接入负载均衡
vim /etc/nginx/conf.d/proxy_php.conf
upstream php {
server 172.16.1.7:80;
server 172.16.1.8:80;
}
server {
listen 80;
server_name php.nana.com;
location / {
proxy_pass http://php;
include proxy_params;
}
}
2. 重启Nginx服务
nginx -t
systemctl restart nginx
3. 关闭Web01服务器的域名解析,在本机添加lb01服务器的域名解析
C:\Windows\System32\drivers\etc
在hosts文件添加域名解析
192.168.15.5 php.nana.com
测试
- 我们配置好Web01和Web02主机后,添加负载均衡服务器lb01。可以通过cmd测试,确认域名
php.nana.com
对应的IP地址为lb01服务器192.168.15.5
。 - 因为我们负载默认是采用轮询的方式进行请求的转发的,我们登陆
phpmyadmin
服务会出现如下的情况。造成无法登陆原因,就是因为会话无法共享问题。 - 用户输入密码后,session_id登陆的标记,记录在web01服务器中;用户发起加载网站页面的第二次请求,负载会把登陆请求转发给web02服务器,但是web02服务器中并没有记录session_id的登陆的标记,这就导致了用户无法登陆的情况。
会话共享问题解决方法
使用Redis解决会话登陆问题
- 配置Redis的华为云epel源
vim /etc/yum.repos.d/redis.repo
[redis]
name=redis
baseurl=https://repo.huaweicloud.com/epel/7/x86_64/
enabled=1
gpgcheck=0
- 配置Redis服务端
- 我这里使用的是db01数据库服务器当作redis缓存服务器使用了,生产环境中,我们一般会使用单独的redis缓存服务器
1. 安装redis内存数据库
yum -y install redis
2. 配置redis监听在172.16.1.0网段上
sed -i "/^bind/c bind 127.0.0.1 172.16.1.51" /etc/redis.conf # 修改配置redis配置文件,第61行
3.启动redis
systemctl restart redis
systemctl enable redis
- PHP配置session连接Redis
- Web01服务器上做配置
1. 修改/etc/php.ini文件 # /etc/php.ini是配置php解析器的配置文件
vim /etc/php.ini
session.save_handler = redis # 第1231行,官方提示改成redis
session.save_path = "tcp://172.16.1.51:6379" # 第1264行,修改session写入文件的路径,redis默认监听在6379端口号上
# session.save_path = "tcp://172.16.1.51:6379?auth=123" # 如果redis存在密码,则使用该方式
session.auto_start = 1 # 第1294行,1表示启动
2. 注释/etc/php-fpm.d/www.conf里面的两条内容,否则session内容会一直写入本地/var/lib/session目录中
vim /etc/php-fpm.d/www.conf
;php_value[session.save_handler] = files # 第395行
;php_value[session.save_path] = /var/lib/php/session # 第396行
3. 重启php-fpm服务
systemctl restart php-fpm
4. 将web01服务器上配置好的文件覆盖到web02服务器上
scp /etc/php.ini root@172.16.1.8:/etc/php.ini
scp /etc/php-fpm.d/www.conf root@172.16.1.8:/etc/php-fpm.d/www.conf
- web02服务器
1. 重启php-fpm服务
systemctl restart php-fpm
测试
- 配置好服务之后,我们是可以成功登陆的网站的。
- 查看Redis服务器中记录的session_id
- db01服务器
redis-cli # 进入redis客户端
127.0.0.1:6379> keys * # 查看服务端的session_id
1) "PHPREDIS_SESSION:0aec4cc7488d675c85d4395a4483a564" # 服务端的session_id和浏览器cookie记录session_id的一致
2) "PHPREDIS_SESSION:8a82ea11d562296eb1b9ce8bc766808a" # 以前登陆过的session_id值
- 我们查看浏览器Cookie记录的session_id值,跟redis服务端记录的session_id进行对比,发现两个值是一样的,这就表示我们的登陆会话信息的session_id是写入Redis数据库中的,这就是通过redis内存数据共享解决了会话共享的问题。