011.Nginx反向代理与负载均衡

Nginx代理

在无代理模式下,客户端直接向服务端发起请求,客户端访问请求规模较小的情况下这种直接响应的方式无可厚非,当客户端访问量过大时,一个服务端节点显然无法满足大量的访问请求,而多个服务端节点又会导致客户端不知道该访问‘谁’的问题,此时就需要代理服务

在互联网请求里面,客户端往往无法直接向服务端发起请求,而是通过代理服务实现客户端与服务端之间的通信

Nginx代理服务支持的协议

  1. Nginx作为代理服务,可支持的代理协议非常多

Nginx代理协议

  1. 如果将Nginx作为反向代理服务,常用到如下几种代理协议

Nginx反向代理协议

Nginx正向代理

Nginx的代理服务按照应用场景分为正向代理反向代理,正向代理主要用于解决用户的“上网”问题,例如国内通过正常途径无法访问谷歌,此时仅需要一台能够访问到谷歌的代理服务器,客户端将自己的访问请求交给代理服务器,由代理服务器进行DNS解析、访问谷歌,然后将谷歌返回的数据包封装转发给客户端,这样就实现了“上网”功能

Nginx正向代理

正向代理类似家用WIFI的出口路由器,任何上网的请求都会交给出口路由器,由路由器与公网主机进行数据通信,路由器获取请求的资源后,根据设备MAC地址将资源返回到客户端

Nginx反向代理

Nginx反向代理

反向代理用于公司集群架构中,客户端的所有请求都会先到代理,由代理将客户端请求转交给后端Web集群,后端Web集群返回资源后再由代理服务器返回客户端

正向代理与反向代理的区别在于,正向代理的对象是客户端,为客户端服务、反向代理的对象是服务端,为服务端服务

Nginx反向代理配置

  1. 指令
Syntax: proxy_pass URL;
Default: -
Context: location, if in location, limit_except
  1. 示例

需新增lb01节点

# 示意图
Client <-> (10.0.0.5) Proxy (172.16.1.5) <-> (172.16.1.7) Web01-Server

# 1.lb01节点yum安装nginx

# 2.web01节点准备网页
[root@web01 ~]# vim /usr/local/nginx/conf.d/web01.example.com.conf
server {
        listen 80;
        server_name web01.example.com;
        root /usr/local/nginx/html/;

        location / {
                index web01.php web01.html;
        }
}
[root@web01 ~]# echo "Web01.example.com" > /usr/local/nginx/html/web01.html
[root@web01 ~]# systemctl restart nginx

# 3.准备Proxy代理配置
[root@lb01 ~]# vim /etc/nginx/conf.d/lb01.proxy.web01.conf
server {
        listen 80;
        server_name web01.example.com;
        location / {
                proxy_pass http://10.0.0.7:80;  # 正常这个IP应该是内网IP,此处需要抓包测试
        }
}
[root@lb01 ~]# systemctl enable nginx

# 4.修改本地主机的hosts解析后访问域名

初次测试反向代理的时候,最好是准备一个干净的nginx配置文件和html目录存放测试网页,否则可能会分不清当前获取到的网页,到底是web01节点返回的,还是lb01节点返回的

  1. 抓包

Nginx-Proxy无Host信息

由于代理服务器将请求往后端抛的时候使用的是IP地址proxy_pass http://10.0.0.7:80;,通过IP访问站点的方式会丢失http报文头部信息,如果后端服务器上存在多个站点文件,服务端无法正确的识别到http头部信息,则会随机返回站点目录里文件排序最靠前的站点给客户端

这个问题可以通过两种方式解决:使用不同端口在nginx配置文件中补充请求头部信息

server_name web01.example.com;
location / {
    proxy_pass http://10.0.0.7:80;	# 声明访问10.0.0.7的同时
    proxy_set_header Host $http_host;	# 访问的是web01.example.com这个站点
}

Nginx-Proxy携带Host信息

Nginx反向代理参数

  1. 修改http默认版本
Syntax: proxy_http_version 1.0|1.1;
Default: proxy_http_version 1.0;        # 默认nginx代理访问后端web服务器使用的是http 1.0协议(短连接)
Context: http, server, location
  1. Web服务器记录客户端真实访问IP

Web服务器记录客户端真实IP的前提,其节点本身也需要开启$http_x_forwarded_for日志参数,如果只有代理节点开启了这个参数,Web服务器日志仍不会记录客户端的真实IP

# Web节点默认的日志格式
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$http_x_real_ip"';

# Nginx代理参数
location / {
        proxy_pass http://10.0.0.7:80;
        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_http_version 1.1;
}

