Nginx 流量镜像实践分享

流量镜像

出于安全检测、性能分析和交易仿真的需求,往往会考虑对请求流量做旁路镜像,进而分析处理镜像流量,避免对主体访问产生影响。这类需求场景包括但不限于APM流量分析、蜜罐模拟、交易仿真、问题复现和数据拷贝,等等。
为了实现流量镜像,方案有多种多样,有硬件和软件的方式,也有代理和网关的模式,大致有以下几类。

  • 交换机端口镜像
    部分交换机可以使用端口镜像的功能,将指定端口的收发报文复制到另一个目的端口,而目的端口的下行设备则接入相关的服务,处理镜像的流量。

    优点:通过物理端口实现,能耗较低,根据交换机的产品能力,支持二、三、四层的报文镜像。
    缺点:动态配置不够灵活,需要机房网络人员参与配合。

  • 软件网络镜像
    SDN架构同样可以利用软件来实现类似的端口镜像,例如Open-vSwitch (OVS) 可以使用 ovs-vsctl 命令的 mirrors 参数将 select-src-portselect-dst-port 流量转发到 output-port 来进行复制,或者指定镜像到 output-vlan 指定的 vlan,再由其他 bridge 对此 vlan 做分发。

    优点:利用软件定义实现,贴合云原生的架构,并且配置比较灵活。
    缺点:学习成本大,软件的能耗会比硬件要高,复杂的镜像配置也可能加大软件 bug 的命中率。

  • 代理镜像
    现在数据中心的大部分实体服务都是经过负载均衡模式进行请求分发,例如 Nginx、OpenResty 和 BFE 等 LB 组件承接了出入局流量的代理。应用层的流量,能够在代理阶段,借助负载均衡软件来实现镜像。

    优点:应用层负载均衡的流量镜像,处理四层协议比较友好,可以预处理后再进行预处理。
    缺点:镜像流量与实体流量都在同一个软件上调度分发,镜像流量的转发可能会影响原有请求服务稳定。

  • API 网关镜像
    API 网关可以在路由对应服务请求的同时,也可以配置镜像,常用的 kong 有 kong-plugin-http-mirror 模块,apisix 有 proxy-mirror 模块。

    优点:API 网关在 Kubernetes Ingress 方案里非常常用,这种镜像模式与容器化服务结合使用比较平滑。
    缺点:管理成本会比传统 LB 更繁琐,如果是 Operator 模式,需要一定的二开能力来开发相应的 CRD

Nginx

这里选择 Nginx 的镜像功能来作为实施方案,对使用四层协议 HTTP 的服务来设计镜像方案。
Nginx 的核心模块 ngx_http_mirror_module 支持将原始请求复制为子请求来做转发,官方的版本里是默认编译了此模块的,详细的模块介绍可参考官方文档。
这里介绍下该模块的使用,其涉及的参数比较少,几乎大部分使用场景都是以下配置能够覆盖的。
直接镜像到域名地址:

server {
  # ########## MIRROR
  location /mirror_proxy {
    # 如果使用域名,需要添加 DNS 解析源
    resolver 8.8.8.8;
    # 标记为内部地址
    internal;
    # 转发请求体
    proxy_pass_request_body on;
    proxy_pass http://example.com$request_uri;
  }
  # ######### EOF MIRROR

  location / {
    # 开启镜像
    mirror /mirror_proxy;
    # 镜像请求体
    mirror_request_body on;
    proxy_pass http://src/;
  }
}

痛点

Nginx mirror 是将 request 派生出一个 subrequest,mirror 的 endpoint 处理 subrequest 的耗时也会算入原始请求的响应时长内,如果处理镜像服务的网络质量不稳定或者处理耗时过长,会影响原始服务的 P99 时延。
因为是通过 $request_uri 获取参数和 mirror_request_body 开启请求体复制,location 的 rewrite 无法很好的处理镜像的路径。当需要 rewrite 后转发到目标域名时,配置起来比较头疼。

优化

Nginx 配置优化

为了减少 subrequest 的耗时,Nginx 在配置镜像的 location 时,配置较小的超时参数,兜底镜像流量处理时延时抖动的影响。

upstream src {
  server 127.0.0.1:9100;
}

server {
  # ########## MIRROR
  location /mirror_proxy {
    resolver 8.8.8.8;
    internal;
    proxy_pass_request_body on;
    # 连接超时
    proxy_connect_timeout 600ms;
    # 读取响应超时
    proxy_read_timeout 2s;
    proxy_pass http://mirror.com$request_uri;
  }
  # ######### EOF MIRROR

  location / {
    mirror /mirror_proxy;
    mirror_request_body on;
    proxy_pass http://src/;
  }
}

转发组件

Nginx 的超时参数只是尽可能地减少复制流量的处理耗时风险,无法完全消除异常条件下的木桶效应,每个请求都必须经历完整的超时,原始请求的 P99 时延也会明显增加。而且在不使用 Lua 扩展的前提下,正如之前所说,Nginx mirror 模块的处理灵活性并不理想。
为了追求更少、更稳定的旁路 subrequest 耗时,以及可扩展更多的管理,可以使用一个转发组件,劫持镜像请求并加入队列后,快速返回 HTTP 响应。并将此组件通过一个 Nginx 来代理,相关的 rewrite 需求在该”影子“ Nginx 里配置,不影响实际链路中的 Nginx 节点。
该”影子“Nginx的配置可如下:

# 转发组件
upstream mirror_proxy {
  server 10.10.10.1:9201;
  server 10.10.10.2:9201;
  # 使用本 Nginx 作为兜底的 backup,当外部组件失效时通过本 Nginx 直接返回
  server 127.0.0.1:8080 backup;
}

server {
  # 接收镜像流量的端口
  listen 9102 reuseport default_server;
  server_name _;
  expires -1;

  location / {
    proxy_pass http://mirror_proxy/;
  }
}

server {
  listen 8080;
  server_name _;

  # 直接返回 2xx
  location / {
    return 200;
  }
}

转发组件需要支持更加简单的行为管理,可以选择使用特定的HTTP Header来对接,主要有如下的字段:

Header 描述 作用
X-Mirror-Dest 声明需要转发的目标地址。 可以灵活控制目标流向,以及在Nginx里以处理变量的方式来实现rewrite。
X-Mirror-Pass 标记当前请求是否已经转发。 避免出现环路。
X-Mirror-Rate 转发采样率。 设置采样百分比来对镜像流量做采样压缩。

在负责复制流量的 Nginx 上添加以下配置即可:

proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Mirror-Dest $mirror_prefix$request_uri;

实践

Nginx 流量镜像实践分享

具体实现时,使用 Go 的 FastHTTP 库来完成高性能的处理,另外参考了 github.com/VictoriaMetrics/VictoriaMetrics/lib/persistentqueue 持久库添加了镜像流量堆积处理的能力。
观测使用情况,负载情况整体良好。

Nginx 流量镜像实践分享

转发率和堆积情况也平稳。

Nginx 流量镜像实践分享


原文始发于微信公众号(AcidPool):Nginx 流量镜像实践分享

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/171057.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!