Nginx mirror流量镜像-流量复制模块

释放双眼,带上耳机,听听看~!
Nginx 的镜像功能并不只是简单的复制,复制的镜像请求和原始请求是相关联的,若镜像请求没有处理完成,原始请求就会被阻塞。也就是说,如果镜像的子请求出现问题,也会影响原始的请求,所以,开启流量镜像模式做为测试流量时,要监控 Nginx 性能指标,发现请求反馈异常要即时处理。
🤖 由 ChatGPT 生成的文章摘要

介绍

Nginx 从 1.13.4 版本开始引入了 ngx_mirror_module 模块:

The ngx_http_mirror_module module (1.13.4) implements mirroring of an original request by creating background mirror subrequests. Responses to mirror subrequests are ignored.

利用 mirror 模块,业务可以将线上实时访问流量拷贝至其他环境,基于这些流量可以做版本发布前的预先验证,进行流量放大后的压测。

官方文档:https://nginx.org/en/docs/http/ngx_http_mirror_module.html

Nginx 如何实现流量镜像

Nginx mirror流量镜像-流量复制模块

ngx_mirror_module模块会对原请求进行复制,产生一个镜像的子请求,镜像出来的子请求的响应会被忽略。有了镜像请求后,我们可以将请求引流到其他的服务当中去,比如线上请求引到测试环境,这样,当线上环境出现了问题时,我们就能对其进行排查。

  • Nginx 的镜像功能使用的是 mirror 指令,通过 mirror 指令实现请求的监听和拷贝。

Nginx 的镜像功能并不只是简单的复制,复制的镜像请求和原始请求是相关联的,若镜像请求没有处理完成,原始请求就会被阻塞。也就是说,如果镜像的子请求出现问题,也会影响原始的请求,所以,开启流量镜像模式做为测试流量时,要监控 Nginx 性能指标,发现请求反馈异常要即时处理。

安装Nginx

[root@abcdocker tmp]# bash https://d.frps.cn/file/tools/nginx/nginx_install.sh
[root@abcdocker tmp]# bash nginx_install.sh

当然也可以自己yum或者采用其它方式安装Nginx,我这里为编译安装

查看Nginx版本信息

[root@abcdocker tmp]# /opt/nginx-1.22.1/sbin/nginx  -V
nginx version: nginx/1.22.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.1.1t  7 Feb 2023
TLS SNI support enabled
configure arguments: --prefix=/opt/nginx-1.22.1 --with-openssl=/usr/local/src/openssl-1.1.1t --with-pcre=/usr/local/src/pcre-8.45 --with-zlib=/usr/local/src/zlib-1.2.13 --with-http_ssl_module --with-http_stub_status_module --with-stream --with-http_stub_status_module --with-http_gzip_static_module

安装mirror模块

mirror 模块在 Nginx 1.13.4 以后的版本中默认是启用,不需要额外模块安装

为了模拟后端我这里docker run 2个mirror-backed镜像

# 8081为正常后端节点
docker run -d  --name web-01 --restart always -p 8081:80  nginx

#8082为模拟流量复制节点
docker run -d  --name web-02 --restart always -p 8082:80  nginx

进入nginx容器配置静态页面

web-01配置

docker exec  -it web-01 bash
echo "abcdocker web" >/usr/share/nginx/html/index.html

[root@abcdocker ~]# curl localhost:8081
abcdocker web

web-02配置

docker exec  -it web-02 bash
echo "nginx mirror" >/usr/share/nginx/html/index.html

[root@abcdocker ~]# curl localhost:8082
nginx mirror

配置mirror模块

向后端添加web-01服务,并将发往源后端的流量复制一份到web-02后端。

Nginx复制所有请求流量

不做任何过滤以及相关限制,只是将web-01节点流量复制一份到web-02节点中

nginx.conf配置文件如下

upstream backend {
    server 127.0.0.1:8081;
}

upstream mirr_backend {
    server 127.0.0.1:8082;
}

server {
    server_name localhost;
    listen 80;

    location / {
        mirror /nginx-mirror;
        mirror_request_body on;
        proxy_pass http://backend;
    }

    location = /nginx-mirror {
        internal;
        proxy_pass http://mirr_backend$request_uri;
        proxy_pass_header Set-Cookie;
        proxy_set_header  Host  $host;
        proxy_set_header  X-Real-Ip  $remote_addr;
        proxy_set_header  X-Forwarded-For  $remote_addr;
        proxy_set_header  X-Original-URI  $request_uri;
    }

}

参数解释

  • mirror /nginx-mirror; #指定镜像uri,也可以修改为其它地址,需要和localtion = /nginx-mirror 匹配
  • mirror_request_body on; #指定是否镜像请求body部分,请求自动缓存;
  • internal #指定此location只能被“内部的”请求调用,外部的调用请求会返回”Not found” (404)

访问测试

[root@abcdocker ~]# curl localhost
abcdocker web
[root@abcdocker ~]# curl localhost
abcdocker web
[root@abcdocker ~]# curl localhost
abcdocker web

查看web-01日志

Nginx mirror流量镜像-流量复制模块

查看web-02日志

Nginx mirror流量镜像-流量复制模块

Nginx后端节点响应慢

当mirror节点down掉后,Nginx会丢弃mirror响应,但是如果镜像请求响应很缓慢,原始请求就会被阻塞,这时会影响主请求的响应速度的。我们要考虑使用Nginx复制流量分流来限制发送的流量

Nginx复制流量分流

生产环境中流量会很大,某些场景下我们只想获取部分流量到mirror节点中,但是mirror 模块无法进行流量分流,所以我们需要使用 Split Client模块来实现