关于X-Real-IPX-Forwarded-For的区别,两者都是用于记录客户端的真实请求IP,X-Forwarded-For每经过一级代理(匿名代理除外),代理服务器都会把这次请求的来源IP追加在X-Forwarded-For中,也就是说X-Forwarded-For保存了一整条完整的代理链;X-Real-IP没有明确标准,只有一级代理的情况下X-Real-IPX-Forwarded-For一样能够保留客户端的真实IP,在多级代理的情况下X-Real-IP保留的IP大概率会是上一级反向代理节点的IP。所以在实际使用的场景下,更推荐使用X-Forwarded-For参数

同样的,代理节点使用X-Real-IP参数时,在Web服务器上也需要添加日志参数$http_x_real_ip

  1. 超时时间
# nginx代理与Web服务器连接超时时间;能ping通,但发起TCP连接超过这个时间,nginx代理会直接向客户端返回504响应码
Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http, server, location

# nginx代理等待Web服务器的响应时间;TCP连接成功,发起http请求超时,超时只在两次连续写入操作之间设置,而不是用于传输整个请求
Syntax: proxy_read_timeout time;
Default: proxy_read_timeout 60s;
Context: http, server, location

# Web服务器数据回传给nginx代理的超时时间;TCP连接成功、http请求成功,Web服务器返回数据超时,为了防止请求资源过大,建议这个时间可相对设置大一点
Syntax: proxy_send_timeout time;
Default: proxy_send_timeout 60s;
Context: http, server, location
  1. 代理缓冲
# nginx代理会将后端返回的数据先放在缓冲区,然后再返回给客户端,边收边传,不是一次性接收完后端的所有数据再传给客户端
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;      # number等同于一个倍数,缓冲区的大小等于number*size
Default: proxy_buffers 8 4k|8k;         # size=4k时,缓冲区大小为32k;size=8k时,缓冲区大小为64k
Context: http, server, location

代理缓冲达到的效果类似某些大型网站,因为资源太多无法做到一次加载所有资源,随着用户的使用逐渐加载数据

  1. nginx代理站点配置优化

nginx的代理参数比较多,通常代理配置会单独写到一个文件中,使用这些参数时include调用配置文件即可

[root@lb01 ~]# 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 30;
proxy_send_timeout 60;
proxy_read_timeout 60;

proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;

[root@lb01 ~]# vim /etc/nginx/conf.d/lb01.proxy.web01.conf
server {
        listen 80;
        server_name web01.example.com;
        location / {
                proxy_pass http://10.0.0.7:80;
                include proxy_params;
        }
}

nginx反向代理存在局限性,nginx的代理配置一个location仅能代理后端一台主机,这意味着无法仅靠代理功能,将用户的访问请求均衡的转交给后端的Web节点

Nginx负载均衡

Web服务器直接面向客户时往往要承载大量的并发请求,单台服务器难以负荷,而多台Web服务器集群又会造成用户不知道该访问哪个节点的问题,所以需要Nginx负载均衡搭配Web服务器集群,将用户的请求分散到Web集群中,实现负载的分发,这样能够提升系统的吞吐率、请求性能、高容灾

# 负载均衡常见的名词:
LB(load balance):直译就叫负载均衡。也存在其他的称呼,例如:负载、调度,都是指的同一个东西

# 公有云负载均衡名词:
SLB:阿里云负载均衡
QLB:青云负载均衡
CLB:腾讯云负载均衡
ULB:ucloud负载均衡

一般情况下,管理员接触和实现最多的都是SLB(Server Load Balance)服务负载均衡,SLB的调度节点和服务节点通常是在一个地域内。在这个小的逻辑地域内决定了它对部分服务的实时性、响应性是非常好的,Nginx是一个典型的SLB

SLB

负载均衡的类型

四层负载均衡

四层负载均衡针对OSI七层模型中的传输层,在传输层Nginx能够支持TCP/IP的控制,所以只需要对用户的请求进行TCP/IP协议的包转发就能够实现负载均衡。由于是针对四层的负载,代理节点收到用户的请求报文只需要解封装到四层就能够实现调度,所以四层负载的性能非常快、只需要底层进行应用处理,不需要复杂的逻辑运算

七层负载均衡

七层负载均衡处于应用层,能够实现应用方面的协议请求,例如,http应用的负载均衡,它可以实现http信息的改写、头信息的改写、安全应用规则控制、URL匹配规则控制、以及转发、rewrite等规则,在应用层可操作的内容更多,Nginx是一个典型的七层负载均衡SLB

四层负载与七层负载的区别就在于,四层负载的数据包在底层进行分类,而七层负载的数据包在最顶层分发,由此看出四层负载的效率比七层负载的效率更高;但七层负载更贴近服务,例如http协议就是七层协议,七层Nginx可以做会话保持、URL路径规则匹配等,这些都是四层负载无法实现的

