资讯动态

Envoy 流量抓取(Traffic Tapping)实战:tap 传输套接字的配置、原理与 PCAP 分析

发布时间:2026/9/14 22:59:44 来源:尧图企业网站定制
Envoy 流量抓取Traffic Tapping实战tap 传输套接字的配置、原理与 PCAP 分析【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本篇指南基于 Envoy 官方文档docs/root/operations/traffic_tapping.rst展开完整讲解 Envoy 两种实验性流量抓取扩展的定位与差异、envoy.transport_sockets.tap传输套接字在 Listener/Cluster 上的完整 YAML 配置方法、缓冲数据上限与截断机制、流式streaming与采样sampling行为、统计指标含义以及如何用tap2pcap把 protobuf trace 文件转换为 Wireshark 可分析的 libpcap 文件。读完并结合仓库源码 tap.cc、tap.proto 后你可以独立完成 L4 层流量旁路记录并理解数据在doRead/doWrite拦截点是如何被复制进 trace 的。两种抓取扩展的定位HTTP tap 过滤器 vs tap 传输套接字Envoy 当前提供两个实验性扩展用于抓取tap流量HTTP tap 过滤器envoy.filters.http.tap工作在 L7 层抓取经过 HTTP 连接管理器的请求/响应。官方建议读者参考其过滤器文档获取细节参见仓库文档config_http_filters_tapping相关章节。tap 传输套接字扩展envoy.transport_sockets.tap工作在 L4 层能够拦截连接上的原始流量并写入 protobuf trace 文件TraceWrapper消息。本文档主体即围绕这一扩展的配置展开。两者能力上有部分重叠例如 streaming 语义、截断标记但传输套接字抓取的是未经上层协议解析的原始字节流适用于需要观察 TLS、gRPC、自定义二进制协议等任意 L4 流量的排障场景。注意tap 传输套接字目前处于实验阶段且处于积极开发中。匹配条件、输出配置、输出 sink 的种类都还非常有限能力会随版本扩展配置结构也可能发生变化。配置位置Listener 还是 Cluster抓取可以配置在两类传输套接字位置Listener 的filter_chain.transport_socket拦截下游downstreamL4 连接即客户端发往 Envoy 的流量Cluster 的transport_socket拦截上游upstreamL4 连接即 Envoy 发往后端服务的流量。具体做法是给对应位置的transport_socket增加一个envoy.transport_sockets.tap传输套接字配置消息类型为envoy.extensions.transport_sockets.tap.v3.Tap。从 tap.proto 的定义可以看到该消息有三个字段message Tap { // 必填公共配置匹配条件 match 输出配置 output_config common.tap.v3.CommonExtensionConfig common_config 1 [(validate.rules).message {required: true}]; // 必填被包装的底层传输套接字 config.core.v3.TransportSocket transport_socket 2 [(validate.rules).message {required: true}]; // 可选传输套接字抓取的附加配置 SocketTapConfig socket_tap_config 3; }也就是说 tap 套接字是一个“包装器”它必须内嵌一个真实的底层传输套接字明文raw_buffer或 TLS 上下文抓取逻辑叠加在底层套接字之上。明文 TCP 套接字的 Listener 端配置以下示例来自仓库自带示例文件 traffic_tapping_plain_text.yaml对应原文档引用的第 29–45 行transport_socket片段监听 8000 端口将所有流量明文转发至本地 80 端口的service-http集群同时对下游连接开启抓取static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 8000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: AUTO stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: app domains: - * routes: - match: prefix: / route: cluster: service-http http_filters: - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router transport_socket: # L4: 下游连接在此被包装 name: envoy.transport_sockets.tap typed_config: type: type.googleapis.com/envoy.extensions.transport_sockets.tap.v3.Tap common_config: static_config: match: any_match: true # 命中全部连接当前匹配条件集非常有限 output_config: sinks: - format: PROTO_BINARY # trace 输出为 protobuf 二进制 file_per_tap: path_prefix: /some/tap/path # 每个唯一套接字生成 path_0.pb 等文件 transport_socket: # 被包装的真实底层套接字 name: envoy.transport_sockets.raw_buffer typed_config: type: type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer要点说明common_config.static_config.match.any_match: true表示对每条连接都尝试匹配并抓取。原文档提醒“目前匹配条件的集合非常有限”这是当前实验状态的主要约束output_config.sinks输出 sink 为PROTO_BINARY格式、按“每个 tap 一个文件”file_per_tap写入path_prefix目录。每个唯一套接字实例都会生成一个以path_prefix为前缀的 trace 文件例如/some/tap/path_0.pbtransport_socket内层这里是raw_buffer明文套接字即被抓取的“宿主”传输通道。TLS 套接字的 Cluster 端配置对上游 TLS 连接抓取时tap 配置挂在 Cluster 的transport_socket上且内层 TLS 上下文会取代replace该 Listener/Cluster 上已有的 TLS 配置。以下示例来自 traffic_tapping_ssl.yaml对应原文档引用的第 44–60 行片段抓取service-https集群到本地 8080 端口的上游 TLS 流量clusters: - name: service-https type: STATIC lb_policy: ROUND_ROBIN load_assignment: cluster_name: service-https endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 8080 protocol: TCP transport_socket: name: envoy.transport_sockets.tap typed_config: type: type.googleapis.com/envoy.extensions.transport_sockets.tap.v3.Tap common_config: static_config: match: any_match: true output_config: sinks: - format: PROTO_BINARY file_per_tap: path_prefix: /some/tap/path transport_socket: name: envoy.transport_sockets.tls typed_config: type: type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext # ... 此处填完整的 upstream TLS 上下文证书、SNI 等 # 它将取代集群上原有的 UpstreamTlsContext 配置注意由于 tap 套接字“吞掉”了原来挂 TLS 的位置原来的UpstreamTlsContext上游或DownstreamTlsContext下游内容必须完整地移入 tap 配置的内层transport_socket中否则 TLS 行为会丢失。另外抓取记录的是加密前的明文字节Envoy 在 TLS 层之下、应用层之上拦截因此 trace 内容是可读的这一点从 tap.proto 中对该扩展的描述“interpose and record in plain text any traffic that is surfaced to Envoy”拦截并以明文记录到达 Envoy 的任何流量可以得到印证。源码级原理TapSocket 如何旁路复制流量从源码结构看tap 传输套接字的核心实现在 source/extensions/transport_sockets/tap/tap.cc 与 tap.hTapSocket继承自PassthroughSocket透套接字构造时接收一个“宿主”传输套接字所有真正的 I/O 最终仍由宿主套接字完成——抓取逻辑是纯旁路interpose不改变数据面行为TapSocketFactory/DownstreamTapSocketFactory分别实现上游、下游两条工厂路径上游走createTransportSocket(options, host)下游走createDownstreamTransportSocket()二者都构造TapSocket并注入SocketTapConfig这与“配置在 Listener 或 Cluster 上”的两类位置一一对应。关键的拦截链路有四处// setTransportSocketCallbacks连接建立时决定是否真正抓取 void TapSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks callbacks) { transport_socket_-setTransportSocketCallbacks(callbacks); if (config_ ! nullptr) { if (config_-shouldRecord()) { // 采样通过 匹配命中 tapper_ config_-createPerSocketTapper(socket_tap_config_, stats_, callbacks.connection()); } else { stats_.cx_sampled_out_.inc(); // 采样未通过仅计数 } } } // 读方向底层读完后把“本次读到的字节”交给 tapper Network::IoResult TapSocket::doRead(Buffer::Instance buffer) { Network::IoResult result transport_socket_-doRead(buffer); if (tapper_ ! nullptr result.bytes_processed_ 0) { tapper_-onRead(buffer, result.bytes_processed_); } return result; } // 写方向先快照一份缓冲区副本再委托宿主写最后把快照交给 tapper Network::IoResult TapSocket::doWrite(Buffer::Instance buffer, bool end_stream) { if (tapper_ nullptr) { return transport_socket_-doWrite(buffer, end_stream); } Buffer::OwnedImpl copy(buffer); // 宿主写完会消耗缓冲区必须先拷贝 const Network::IoResult result transport_socket_-doWrite(buffer, end_stream); if (result.bytes_processed_ 0) { tapper_-onWrite(copy, result.bytes_processed_, end_stream); } return result; }这解释了文档中几个行为的底层原因每个连接有一个独立的PerSocketTapper接口见 tap_config.h包含onRead/onWrite/closeSocket三个纯虚方法因此“每个唯一套接字实例生成一个 trace 文件”写方向需要先Buffer::OwnedImpl copy(buffer)快照因为宿主套接字会排空drain写入缓冲区注释中写得很明确“the bytes handed to the tapper have to be snapshotted before delegating”——这是抓取不影响真实发送的前提tapper_ nullptr采样未通过或配置关闭时doRead/doWrite直接透传宿主套接字抓取路径零开销。缓冲数据上限防止 OOM 的截断机制对于**缓冲模式buffered**的套接字抓取Envoy 会限制被抓取的 body 数据量以避免 OOM。默认情况下接收rx与发送tx方向的缓冲上限各为1KiB可通过以下字段配置配置字段OutputConfig含义默认值max_buffered_rx_bytes缓冲抓取时接收方向缓冲上限超过后截断1KiBmax_buffered_tx_bytes缓冲抓取时发送方向缓冲上限超过后截断1KiB字段定义见 api/envoy/config/tap/v3/common.proto 中的OutputConfigmessage OutputConfig { // 输出 sink。当前允许列表中存在且仅存在一个 sink repeated OutputSink sinks 1 [(validate.rules).repeated {min_items: 1 max_items: 1}]; // 缓冲抓取时接收 body 的缓冲上限超限截断并置 truncated 字段默认 1KiB google.protobuf.UInt32Value max_buffered_rx_bytes 2; // 缓冲抓取时发送 body 的缓冲上限超限截断并置 truncated 字段默认 1KiB google.protobuf.UInt32Value max_buffered_tx_bytes 3; // 缓冲 or 流式true 时每次 tap 产生多条 streamed 消息 bool streaming 4; // 流式抓取下达到该字节阈值才发出一次 tapped 消息避免高频发送 google.protobuf.UInt32Value min_streamed_sent_bytes 5; }当缓冲抓取发生截断时trace 会通过以下字段如实标记而不是静默丢数据data.tap.v3.SocketBufferedTrace.read_truncated读方向被截断data.tap.v3.SocketStreamedTraceSegment对应的write_truncated写方向被截断data.tap.v3.Body.truncated具体某段 body 被截断。流式抓取Streamingtap 传输套接字同时支持缓冲buffered与流式streaming两种模式由OutputConfig.streaming字段控制streaming: false默认整段 tap 结束后发出一条SocketBufferedTrace消息streaming: true在读取/写事件发生时持续发出一系列SocketStreamedTraceSegment消息。proto 注释中特别澄清“streamed tapping 并不意味着不做缓冲——如果在尚不能确定是否匹配之前就处理了数据缓冲仍可能发生”即匹配判定前的数据必须暂存。流式模式下还有两个实用细节min_streamed_sent_bytes默认情况下每次 read/write 事件都会发送一次 tapped 消息设置该阈值后累积达到阈值才发送用于避免高频写盘/发送开销socket_tap_config.set_connection_per_event可选字段仅对流式 trace 生效控制是否在每个事件上都附带输出连接信息。HTTP tap 过滤器有同名的 streaming 概念文档指出两者的概念大部分重叠可对照其 streaming 章节理解。采样Sampling只抓一部分连接通过TapConfig.tap_enabled位于common.tap.v3公共配置中配置采样比例按连接粒度采样只有被配置比例命中的一部分连接会继续走“匹配谓词match predicate”评估并被抓取其余连接完全不被抓取并递增cx_sampled_out统计——这与tap.cc中shouldRecord()返回 false 时stats_.cx_sampled_out_.inc()的实现完全对应配置的采样率会记录在每条 trace 第一个发出的TraceWrapper段的configured_sample_rate字段上便于事后分析时确认该 trace 的采样口径与 HTTP tap 过滤器行为一致。采样的价值在于对高连接数场景例如每请求一连接的 gRPC/HTTP/1.1 后端全量抓取会产生海量 trace 文件采样可以显著降低磁盘与拷贝开销。统计指标tap 过滤器在transport.tap.stat_prefix命名空间下发出统计。要自定义该命名空间中的前缀配置SocketTapConfig.stats_prefix字段即可该字段定义于 tap.proto 的SocketTapConfigstats_prefix之外还有前述set_connection_per_event。前缀拼接逻辑见TapSocket::generateStatstap.cc 第 20–29 行if (prefix.empty()) { final_prefix fmt::format(transport.tap.); // 未配置 stats_prefix } else { final_prefix fmt::format(transport.tap.{}., prefix); // 配置了 stats_prefix }完整统计清单宏定义ALL_TRANSPORT_TAP_STATS见 tap_config.h 第 18–21 行指标名类型说明transport.tap.stat_prefix.buffered_submitCounter由缓冲 trace 事件触发的提交总次数transport.tap.stat_prefix.cx_sampled_outCounter因tap_enabled采样在匹配评估之前被短路未抓取的连接总数transport.tap.stat_prefix.streamed_submitCounter由流式 trace 事件触发的提交总次数生成 PCAP用 tap2pcap 转换为 Wireshark 可分析格式生成的 protobuf trace 文件可以用tap2pcap工具转换为 libpcap 格式从而用 Wireshark、tshark 等工具分析。该工具在仓库中为 api/tools/tap2pcap.py。官方文档给出的完整命令示例# 将 trace protobuf 文件转换为 pcap bazel run envoy_api//tools:tap2pcap /some/tap/path_0.pb path_0.pcap # 用 tshark 按 HTTP/2 协议解码假设 10000 端口跑的是 HTTP/2 tshark -r path_0.pcap -d tcp.port10000,http2 -P 1 0.000000 127.0.0.1 → 127.0.0.1 HTTP2 157 Magic, SETTINGS, WINDOW_UPDATE, HEADERS 2 0.013713 127.0.0.1 → 127.0.0.1 HTTP2 91 SETTINGS, SETTINGS, WINDOW_UPDATE 3 0.013820 127.0.0.1 → 127.0.0.1 HTTP2 63 SETTINGS 4 0.128649 127.0.0.1 → 127.0.0.1 HTTP2 5586 HEADERS 5 0.130006 127.0.0.1 → 127.0.0.1 HTTP2 7573 DATA 6 0.131044 127.0.0.1 → 127.0.0.1 HTTP2 3152 DATA, DATA其中-d tcp.port10000,http2告诉 tshark 将该端口上的原始 TCP 流按 HTTP/2 解析对于 HTTP/1.1 或其他协议替换为对应解码指令即可。这是 L4 抓包相对于 L7 过滤器的优势拿到的字节流与线上一致协议解析完全交给标准抓包工具链。小结与适用前提抓 L4 原始流量含 TLS 加密前的明文→ 用envoy.transport_sockets.tap挂到 Listener/Cluster 的transport_socket抓 HTTP 请求/响应语义 → 用 HTTP tap 过滤器tap 套接字必须内嵌真实的底层传输套接字raw_buffer或 TLS 上下文且会取代原有位置的 TLS 配置迁移时不要遗漏缓冲模式默认每方向仅 1KiB 缓冲大 body 场景需显式调大max_buffered_rx_bytes/max_buffered_tx_bytes并注意truncated字段判读高连接数场景建议配置tap_enabled采样并用cx_sampled_out与 trace 首段的configured_sample_rate校验采样口径该扩展仍是实验特性匹配条件集有限、配置结构可能随版本变化生产使用需以当前仓库版本的 API 定义api/envoy/extensions/transport_sockets/tap/v3/tap.proto、api/envoy/config/tap/v3/common.proto为准。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价