反向代理

sl08 23 / 2023-09-03 / 原文

反向代理

反向代理:reverse proxy,指的是代理外网用户的请求到内部的指定的服务器,并将数据返回给用户的一种方式,这是用的比较多的一种方式。

ngx_http_proxy_module: #将客户端的请求以http协议转发至指定服务器进行处理
ngx_http_upstream_module #用于定义为proxy_pass,fastcgi_pass,uwsgi_pass等指令引用的后端服务器分组
ngx_stream_proxy_module:#将客户端的请求以tcp协议转发至指定服务器处理
ngx_http_fastcgi_module:#将客户端对php的请求以fastcgi协议转发至指定服务器助理
ngx_http_uwsgi_module: #将客户端对Python的请求以uwsgi协议转发至指定服务器处理

image-20230901171623300

1.1 http 协议反向代理

1. 反向代理配置参数

#官方文档:https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

proxy_pass; 
#用来设置将客户端请求转发给的后端服务器的主机,可以是主机名(将转发至后端服务做为主机头首部)、IP
地址:端口的方式
#也可以代理到预先设置的主机群组,需要模块ngx_http_upstream_module支持
​

proxy_pass 后跟斜杠与不跟斜杠的区别

#示例:

#不带斜杠相当于追加
10.0.0.8/web
 location /web {
   index index.html;
   proxy_pass http://10.0.0.18:8080; #8080后面无uri,即无 / 符号,需要将location后面url 附加到proxy_pass指定的url后面,此行为类似于root
#proxy_pass指定的uri不带斜线将访问的/web,等于访问后端服务器
http://10.0.0.18:8080/web/index.html,即后端服务器配置的站点根目录要有web目录才可以被访问
    # http://nginx/web/index.html ==> http://10.0.0.18:8080/web/index.html

 

//有/相当于置换
​
proxy_pass http://10.0.0.18:8080/;   #8080后面有uri,即有 / 符号,相当于置换,即访问/web时实际返回proxy_pass后面uri内容.此行为类似于alias 
    #proxy_pass指定的uri带斜线,等于访问后端服务器的http://10.0.0.18:8080/index.html 内容返回给客户端
 }  # http://nginx/web/index.html ==> http://10.0.0.18:8080

隐藏后端服务器相应头部的信息

#用于nginx作为反向代理的时候,在返回给客户端http响应时,隐藏后端服务器相应头部的信息,可以设置在http,server或location块

#示例: 隐藏后端服务器ETag首部字段

 location /web {
   index index.html;
   proxy_pass http://10.0.0.18:8080/; 
   proxy_hide_header ETag;
 }
​
proxy_pass_header field;
#默认nginx在响应报文中不传递后端服务器的首部字段Date, Server, X-Pad, X-Accel等参数,如果
要传递的话则要使用 proxy_pass_header field声明将后端服务器返回的值传递给客户端
#field 首部字段大小不敏感
#示例:透传后端服务器的Server和Date首部给客户端,同时不再响应报中显示前端服务器的Server字段
proxy_pass_header Server;
proxy_pass_header Date;
proxy_pass_request_body on | off; 
#是否向后端服务器发送HTTP实体部分,可以设置在http,server或location块,默认即为开启
proxy_pass_request_headers on | off; 
#是否将客户端的请求头部转发给后端服务器,可以设置在http,server或location块,默认即为开启

1.2 实战案例: 反向代理单台web 服务器

实现单台反向代理

实验前关闭防火墙
systemctl stop firewalld.service
 setenforce 0
​

配置7-1 代理服务器

vim /apps/nginx/conf.d/pc.conf
server{
        listen   80;
        server_name www.pc.com;
        root /data/nginx/pc;
     location / {
        proxy_pass http://192.168.1.200;//如果访问根就将请求转发到位于192.168.1.200的服务器上,通过http协议通信
    }
}
nginx -s reload 重新加载配置文件

配置7-2 真实服务器

yum  install   httpd  -y  //安装http服务
​
cd  /var/www/html //进入http默认网页目录
​
echo   "real  server 200"  > index.html  //在默认网页目录写如文件用于测试
​
systemctl   stop nginx   //关闭nginx服务防止端口号冲突nginx和http都默认用80端口,如果nginx服务已关闭可以不执行此操作
​
systemctl   start httpd 开启http服务

配置7-3 客户机

vim  /etc/hosts//进入本地hosts文件
192.168.1.100  www.pc.com//指明服务器位置

