流量复制
文章目录
传统线下压力测试,难模拟真实流量,尤其正常流量混杂着各色异常流量
线下压得好好的系统,上线后可能某天突然雪崩,说好能支撑 5 倍流量的系统重构,也许流量一翻倍就彻底挂了
ab,JMeter, LoadRunner, httperf 都没能解决如何正确模拟生产环境的流量
TcpCopy
c语言编写,使用复杂,依赖于 assistant server的intercept截获应答
思路
应用层面复制,如基于服务器的请求复制,实现相对简单,但
- 请求复制从应用层出发,穿透整个协议栈,挤占应用资源,比如宝贵的连接资源
- 测试跟实际应用耦合在一起
- 难支撑压力大的请求复制
- 难控制网络延迟
基于底层数据包的请求复制,无需穿透整个协议栈
从链路层抓请求包,从数据链路层发包,路程最短 IP层抓请求包,IP层发出去,路程一般
只要不走TCP,对在线的影响就会小得多。是 TCPCopy基本思路
演化
- 传统架构 rawsocket+iptable+netlink
- 新架构的 pacp+route
资源需求
- Online Server(OS):部署 TCPCopy,从数据链路层(pcap 接口)抓请求数据包,发包从IP层
- Test Server(TS):新架构把 intercept 工作从 TS 中 offload 出来。指定默认路由到辅助server,确保响应包路由到AS,而不是回包给 Online Server
- Assistant Server(AS):在数据链路层(pcap)截获到(测试机)的响应包,抽取出有用的信息,再返回给相应的 OS 上的 tcpcopy 进程,完成一次请求的复制
AS上设置, net.ipv4.ip_forward = 0
拓扑
数据流转顺序
生产环境和镜像环境数据传输流程图
- TCPcopy 从数据链路层 copy 端口请求,然后更改目的 ip 和目的端口
- 将修改过的数据包传送给数据链路层,并且保持 tcp 连接请求
- 通过数据链路层从 online server 发送到 test server
- 在数据链路层解封装后到达 nginx 响应的服务端口
- 等用户请求的数据返回结果后,回包走数据链路层
- 通过数据链路层将返回的结果从 test server 发送到 assistant server。注:test server 只有一条默认路由指向 assistant server
- 数据到达 assistant server 后被 intercept 进程截获
- 过滤相关信息将请求状态发送给 online server 的 tcpcopy,关闭 tcp 连接
普通场景
name | ip |
---|---|
client | 10.10.1.245 |
线上server | 10.10.1.240 |
测试server | 10.10.1.241 |
辅助server | 10.10.1.242 |
线上、辅助server 安装tcpcopy
yum -y install libpcap-devel
./configure --enable-advanced --enable-pcap --enable-dlinject(支持链路层pcap发包)
启动辅助服务器(10.10.2.242)tcpcopy server
/usr/local/bin/intercept -i eth1 -F 'tcp and src host 10.10.2.241 and src port 80' -d
复制线上流量到测试机 -s指定辅助服务器(intercept serve),-i 指定抓包网卡
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d
测试服务器加路由:
route add -host 10.10.1.245 gw 10.10.2.242
客户端(10.10.1.245)的请求走辅助服务器(10.10.2.242),外网应用,测试服务器也可默认路由设成辅助服务器
route del default gw 原外网网关IP
route add -net 0.0.0.0/0 gw xxx
复制N倍流量到测试机
第一种:
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d -n 3
第二种:
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d -f 1
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d -f 2
复制 20% 流量
/usr/local/bin/tcpcopy -x 10.10.2.240:80-10.10.2.241:80 -s 10.10.2.242 -i eth1 -d -r 20
lvs场景
name | ip |
---|---|
线上server | 10.10.2.100 外网IP:x.x.x.x |
测试server1 | 10.10.2.241 |
测试server2 | 10.10.2.242 |
辅助sever | 10.10.2.246 |
LVS 调度 | 10.10.2.240 |
VIP | 10.10.2.245 |
说明:
1、240 LB,241、242 realserver
2、realserver 部署线上应用,修改默认网关为辅助server
route del default gw 10.10.2.1
route add default gw 10.10.2.246
要保证lvs可以正常监控到realserver,就是LB和realserver网络正常
3、辅助server在上面运行intercept
/usr/local/bin/intercept -i eth1 -F 'tcp and src port 80' -d
4、线上server运行tcpcopy
/usr/local/bin/tcpcopy -x 80-10.10.2.245:80 -s 10.10.2.246:36524 -i eth0 -d
复制eth0数据包到 vip 245,测试应用端口号80,intercept机器ip 246,端口36524
要复制流量很大,辅助server可扩展,两台辅助server 246、247,线上server的tcpcopy
/usr/local/bin/tcpcopy -x 80-10.10.2.245:80 -s 10.10.2.246:36524,10.10.2.247:36524 -i eth0 -d
cloud VPC环境
线上连接数量不多(非短连接应用),可用tcpcopy -c参数设置成tcpcopy所在机器的ip地址,就无需在测试服务器端设置路由,在tcpcopy所在机器设置iptables,干掉测试服务器回来的响应
aws可用此测试MySQL应用
云平台环境,用传统架构,如何编译
tcpcopy方式不变,intercept改变:
./configure --traditional
等同1.0以下版本默认方式
intercept将采用iptables的方式来获取响应包,并干掉响应包
内核<3.5,默认采用IP Queue
modprobe ip_queue # if not running
iptables -I OUTPUT -p tcp --s端口 -j QUEUE
./intercept
Or
内核>=3.5,默认采用NFQueue
iptables -I OUTPUT -p tcp --s端口 -j NFQUEUE
./intercept
Tips
- 性能
想用好 tcpcopy,需要熟悉系统知识,包括如何高效率抓包,如何定位系统瓶颈,如何部署测试应用系统,如何抓包分析
建议连接数量非常多的时候,应关闭 内核模块 ip_conntrack
tcpcopy,默认从 ip 层发包,会被 ip_conntrack 干涉,可 –enable-dlinject 发包,避开ip层的ip_conntrack,或者对线上应用系统端口设置 NOTRACK,为性能考虑,是一种好的运维习惯
如果没有报“ip_conntrack: table full, dropping packet”,一般无需去操心ip_conntrack
- 不提取 7 层信息
按域名区分拷贝流量,省得把不在本次压测范围内的工程打挂
但 tcpcopy 的原理是在 ip 层拷贝,不提取 7 层的信息,就是说,Nginx server上部署 TCPCopy,只能是将所有流量拷贝到镜像环境的 Nginx 上。反正没有配置对应的 server,或者 server 停掉,这种处理不了的流量就丢弃掉
- pcap_inject发包两点好处
1) 避开在线机器在IP层的防火墙设置
tcpcopy进程复制过来的包源IP还是线上客户端的IP,在线机器上可能会被直接扔掉(比如只允许real server 源ip发回包源?),不能改变防火墙设置的情况下,pacp_inject直接从数据链路层发包,可以避开IP层复杂的防火墙设置
2) 提高30%的性能
但是pcap_inject发包需要知道数据包从在线机器发出下一跳的网卡地址,需要使用tcpdump工具抓包获取
tcpcopy -x 在线端口号@在线机器的出口网卡地址-测试机器的IP地址:测试机器的端口@下一跳网卡地址 -s 运行intercept的机器IP地址 -o 出口网卡设备 -i 抓包网卡设备
gor
Go语言编写,简单易用
现叫goreplay
支持流量放大和缩小、频率限制,正则表达式过滤流量,修改HTTP请求头,比如替换User-Agent, 或者增加某些HTTPHeader
将流量存入ES进行实时分析, –output-http-elasticsearch localhost:9200/gor
请求回放会模拟真实情况,如 request1 和 request2 间隔 3 秒,回放也会保留3秒间隔,准确呈现访问的负载状况
demo
- 实时转发
线上系统8000 ,测试系统8001
gor --input-raw :8000 --output-http="http://localhost:8001"
- 录制 回放 过滤
gor --input-raw :8000 --output-file=requests.gor
gor --input-file requests_0.gor --output-http="http://localhost:8001"
过滤
url过滤
gor --input-raw :8080 --output-http staging.com --http-allow-url /api
头过滤
gor --input-raw :8080 --output-http staging.com --http-allow-header api-version:^1.0\d
2倍
gor --input-file "requests.gor|200%" --output-http "staging.com"
频率控制
gor --input-tcp :8080 --output-http "http://staging.com|1000%"
缩小
gor --input-raw :8080 --output-tcp "staging.com:8080|10%"
定义参数
$ gor --input-raw :80 --output-http 192.168.2.6:8080 --http-allow-method POST --http-set-header 'User-Agent: Gor' -output-http-workers=1 -http-allow-url test.php
复制两份到不同的测试服务
gor --input-tcp :8080 --output-http "http://staging.com" --output-http "http://dev.com"
loadbalance
gor --input-tcp :8080 --output-http "http://staging.com" --output-http "http://dev.com" --split-output true
demo file server
gor file-server :8000
预发布系统压力测试
Api服务器上启动Gor Listener,复制80端口的流量,转发到预发布服务器的端口
gor --input-raw :80 --output-tcp preview:80
预发布启动Gor,将请求转发到预发布服务器
gor --input-tcp preview:80 --output-http http://staging.com
也可以将listener和replayer合二为一
gor --input-tcp preview:28020 --output-http http://staging.com
windows 编译
参考
https://stackoverflow.com/questions/38047858/compile-gopacket-on-windows-64bit
win 下编译要特别处理下,否则
go build -v github.com/buger/goreplay
github.com/buger/goreplay
# github.com/buger/goreplay
c:\go\pkg\tool\windows_amd64\link.exe: running gcc failed: exit status 1
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../lib/libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../lib\libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../..\libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../lib/libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../libwpcap.a when searching for -lwpcap
C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/5.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lwpcap
tdm64-gcc
http://tdm-gcc.tdragon.net/download
Winpcap
https://www.winpcap.org/install/default.htm
Winpcap developer’s pack
https://www.winpcap.org/devel.htm
extract include* to TDM-GCC-64\include
C:\Windows\SysWOW64\wpcap.dll
C:\Windows\SysWOW64\Packet.dll
gendef wpcap.dll
gendef packet.dll
dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libwpcap.a --input-def wpcap.def
dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libpacket.a --input-def packet.def
copy libwpcap.a and libpacket.a to TDM-GCC-64\lib
nginx
post action
location / {
proxy_pass http://prod.env:8080;
post_action @post_action;
}
location @post_action {
proxy_pass http://dst_host:dst_port;
}
ngx_http_mirror_module
Nginx 1.13.4 新增,对原始请求创建后台镜像,镜像子请求的输出会被忽略 比tcpcopy使用更方便
配置块可在 http, server, location
# original配置
location / {
mirror /mirror;
mirror /mirror2;
mirror_request_body off; //是否镜像请求Body部分
proxy_pass http://prod.env:8080;
}
# mirror配置 //mirror不会输出http返回内容
location /mirror {
internal; //指定此location只能被内部的请求调用,外部的调用请求会返回404
proxy_pass http://test.env:8000$request_uri; //指定上游Server的地址
proxy_set_header X-Original-URI $request_uri; //镜像流量的头部
}
location /mirror2 {
internal;
proxy_pass http://test2.env:8000$request_uri;
proxy_set_header X-Original-URI $request_uri;
}
dubboCopy
Dubbo 技术栈,用TCPCopy将线上流量复制到线下测试环境,很多时候进行引流的时候,只希望复制部分服务的流量。比如想将下单服务的引入进来,但并不想将支付订单的流量也引入进来,这样可能需要搭建一个很复杂的环境
另外有时只想针对单个服务进行测试,只复制单个服务或符合条件的流量,可以更好的隔离其他服务对测试的影响
使用TCPCopy的原始结构
使用DubboCopy之后的结构
TCPCopy直接面对的是 dubboCopy proxy server,不直接跟线下测试服务器交互,使用者透明,降低了使用的复杂度
需要提供可供调用的接口在线上机器启动TCPCopy
DubboCopy可部署多台,可用类似负载均衡的方式发送不同的命令到不同的线上机器,将流量均衡的复制到各个DubboCopy
ref
tcpcopy架构漫谈 三代架构
TCPCopy使用总结
https://winway.github.io/2017/10/17/tcpcopy-introduce/
dubbocopy
http://www.aizhuanji.com/a/PxDnAqVY.html
美团点评酒店后台故障演练系统
https://tech.meituan.com/thrifycopy_and_faultdrill_system.html