05.Nginx负载均衡

徐亮伟, 江湖人称标杆徐。多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。擅长Web集群架构与自动化运维,曾负责国内某大型电商运维工作。
个人博客"徐亮伟架构师之路"累计受益数万人。

老男孩Linux云计算运维QQ交流群: 384467551 226199307
老男孩教育官网 http://www.oldboyedu.com

1.Nginx代理服务

代理我们往往并不陌生, 该服务我们常常用到如(代理租房、代理收货等等)

那么在互联网请求里面, 客户端无法直接向服务端发起请求, 那么就需要用到代理服务, 来实现客户端和服务通信

Nginx作为代理服务可以实现很多的协议代理, 我们主要以http代理为主

正向代理(内部上网) 客户端<-->代理->服务端

反向代理 客户端->代理<-->服务端

正向与反向代理的区别

区别在于代理的对象不一样
正向代理代理的对象是客户端
反向代理代理的对象是服务端

1.1 Nginx反向代理配置语法

1.Nginx代理配置语法

Syntax: proxy_pass URL;
Default:    —
Context:    location, if in location, limit_except

http://localhost:8000/uri/
http://192.168.56.11:8000/uri/
http://unix:/tmp/backend.socket:/uri/

2.url跳转修改返回Location[不常用]参考URL

Syntax: proxy_redirect default;
proxy_redirect off;proxy_redirect redirect replacement;
Default:    proxy_redirect default;
Context:    http, server, location

3.添加发往后端服务器的请求头信息, 如图

Syntax: proxy_set_header field value;
Default:    proxy_set_header Host $proxy_host;
            proxy_set_header Connection close;
Context:    http, server, location

# 用户请求的时候HOST的值是www.bgx.com, 那么代理服务会像后端传递请求的还是www.bgx.com
proxy_set_header Host $http_host;
# 将$remote_addr的值放进变量X-Real-IP中,$remote_addr的值为客户端的ip
proxy_set_header X-Real-IP $remote_addr;
# 客户端通过代理服务访问后端服务, 后端服务通过该变量会记录真实客户端地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

4.代理到后端的TCP连接、响应、返回等超时时间, 如图

//nginx代理与后端服务器连接超时时间(代理连接超时)
Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http, server, location

//nginx代理等待后端服务器的响应时间
Syntax: proxy_read_timeout time;
Default:    proxy_read_timeout 60s;
Context:    http, server, location

//后端服务器数据回传给nginx代理超时时间
Syntax: proxy_send_timeout time;
Default: proxy_send_timeout 60s;
Context: http, server, location

5.proxy_buffer代理缓冲区, 如图

//nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端,边收边传, 不是全部接收完再传给客户端
Syntax: proxy_buffering on | off;
Default: proxy_buffering on;
Context: http, server, location

//设置nginx代理保存用户头信息的缓冲区大小
Syntax: proxy_buffer_size size;
Default: proxy_buffer_size 4k|8k;
Context: http, server, location

//proxy_buffers 缓冲区
Syntax: proxy_buffers number size;
Default: proxy_buffers 8 4k|8k;
Context: http, server, location

6.Proxy代理网站常用优化配置如下,将配置写入新文件,调用时使用include引用即可

[root@Nginx ~]# vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
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 32k;
proxy_buffers 4 128k;

7.代理配置location时调用, 方便后续多个Location重复使用

location / {
    proxy_pass http://127.0.0.1:8080;
    include proxy_params;
}

1.2 Nginx反向代理配置场景

Nginx反向代理配置实例


1.环境准备

角色 外网IP(NAT) 内网IP(LAN) 主机名
Proxy eth0:10.0.0.5 eth1:172.16.1.5 lb01
web01 eth0:10.0.0.7 eth1:172.16.1.7 web01

2.web01服务器, 配置一个网站,监听在8080,仅运行172网段能访问

[root@web01 ~]# cd /etc/nginx/conf.d/
[root@web01 conf.d]# vim web.conf
server {
    listen 8080;
    server_name 172.16.1.7;
    
    location / {
        root /code_8080;
        index index.html;
        deny 10.0.0.0/24;
        allow all;
    }
}
[root@web01 conf.d]# mkdir /code_8080
[root@web01 conf.d]# echo "web01-7...." >/code_8080/index.html
[root@web01 conf.d]# systemctl restart nginx