测试

在客户机

[root@localhost ~]# curl 192.168.1.100
real server 200

image-20230902133955816

#真实服务器
建立大文件查看连接情况
dd if=/dev/zero   of=test.img    count=1  bs=1G
​
​
找台服务器访问
curl www.pc.com
wget --limit-rate=1024 www.pc.com/test.img
​
#真实服务器
ss -natp | grep ":80" //查看有哪些在使用80端口

image-20230902143922658

image-20230902144048450

修改端口号 真实服务器 httpd 服务器修改端口号

找到httpd服务主配置文件
我的在
vim /etc/httpd/conf/httpd.conf
如果不知道在哪可以使用
 rpm -ql httpd | egrep 'httpd\.conf'
 #\为转意符
找到Listen 指令并将后面的端口号改为想要的端口号如8080
systemctl restart httpd 重启服务

image-20230902151612962

修该代理服务器

 vim /apps/nginx/conf.d/pc.conf //进入配置文件
 
server{
        listen   80;
        server_name www.pc.com;
        root /data/nginx/pc;
     location / {
        proxy_pass http://192.168.1.200:8080;
    }
}
nginx -s reload 重新加载配置文件
配置好后客户端在访问的时候就会被转到对应端口如果这边不修改端口默认转发80端口
就会报错502

image-20230902151658162

客户机访问测试

curl 192.168.1.100
#注意我们访问的是代理服务器所以还是80端口所以在访问时不用指定端口
由代理服务器帮我们访问真实服务器所以代理服务器要指明端口号8080

image-20230902152140121

 

#还可以在代理服务器上做一些防火墙操作
在代理服务器上 做防火墙规则
iptables -A INPUT  -s 192.168.1.100 -j DROP
客户端再次访问  会出现504网关超时(有可能只是处理时间久,服务器不一定挂了),时间较长1分钟,没有定义代理超时时间

iptables -A INPUT  -s 192.168.1.100 -j REJECT
客户端再次访问  会出现502,一般出现502 代表后端真实服务器挂了

针对某个uri 进行访问

要求:将用户对域 www.pc.com的请求转发给后端服务器处理

[root@centos8 ~]# cat /apps/nginx/conf.d/pc.conf
server {
 listen 80;
 server_name www.pc.com;
 location ~* /api {
     proxy_pass http://192.168.1.200;
 }
}

~*无视api大小写
#在后端服务器下创建文件
vim /var/www/html/api/index.html
 cat  /var/www/html/api/index.html
api
api
api
api
api

#重启Nginx 并访问测试

 访问www.pc.com  后端服务器


如果后端服务器无法连接((比如:iptables -AINPUT -s nginx_ip -j REJECT或者systemctl stop httpd)), 会出

浏览器测试

image-20230902165519873

image-20230902165622511

1.3 实战案例:指定location 实现反向代理 动静分离

server{
        listen 80;
        server_name  www.pc.com;
        root    /data/nginx/pc;
     location  / {
        root  /data/nginx/pc;
}
     location  /api {
        root  /data/nginx/pc;
        proxy_pass http://192.168.1.200:8080;//访问动态转到192.168.1.200
​
        }
     location ~* \.(jpg|jpeg|png|gif|bmp)$ {
        root  /data/nginx/pc;
        proxy_pass http://192.168.1.150;//访问静态内容转到192.168.1.150
        }
​
}

image-20230902182258185

1.4反向代理示例:缓存功能

关闭后端服务器后,图片无法访问

缓存功能默认关闭,需要开启

proxy_cache zone_name | off; 默认off
#指明调用的缓存,或关闭缓存机制;可用在Context:http, server, location
#zone_name 表示缓存的名称.需要由proxy_cache_path事先定义
​
proxy_cache_key string;
#缓存中用于“键”的内容,默认值:proxy_cache_key $scheme$proxy_host$request_uri;
​
​
​
proxy_cache_valid [code ...] time;
#定义对特定响应码的响应内容的缓存时长,定义在http{...}中
 示例:
 proxy_cache_valid 200 302 10m;//状态码为200 302 缓存10m中
 proxy_cache_valid 404 1m;状态码为404缓存1m
