将生产环境的流量拷贝到预上线环境或测试环境,这样做有很多好处,比如:
可以验证功能是否正常,以及服务的性能;
用真实有效的流量请求去验证,又不用造数据,不影响线上正常访问;
这跟灰度发布还不太一样,镜像流量不会影响真实流量;
可以用来排查线上问题;
重构,假如服务做了重构,这也是一种测试方式;
为了实现流量拷贝,Nginx提供了ngx_http_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.
Example Configuration
location / { mirror /mirror; proxy_pass http://backend; } location = /mirror { internal; proxy_pass http://test_backend$request_uri; }
Directives
Syntax: | mirror |
---|---|
Default: | |
Context: | http , server , location |
Sets the URI to which an original request will be mirrored. Several mirrors can be specified on the same level.
Syntax: | mirror_request_body |
---|---|
Default: | |
Context: | http , server , location |
Indicates whether the client request body is mirrored. When enabled, the client request body will be read prior to creating mirror subrequests. In this case, unbuffered client request body proxying set by the proxy_request_buffering, fastcgi_request_buffering, scgi_request_buffering, and uwsgi_request_buffering directives will be disabled.
location / { mirror /mirror; mirror_request_body off; proxy_pass http://backend; } location = /mirror { internal; proxy_pass http://log_backend; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; }
mirror模块可以帮助我们创建一份镜像流量,比如在生产环境下处理一些请求,这些请求可能要同步的copy一份到我的测试环境当中或者开发环境当中做处理,mirror模块就可以实现。
每当我们的请求到了nginx之后,可以生成子请求。这个子请求可以通过反向代理去访问我们其他的环境,比如我们的测试环境。而对测试环境和其他环境返回的内容我们是不做处理的,因为只做镜像的。
利用mirror模块,业务可以将线上实时访问流量拷贝至其他环境,基于这些流量可以做版本发布前的预先验证,进行流量放大后的压测等等。(复制线上真实流量,在不影响真实业务前提下,利用复制流量来做故障分析、性能定位、迁移评估等功能。)
我是这样理解的,这里,mirror本意是镜子、镜像,这里可以理解就像一个镜像站点一样,将所有的请求都收集起来,这个镜像就代表了所有真实有效的原始请求。有了这个镜像,后续我们才可能用这个镜像去做一些事情,比如重现一下所有的请求,这就实现了把线上的流程复制到别的地方。
192.168.179.99流量镜像复制到上游服务192.168.179.100,配置如下:
192.168.179.99源站配置配置如下
[root@www ~]# echo "mirror.txt" > /usr/local/nginx/html/mirror.txt
server {
listen 80;
server_name localhost;
location / {
mirror /mirror;
mirror_request_body off;
}
location =/mirror{
proxy_pass http://192.168.179.100$request_uri;
#原始uri不会镜像,可以通过#$request_uri变量取得原始请求的uri
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
192.168.179.100镜像站配置
server{
listen 80;
server_name localhost;
charset utf-8;
error_page 404 =200 /404.html;
location /{
return 200 'mirror respone';
}
}
客户端192.168.179.102去请求192.168.179.99资源
[root@localhost ~]# curl 192.168.179.99/mirror.txt
mirror.txt
-----------------------------------------------------------------------------------------
192.168.179.99日志如下:
192.168.179.102 - - [29/Apr/2020:11:30:09 +0800] "GET /mirror.txt HTTP/1.1" 200 11 "-" "curl/7.29.0" "-" --通过日志可以看到99收到了该请求
192.168.179.100日志如下:
192.168.179.99 - - [29/Apr/2020:11:30:09 +0800] "GET /mirror.txt HTTP/1.0" 200 14 "-" "curl/7.29.0" "192.168.179.102" --上游服务也收到该请求,可以看到时间也是一样
可以看到上游服务和代理服务都拿到到了客户端相同的请求这样实现了流量的拷贝
server {
listen 80;
server_name web1.www.com;
# 源站配置
location / {
access_log /data/nginx/1.14.1/logs/web1/access.log accesslog;
mirror /mirror;
# 多加一份mirror,流量放大一倍
mirror /mirror;
mirror_request_body on;# Indicates whether the client request body is mirrored. default value is on.
proxy_pass http://web1.upstream.name;
}
# 镜像站点配置
location /mirror {
internal;
#internal 指定此location只能被“内部的”请求调用,外部的调用请求会返回”Not found” (404)
#跳转到下面的内部server
proxy_pass http://mirror.web1.upstream.name$request_uri;
proxy_pass_request_body on;
# Indicates whether the original request body is passed to the proxied server. default #value is on
proxy_set_header X-Original-URI $request_uri; # reset uri
}
}
镜像配置不正确,导致复制操作没正常执行,此时nginx可能缺少错误日志,严重影响调试,所以非常建议配置镜像日志。mirror中不支持配置access_log,解决方法:mirror-location跳转到server,在server中配置accesslog。
server {
listen 80;
server_name web1.www.com;
# 源站配置
location / {
access_log /data/nginx/1.14.1/logs/web1/access.log accesslog;
mirror /mirror;
mirror_request_body off;# Indicates whether the client request body is mirrored. default value is on.
proxy_pass http://web1.upstream.name;
}
# 镜像站点配置
location /mirror {
internal; # 内部配置
# 跳转到下面的内部server
proxy_pass http://127.0.0.1:10901$request_uri;
proxy_pass_request_body off; # Indicates whether the original request body is passed to the proxied server. default value is on
# Content-Length必须配置在mirror中否则无效
proxy_set_header Content-Length "";
# mirror_request_body/proxy_pass_request_body都设置为off,则Conten-length需要设置为"",否则有坑
proxy_set_header X-Original-URI $request_uri; # 使用真实的url重置url
}
}
server {
# server没法设置为内部
listen 127.0.0.1:10901;
location / {
# 判断放在server,使得post请求日志可以记录
if ($request_method != GET) {
return 403;
}
access_log /data/nginx/1.14.1/logs/web1/access.log accesslog;
proxy_pass http://mirror.web1.upstream.name;
}
}