2.proxy代理服务器, 配置监听在80,让10.0.0.1客户端,能够通过代理访问到后端的172.16.1.7网站

[root@lb01 ~]# cd /etc/nginx/conf.d/
[root@lb01 conf.d]# cat proxy_web_node1.conf 
server {
    listen 80;
    server_name nginx.oldboy.com;
    
    location / {
        proxy_pass http://172.16.1.7:8080;
        include proxy_params;
    }
}

[root@lb01 conf.d]# systemctl enable nginx
[root@lb01 conf.d]# systemctl start nginx

2.Nginx负载均衡

Web服务器,直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台WEB服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾

Nginx是一个典型的SLB(Server Load Balance)网络负载均衡器

2.1 Nginx负载均衡按层划分

负载均衡按层划分应用场景: 四层负载均衡

负载均衡按层划分应用场景: 七层负载均衡, Nginx最常用

2.2 Nginx负载均衡配置场景

Nginx实现负载均衡需要用到proxy_pass代理模块配置.
Nginx负载均衡是将客户端请求代理转发至一组upstream虚拟服务池

Nginx 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;
    }
}

0.环境规划

角色 外网IP(NAT) 内网IP(LAN) 主机名
LB01 eth0:10.0.0.5 eth1:172.16.1.5 lb01
web01 eth0:10.0.0.7 eth1:172.16.1.7 web01
web02 eth0:10.0.0.8 eth1:172.16.1.8 web02

1.Web01服务器上配置nginx, 并创建对应html文件

[root@web01 ~]# cd /etc/nginx/conf.d/
[root@web01 conf.d]# cat node.conf 
server {
    listen 80;
    server_name node.oldboy.com;
    location / {
        root /node;
        index index.html;
    }
}
[root@web01 conf.d]# mkdir /node
[root@web01 conf.d]# echo "Web01..." > /node/index.html
[root@web01 conf.d]# systemctl restart nginx

2.Web02服务器上配置nginx, 并创建对应html文件

[root@web02 ~]# cd /etc/nginx/conf.d/
[root@web02 conf.d]# cat node.conf 
server {
    listen 80;
    server_name node.oldboy.com;
    location / {
        root /node;
        index index.html;
    }
}
[root@web02 conf.d]# mkdir /node
[root@web02 conf.d]# echo "Web02..." > /node/index.html
[root@web02 conf.d]# systemctl restart nginx

3.配置Nginx负载均衡

[root@lb01 ~]# cd /etc/nginx/conf.d/
[root@lb01 conf.d]# cat node_proxy.conf 
upstream node {
    server 172.16.1.7:80;
    server 172.16.1.8:80;
}
server {
    listen 80;
    server_name node.oldboy.com;

    location / {
        proxy_pass http://node;
        include proxy_params;
    }
}
[root@lb01 conf.d]# systemctl restart nginx

4.准备Nginx负载均衡调度使用的proxy_params

[root@Nginx ~]# vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
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 32k;
proxy_buffers 4 128k;

5.使用浏览器访问node.oldboy.com, 然后不断刷新测试

2.3 Nginx负载均衡后端状态

后端Web服务器在前端Nginx负载均衡调度中的状态

状态 概述
down 当前的server暂时不参与负载均衡
backup 预留的备份服务器
max_fails 允许请求失败的次数
fail_timeout 经过max_fails失败后, 服务暂停时间
max_conns 限制最大的接收连接数

1.测试down状态, 测试该Server不参与负载均衡的调度

upstream load_pass {
    //不参与任何调度, 一般用于停机维护
    server 10.0.0.7:80 down;
}

2.测试backup以及down状态

upstream load_pass {
    server 10.0.0.7:80 down;
    server 10.0.0.8:80 backup;
    server 10.0.0.9:80 max_fails=1 fail_timeout=10s;
}

location  / {
    proxy_pass http://load_pass;
    include proxy_params;
}

3.测试max_fails失败次数和fail_timeout多少时间内失败多少次则标记down

upstream load_pass {
    server 10.0.0.7:80;
    server 10.0.0.8:80 max_fails=2 fail_timeout=10s;
}

4.测试max_conns最大TCP连接数

upstream load_pass {
    server 10.0.0.7:80;
    server 10.0.0.8:80 max_conns=1;
}

2.4 Nginx负载均衡调度算法