proxy_cache_path;
#定义可用于proxy功能的缓存;Context:http   必须放在http语句中
proxy_cache_path path [levels=levels] [use_temp_path=on|off] 
keys_zone=zone_name:size [inactive=time] [max_size=size] [manager_files=number] 
[manager_sleep=time] [manager_threshold=time] [loader_files=number] 
[loader_sleep=time] [loader_threshold=time] [purger=on|off] 
[purger_files=number] [purger_sleep=time] [purger_threshold=time];
#示例:在http配置定义缓存信息
   proxy_cache_path /var/cache/nginx/proxy_cache #定义缓存保存路径,proxy_cache会自动创建
   levels=1:2:2 #定义缓存目录结构层次,1:2:2可以生成2^4x2^8x2^8=2^20=1048576个目录
   keys_zone=proxycache:20m #指内存中缓存的大小,主要用于存放key和metadata(如:使用次数),一般1M可存放8000个左右的key
   inactive=120s  #缓存有效时间  
   max_size=10g; #最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值
#调用缓存功能,需要定义在相应的配置段,如server{...};或者location等
proxy_cache proxycache;
proxy_cache_key $request_uri; #对指定的数据进行MD5的运算做为缓存的key
proxy_cache_valid 200 302 301 10m; #指定的状态码返回的数据缓存多长时间
proxy_cache_valid any 1m;   #除指定的状态码返回的数据以外的缓存多长时间,必须设置,否则不会缓存
proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ; #默认是off
#在被代理的后端服务器出现哪种情况下,可直接使用过期的缓存响应客户端
​
#示例
proxy_cache_use_stale error http_502 http_503;
​
​
proxy_cache_methods GET | HEAD | POST ...;
#对哪些客户端请求方法对应的响应进行缓存,GET和HEAD方法总是被缓存

扩展知识:清理缓存

方法1: rm -rf 缓存目录
方法2: 第三方扩展模块ngx_cache_purge

实现缓存实际操作

在未开启缓存前可以测试下载速度
ab -c100 -n1000 http://www.pc.com/3m.jpg
去代理服务器上 添加操作
先去   主配置文件 的 http模块 中加入下方配置
proxy_cache_path /data/nginx/proyxcache //定义缓存路径会自动创建  levels=1:1:1//目录比 keys_zone=proxycache:20m//指定内存缓存大小 inactive=120s//缓存有效时间 max_size=1g最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值


proxy_cache_path /data/nginx/proyxcache   levels=1:1:1 keys_zone=proxycache:20m inactive=120s max_size=1g;

image-20230903142603432

#再去 子配置文件中添加
vim /apps/nginx/conf.d/pc.conf
server{
        listen 80;
        server_name  www.pc.com;
        proxy_cache proxycache;
        proxy_cache_key $request_uri;
或者放这个 #proxy_cache_key $host$uri$is_args$args;
        proxy_cache_valid 200 302 301 10m;
        proxy_cache_valid any 5m;
        root    /data/nginx/html/pc;
     location  / {
        root  /data/nginx/html/pc;
}
     location  /api {
        root  /data/nginx/html/pc;
        proxy_pass http://192.168.1.200;
        }
}
#配置真实服务器
cd /var/www/html/api
[root@localhost api]# ls //这里我放了一张名为2.jpg的图片
2.jpg  index.html

测试

浏览器访问

image-20230903143819639

来到代理服务器,进入缓存目录查看,缓存情况

image-20230903144502866

断开真实服务器查看缓存是否生效

systemctl stop httpd.service//停止httpd服务
systemctl status httpd.service//查看httpd服务状态
​

image-20230903144718666

用浏览器访问

image-20230903144757005

等待缓存时间超时在访问就看不到图片了

image-20230903145251982

image-20230903145159674

1.5** 实现反向代理客户端 IP 透传

image-20230903152909097

为什么要实现IP穿透

客户端在访问资源的时候,经过代理服务器转发,真实服务器日志没法显示客户端IP,不便于排错

实现IP穿透

在客户端访问资源的时候会经过代理服务器转发到真实服务器,真实在服务器会记录客户端访问资源所经过代理服务器ip

 

示例

实现上图的IP穿透

代理服务器

#打开第一台代理服务器日志
进入主配置文件
 vim /apps/nginx/conf/nginx.conf
 //将#去掉
   #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
​
    #access_log  logs/access.log  main;
​

image-20230903172510093

进入虚拟主机配置文件
 vim /apps/nginx/conf.d/pc.conf
#添加客户端IP和反向代理服务器IP到请求报文头部
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 #如果用proxy_set_header X-Real-IP $remote_addr;          #只添加客户端IP到请求报文头部,转发至后端服务器如果是多台代理会覆盖之前的ip信息
​