Nginx负载均衡配置

Nginx负载均衡也需要用到proxy_pass代理模块,与Nginx代理的不同在于,Nginx代理仅支持一个location对应一台Web节点,而Nginx负载均衡则是将用户请求代理转发至一组upstream虚拟服务池

  1. 指令
Syntax: upstream name {...}
Default: -
Context: http
  1. 示例
upstream webgroup {
        server web01.example.com;
        server 172.16.1.8;
        server web03.example.com:8080;
        server unix:/tmp/nginx.pid;     # 本地socket文件基本上用不上
}

server {
        location / {
                proxy_pass http://webgroup;     # 必须指明协议
        }
}

示例

角色 外网IP 内网IP 主机名
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配置
[root@web01 ~]# vim /usr/local/nginx/conf.d/node.example.com.conf
server {
        listen 80;
        server_name node.example.com;
        charset utf-8,gbk;

        location / {
                root /usr/local/nginx/html/;
                index node.html node.php;
        }
}
[root@web01 ~]# echo "node01.example.com" > /usr/local/nginx/html/node.html
[root@web01 ~]# systemctl restart nginx

# 2.web02新建nginx配置
[root@web02 ~]# vim /usr/local/nginx/conf.d/node.example.com.conf
server {
        listen 80;
        server_name node.example.com;
        charset utf-8,gbk;

        location / {
                root /usr/local/nginx/html/;
                index node.html node.php;
        }
}
[root@web02 ~]# echo "node02.example.com" > /usr/local/nginx/html/node.html
[root@web02 ~]# systemctl restart nginx

# 3.lb01配置负载均衡
[root@lb01 ~]# vim /etc/nginx/conf.d/proxy_node.conf
upstream node.example.com {     # 定义资源池的名称 node.example.com
        server 172.16.1.7:80;
        server 172.16.1.8:80;
}

server {
        listen 80;
        server_name node.example.com;   # 代理节点自身的域名
        charset utf-8,gbk;

        location / {
                proxy_pass http://node.example.com;     # 调用upstream资源池
                include proxy_params;
        }
}
[root@lb01 ~]# systemctl restart nginx 

负载wordpree和wecenter站点

  1. 负载节点上设置2个upstream服务池,分别对应wordpress和wecenter站点,在通过不同的域名和location调用服务池
# 新建lb01配置负载文件
[root@lb01 ~]# vim /etc/nginx/conf.d/proxy_example.com.conf
upstream wordpress.example.com {
        server 172.16.1.7:80;
        server 172.16.1.8:80;
}

upstream wecenter.example.com {
        server 172.16.1.7:80;
        server 172.16.1.8:80;
}

server {
        listen 80;
        server_name wordpress.example.com;

        location / {
                proxy_pass http://wordpress.example.com;
                include proxy_params;
        }
}

server {
        listen 80;
        server_name wecenter.example.com;

        location / {
                proxy_pass http://wecenter.example.com;
                include proxy_params;
        }
}
[root@lb01 ~]# systemctl restart nginx

# 修改/etc/hosts文件,将此前直接面向web节点的解析全部删除,只留代理的解析
  1. 代理配置优化。upstream服务池的主机是相同的,所以不需要声明多个upstream,不同的是用户访问站点时所用的域名,proxy_params文件中包含用户请求的http头信息配置,所以只需要声明多个server_name即可
upstream web.example.com {
        server 172.16.1.7:80;
        server 172.16.1.8:80;
}

server {
        listen 80;
        server_name wordpress.example.com;

        location / {
                proxy_pass http://web.example.com;
                include proxy_params;
        }
}

server {
        listen 80;
        server_name wecenter.example.com;

        location / {
                proxy_pass http://web.example.com;
                include proxy_params;
        }
}
  1. 负载流量切换

负载实现的高容灾是毋庸置疑的,在上述示例中任意关闭一个web节点的nginx服务都不会影响用户访问wordpress站点,但如果选择关闭php-fpm服务,而nginx服务正常的情况下,故障的web节点会向代理返回502错误。站在用户的视角就会变成不断刷新浏览器能够在正常站点和故障站点之间切换,导致用户体验差

后台服务器连接超时时,nginx本身存在流量切换机制,当一个web节点down的时候,nginx会根据负载均衡的设置将用户请求转移到其他web节点上。但是,如果后台服务器连接没有down,而是返回异常码:504、502、500,此时需要追加一个负载设置,当收到错误码时,自动分配到下一台web节点继续处理用户请求,提高站点访问成功率

Syntax: proxy_next_upstream error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off; 
Default: proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
Context: http, server, location
posted @ 2023-06-19 09:22  hebo  阅读(132)  评论(0)    收藏  举报