调度算法 概述
轮询 按时间顺序逐一分配到不同的后端服务器(默认)
weight 加权轮询,weight值越大,分配到的访问几率越高
ip_hash 每个请求按访问IP的hash结果分配,这样来自同一IP的固定访问一个后端服务器
url_hash 按照访问URL的hash结果来分配请求,是每个URL定向到同一个后端服务器
least_conn 最少链接数,那个机器链接数少就分发

1.Nginx负载均衡[wrr]轮询具体配置

upstream load_pass {
    server 10.0.0.7:80;
    server 10.0.0.8:80;
}

2.Nginx负载均衡[weight]权重轮询具体配置

upstream load_pass {
    server 10.0.0.7:80 weight=5;
    server 10.0.0.8:80;
}

3.Nginx负载均衡ip_hash具体配置, 不能和weight一起使用。

//如果客户端都走相同代理, 会导致某一台服务器连接过多
upstream load_pass {
    ip_hash;
    server 10.0.0.7:80 weight=5;
    server 10.0.0.8:80;
}

2.5 Nginx负载均衡TCP实践

配置Nginx4层负载均衡实现如下需求

1.通过访问负载均衡的5555端口,实际是后端的web01的22端口在提供服务。
2.通过访问负载均衡的6666端口,实际是后端的mysql的3306端口在提供服务。

1.Nginx四层负载均衡配置语法

stream {
    upstream backend {
        hash $remote_addr consistent;
        server backend1.example.com:12345 weight=5;
        server 127.0.0.1:12345 max_fails=3 fail_timeout=30s;
        server unix:/tmp/backend3;
    }
    server {
        listen 12345;
        proxy_connect_timeout 1s;
        proxy_timeout 3s;
        proxy_pass backend;
    }
}

2.Nginx四层负载均衡实战