image-20230903172829027

配置真实服务器日志
#进入主配置文件
vim /etc/httpd/conf/httpd.conf
添加
\"%{X-Forwarded-For}i\"    //这个与代理服务器的对应

image-20230903174529813

配置第二带代理服务器

#开启日志服务
 vim /apps/nginx/conf/nginx.conf
​
 //将#去掉
   #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
​
    #access_log  logs/access.log  main;

image-20230903181738228

因为我第二台代理服务器没有虚拟主机所以

#讲第一台代理服务器的pc.conf文件复制到101的 /apps/nginx/下
scp /apps/nginx/conf.d/pc.conf 192.168.1.101:/apps/nginx/
//101服务器查看是否复制成功
[root@localhost nginx]# ls
 pc.conf #有pc文件复制成功
[root@localhost nginx]# mkdir conf.d
[root@localhost nginx]# mv pc.conf conf.d/
vim /apps/nginx/conf.d/pc.conf//修改服务器指向
vim /apps/nginx/conf/nginx//添加位置指向

image-20230903192215556

image-20230903192313785

测试

image-20230903193000098

浏览器访问

image-20230903193055288

检查日志

第一台代理服务日志

image-20230903193559902

第二台代理服务日志

image-20230903193735148

真实服务器

image-20230903193851977

1.6 http反向代理负载均衡

在上一个节中Nginx可以将客户端的请求转发至单台后端服务器但是无法转发至特定的一组的服务器,而且不能对后端服务器提供相应的服务器状态监测,Nginx 可以基于ngx_http_upstream_module模块提供服务器分组转发、权重分配、状态监测、调度算法等高级功能

#自定义一组服务器,配置在http块内
//定义一组名为wed的服务器
upstream   web { 
 server 192.168.1.100    后可跟调度算法
 server 192.168.1.101
}
​
location  / {
pass_proxy  http://web/
}
server address [parameters];
#配置一个后端web服务器,配置在upstream内,至少要有一个server服务器配置。
#server支持的parameters如下:
weight=number #设置权重,默认为1,实现类似于LVS中的WRR,WLC等
max_conns=number  #给当前后端server设置最大活动链接数,默认为0表示没有限制
max_fails=number  #后端服务器的下线条件,当客户端访问时,对本次调度选中的后端服务器连续进行检测多少次,如果都失败就标记为不可用,默认为1次,当客户端访问时,才会利用TCP触发对探测后端服务器健康性检查,而非周期性的探测
fail_timeout=time #后端服务器的上线条件,对已经检测到处于不可用的后端服务器,每隔此时间间隔再次进行检测是否恢复可用,如果发现可用,则将后端服务器参与调度,默认为10秒
backup  #设置为备份服务器,当所有后端服务器不可用时,才会启用此备用服务器 sorry server   自己不能转自己
down    #标记为down状态
resolve #当server定义的是主机名的时候,当A记录发生变化会自动应用新IP而不用重启Nginx
#示例
upstream backend {
   server backend1.example.com weight=5;     权重
   server 127.0.0.1:8080       max_fails=3  fail_timeout=30s;
   server unix:/tmp/backend3;
   server backup1.example.com backup;
}

Nginx 负载均衡配置及会话保持

调度算法
​
hash KEY [consistent];
#基于指定请求报文中首部字段或者URI等key做hash计算,使consistent参数,将使用ketama一致性
​
ip_hash;
#源地址hash调度方法,基于的客户端的remote_addr(源地址IPv4的前24位或整个IPv6地址)做hash计算,以实现会话保持
least_conn;
#最少连接调度算法,优先将客户端请求调度到当前连接最少的后端服务器,相当于LVS中的WLC
url_hash;
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器。一旦缓存住了资源,再次收到请求,就可以从缓存中读取。
轮询;
每个请求会按时间顺序逐一分配到不同的后端服务器
weight;
权重方式,在轮询策略的基础上指定轮询的比例。
fair;
此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。

实际操作

这里不好看实际效果就举例了

http {
 upstream web {
  server 192.168.1.101:80;
  server 192.168.1.103:80;
}
........
}
​
[root@localhost nginx]#vim conf.d/test.conf 
​
server {
     listen 80;
     server_name www.pc.com;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     root /data/nginx/pc;
     location /  {
     root /data/nginx/pc;
     proxy_pass http://web;           
}
​
}
​