Split Client 模块是 Nginx 的一个插件,它通过识别客户端的特定属性,例如 IP 地址、用户代理或 Cookie 等,将请求分发到不同的后端服务器。这种分流的方式可以根据客户端的特征将请求导向不同的服务集群,从而实现更精细化的流量控制和管理。

upstream backend {
    server 127.0.0.1:8081;
}

upstream mirr_backend {
    server 127.0.0.1:8082;
}

split_clients $remote_addr $mirror_backend {  
    50% mirr_backend; 
    *   ""; 
    }

server {
    server_name localhost;
    listen 80;

    location / {
        mirror /nginx-mirror;
        mirror_request_body on;
        proxy_pass http://backend;
    }

    location = /nginx-mirror {
        internal;
        if ($mirror_backend = "") {
             return 400;
        }
        proxy_pass http://mirr_backend$request_uri;
        proxy_pass_header Set-Cookie;
        proxy_set_header  Host  $host;
        proxy_set_header  X-Real-Ip  $remote_addr;
        proxy_set_header  X-Forwarded-For  $remote_addr;
        proxy_set_header  X-Original-URI  $request_uri;

    }

}

实现只复制部分流量到镜像后端

  • split_clients 会将左边的变量 $remote_addr(requests remote address)经过 MurmurHash2 算法进行哈希,
  • 得出的值如果在前 50%(从 0 到 2147483500),那么 $mirror_backend 的值为 test_backend
  • 如果不在前 50%,那么 $mirror_backend 的值为空字符 ""
  • 50% test_backend; # 将流量复制到镜像后端
  • * ""; 如果 $mirror_backend 变量的值为空字符串,就不复制流量
  • 因为镜像请求的错误响应并不会影响原始请求,所以丢弃镜像请求并返回错误响应是很安全的

Nginx不允许复制POST请求流量

upstream backend {
    server 127.0.0.1:8081;
}

upstream mirr_backend {
    server 127.0.0.1:8082;
}

server {
    server_name localhost;
    listen 80;

    location / {
        mirror /nginx-mirror;
        mirror_request_body off;
        proxy_pass http://backend;
    }

    location = /nginx-mirror {
        internal;
        # 判断请求方法,不是GET返回403
         if ($request_method !~* GET) {
            return 403;

        }

        proxy_pass http://mirr_backend$request_uri;
        proxy_pass_request_body off;        
        # mirror_request_body和proxy_pass_request_body都设置为off,则Conten-length需要设置为""
        proxy_set_header Content-Length "";        
        proxy_set_header X-Original-URI $request_uri; # 使用真实的url重置url  
    }

}

测试流量

curl -H "Content-Type: application/json" -X POST -d '{"msg":"OK!" }' "http://192.168.0.5"

说明: 需要使用动态的服务后端,静态文件不支持post请求,Nginx会返回405

Nginx拷贝流量放大

upstream backend {
    server 127.0.0.1:8081;
}

upstream mirr_backend {
    server 127.0.0.1:8082;
}

server {
    server_name localhost;
    listen 80;

    location / {
        mirror /nginx-mirror;
        # 多加一份mirror,流量放大一倍     
        mirror /nginx-mirror;   
        mirror_request_body on;
        proxy_pass http://backend;
    }

    location = /nginx-mirror {
        internal;
        proxy_pass http://mirr_backend$request_uri;
        proxy_pass_request_body on;        
        proxy_set_header Content-Length "";        
        proxy_set_header X-Original-URI $request_uri; 
    }

}

我们请求一次流量

[root@abcdocker ~]# curl localhost?abcdocker
abcdocker web

web-01容器响应日志如下

web-01容器只响应了一个正常的请求

172.17.0.1 - - [09/Oct/2023:09:00:26 +0000] "GET /?abcdocker HTTP/1.0" 200 14 "-" "curl/7.29.0" "-"

mirror容器流量如下

172.17.0.1 - - [09/Oct/2023:09:00:26 +0000] "GET /?abcdocker HTTP/1.0" 200 13 "-" "curl/7.29.0" "-"
172.17.0.1 - - [09/Oct/2023:09:00:26 +0000] "GET /?abcdocker HTTP/1.0" 200 13 "-" "curl/7.29.0" "-"

#影响了2次,来模拟多流量的场景

如果我们需要多台节点来接收消息,只需要将mirror /nginx-mirror修改为mirror /nginx-mirror1,对应的location也修改为location = /nginx-mirror1即可

Nginx后端节点响应超时

我们在Nginx后端配置了mirror节点,我们将它down掉,检查是否会影响正常的流量请求

[root@abcdocker ~]# docker stop web-02
web-02
[root@abcdocker ~]# curl localhost
abcdocker web

测试结果: mirror节点down掉并不影响服务正常访问

Nginx流量拷贝的注意事项

如果mirror_request_body或者proxy_pass_request_body设置为 off,则Content-Length必须设置为"" 因为nginx(mirror_request_body)处理post请求时,会根据Content-Length获取请求体, 如果Content-Length不为空,而由于mirror_request_body或者proxy_pass_request_body设置为off, 处理方以为post有内容,当request_body中没有,处理方会一直等待至超时,则前者为off,nginx会报upstream请求超时。

给TA打赏
共{{data.count}}人
人已打赏
NGINX

Nginx中间件安全基线配置与操作指南

2023-9-15 9:56:31

NGINXUbuntu

Ubuntu Nginx 1.25.2开启配置QUIC和HTTP/3

2023-10-10 0:31:24

2 条回复 A文章作者 M管理员
  1. 浮光

    👍👍👍

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索