[root@lb01 ~]# mkdir -p /etc/nginx/conf.c
[root@lb01 ~]# vim /etc/nginx/nginx.conf
# 在events层下面,http层上面配置include
include  /etc/nginx/conf.c/*.conf;

# 编写四层代理配置
[root@lb01 ~]# cd /etc/nginx/conf.c/
[root@lb01 conf.c]# cat stream.conf 
stream {
    #1.定义虚拟资源池
    upstream ssh {
        server 172.16.1.7:22;
    }

    upstream mysql {
        server 172.16.1.51:3306;
    }
    #2.调用虚拟资源池
    server {
        listen 5555;
        proxy_connect_timeout 1s;
        proxy_timeout 300s;
        proxy_pass ssh;
    }
    server {
        listen 6666;
        proxy_connect_timeout 1s;
        proxy_timeout 300s;
        proxy_pass mysql;
    }
}
[root@lb01 conf.c]# systemctl restart nginx

3.Nginx四层负载均衡应用场景

3.Nginx动静分离

动静分离, 通过中间件将动态请求和静态请求进行分离, 分离资源, 减少不必要的请求消耗, 减少请求延时。
好处: 动静分离后, 即使动态服务不可用, 但静态资源不会受到影响

通过中间件将动态请求和静态请求分离


3.1 Nginx动静分离应用案例


0.环境准备

系统 服务 服务 地址
CentOS7.5 负载均衡 Nginx Proxy 10.0.0.5
CentOS7.5 静态资源 Nginx Static 10.0.0.7
CentOS7.5 动态资源 Tomcat Server 10.0.0.8

1.在10.0.0.7服务器上配置静态资源

[root@web01 conf.d]# cat ds_oldboy.conf
server{
        listen 80;
        server_name ds.oldboy.com;
        root /soft/code;
        index index.html;

        location ~* .*\.(png|jpg|gif)$ {
                root /soft/code/images;
        }
}

# 准备目录, 以及静态相关图片
[root@web01 ~]# mkdir /soft/code/images -p
[root@web01 ~]# wget -O /soft/code/images/nginx.png http://nginx.org/nginx.png
[root@web01 ~]# systemctl restart nginx

2.在10.0.0.8服务器上配置动态资源

[root@web01 ~]# yum install -y tomcat
[root@web01 ~]# systemctl start tomcat
[root@web01 ~]# mkdir /usr/share/tomcat/webapps/ROOT
[root@web01 ~]# vim /usr/share/tomcat/webapps/ROOT/java_test.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<HTML>
    <HEAD>
        <TITLE>JSP Test Page</TITLE>
    </HEAD>
    <BODY>
      <%
        Random rand = new Random();
        out.println("<h1>Random number:</h1>");
        out.println(rand.nextInt(99)+100);
      %>
    </BODY>
</HTML>

3.在负载均衡10.0.0.5上配置调度, 实现访问jsppng

root@lb01 conf.d]# cat ds_proxy.conf 
upstream static {
        server 10.0.0.7:80;
}
upstream java {
        server 10.0.0.8:8080;
}
server {
        listen 80;
        server_name ds.oldboy.com;
        location / {
                root /soft/code;
                index index.html;
        }
        location ~ .*\.(png|jpg|gif)$ {
                proxy_pass http://static;
                include proxy_params;
        }
        location  ~ .*\.jsp$ {
                proxy_pass http://java;
                include proxy_params;
        }
}
[root@lb01 conf.d]# systemctl restart nginx

4.通过负载测试访问静态资源

5.通过负载测试访问动态资源

6.在负载均衡10.0.0.5上整合动态和静态资源的html文件

[root@lb01 ~]# mkdir /soft/code -p
[root@lb01 ~]# cat /soft/code/index.html
<html lang="en">
<head>
        <meta charset="UTF-8" />
        <title>测试ajax和跨域访问</title>
        <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
</head>
<script type="text/javascript">
$(document).ready(function(){
        $.ajax({
        type: "GET",
        url: "http://ds.oldboy.com/java_test.jsp",
        success: function(data) {
                $("#get_data").html(data)
        },
        error: function() {
                alert("fail!!,请刷新再试!");
        }
        });
});
</script>
        <body>
                <h1>测试动静分离</h1>
                <img src="http://ds.oldboy.com/nginx.png">
                <div id="get_data"></div>
        </body>
</html>

7.测试动态和静态资源是否能正常加载在一个html文件中

8.当使用systemctl stop nginx停止Nginx后, 会发现静态内容无法访问, 动态内容依旧运行正常

9.当使用systemctl stop tomcat停止tomcat后, 静态内容依旧能正常访问, 动态内容将不会被请求到

3.2 Nginx通过来源设备拆分

2.根据不同的浏览器, 以及不同的手机, 访问的效果都将不一样。

//通过浏览器来分别连接不同的浏览器访问不同的效果。
http {
...
    upstream firefox {
        server 172.31.57.133:80;
    }
    upstream chrome {
        server 172.31.57.133:8080;
    }
    upstream iphone {
        server 172.31.57.134:8080;
    }
    upstream android {
        server 172.31.57.134:8081;
    }
    upstream default {
        server 172.31.57.134:80;
    }
...
}

//server根据判断来访问不同的页面
server {
    listen       80;
    server_name  www.xuliangwei.com;

    #safari浏览器访问的效果
    location / {
        if ($http_user_agent ~* "Safari"){
        proxy_pass http://dynamic_pools;
        }     
    #firefox浏览器访问效果
        if ($http_user_agent ~* "Firefox"){
        proxy_pass http://static_pools;
        }
    #chrome浏览器访问效果
        if ($http_user_agent ~* "Chrome"){
        proxy_pass http://chrome;
        } 
        
    #iphone手机访问效果
        if ($http_user_agent ~* "iphone"){
        proxy_pass http://iphone;
        }
    
    #android手机访问效果
        if ($http_user_agent ~* "android"){
        proxy_pass http://and;
        }
    
    #其他浏览器访问默认规则
        proxy_pass http://dynamic_pools;
        include proxy.conf;
        }
    }
}

3.根据访问不同目录, 代理不同的服务器

//默认动态,静态直接找设置的static,上传找upload
    upstream static_pools {
        server 10.0.0.9:80  weight=1;
    }
   upstream upload_pools {
         server 10.0.0.10:80  weight=1;
    }
   upstream default_pools {
         server 10.0.0.9:8080  weight=1;
   }
 
    server {
        listen       80;
        server_name  www.xuliangwei.com;
 
#url: http://www.xuliangwei.com/
    location / { 
        proxy_pass http://default_pools;
        include proxy.conf;
    }
 
#url: http://www.xuliangwei.com/static/
    location /static/ {
        proxy_pass http://static_pools;
        include proxy.conf;
    }
 
#url: http://www.xuliangwei.com/upload/
    location /upload/ {
        proxy_pass http://upload_pools;
        include proxy.conf;
    }
}

//方案2:以if语句实现。
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;
}