服务网格(Service Mesh)落地之后,流量的形态发生了本质变化。原本客户端直连Nginx的请求,现在往往先经过istio-proxy这类sidecar代理,再转发到后端的Nginx实例。这时Nginx访问日志里记录的客户端地址几乎总是127.0.0.1或者Pod的地址,真实的调用方信息被sidecar屏蔽掉了。如果排障时还按老思路看日志,很容易得出错误结论。本文将围绕如何在Nginx日志中识别sidecar请求来源展开,覆盖头部传递、日志格式设计、realip模块配置和流量区分策略几个方面。

一、先搞清楚Sidecar转发后Nginx看到的是什么
在Istio等服务网格中,每个Pod都会被注入一个Envoy sidecar,应用容器的所有出入流量都会被劫持到sidecar处理。当上游服务调用Nginx所在的服务时,实际建立TCP连接的是同Pod或上游Pod里的Envoy进程,而不是上游应用本身。
这就意味着Nginx视角下的几个关键变量都会发生变化:$remote_addr大概率是127.0.0.1(sidecar与应用同Pod共享网络命名空间时,请求经localhost转发)或者上游Pod的IP;$http_user_agent可能被Envoy改写或保留;HTTP版本也会因为Envoy内部使用HTTP/2转发而与外部协议不同。如果不做任何处理,日志里几乎看不到有价值的来源信息。
好消息是,Envoy在转发时会自动追加或透传一批标准头部,主要包括:
X-Forwarded-For:逐跳追加的客户端IP链,第一个IP通常是真实客户端地址X-Request-Id:Envoy生成的全链路请求ID,可用于在网格内部追踪X-Envoy-External-Address:Envoy识别出的外部客户端地址X-B3-Traceid、X-B3-Spanid:分布式追踪上下文(若启用了相关配置)
识别sidecar请求的第一步,就是把这些头部纳入Nginx日志。需要注意的是,X-Forwarded-For可以被伪造,只有当你信任sidecar一定会正确追加时,它才有诊断价值。
二、设计能识别Sidecar的日志格式
默认的combined日志格式显然不够用,我们需要显式记录转发链、请求ID和连接特征。下面是一个可直接使用的配置示例:
http {
log_format mesh '$remote_addr - $remote_port [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_user_agent" '
'xff="$http_x_forwarded_for" '
'reqid="$http_x_request_id" '
'extaddr="$http_x_envoy_external_address" '
'server_port=$server_port protocol=$server_protocol';
server {
listen 8080;
access_log /var/log/nginx/access_mesh.log mesh;
}
}
这个格式能回答三个关键问题:请求是不是sidecar转来的(看xff和reqid是否存在)、真实客户端是谁(看xff的第一个IP或extaddr)、这条日志能否与网格内的追踪系统关联(用reqid去Jaeger或Zipkin查询)。如果日志中x_forwarded_for为空且$remote_addr是外部IP,基本可以判断是绕过网格直连的流量。
另一个实用技巧是利用端口约定。Istio默认将应用流量劫持到15001、15006等端口,同时Envoy间通信往往带有明显特征。如果你的Nginx直接监听在业务端口上,来自15006端段的连接记录就是sidecar明证。可以通过$remote_port观察,虽然这个方法不是百分之百准确,但在多数标准部署中非常有效。
三、用realip模块还原真实客户端地址
仅靠日志记录转发链还不够,很多场景下我们希望$remote_addr本身就能反映真实来源,这样可以无缝复用既有的IP封禁、限流规则。Nginx的ngx_http_realip_module正是为此设计的。
核心思路是:告诉Nginx哪些地址是可信代理(即sidecar或网格入口网关),当请求来自这些地址时,用X-Forwarded-For中的最后一个可信地址之外的IP替换$remote_addr。配置示例如下:
http {
set_real_ip_from 127.0.0.0/8; # 同Pod sidecar
set_real_ip_from 10.244.0.0/16; # 集群Pod网段,按实际调整
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
real_ip_recursive设为on很关键,它会从右往左剔除所有可信代理IP,剩下的第一个不可信地址才会成为新的$remote_addr。在多层转发(客户端到入口网关,再到服务sidecar,最后到Nginx)的场景下,这个选项决定了你拿到的是真实IP还是中间某一跳的地址。
配置完成后,日志格式里的$remote_addr就直接是真实客户端地址,配合$http_x_request_id保留了网格追踪能力,一举两得。要注意的是,set_real_ip_from的范围必须严格受控,如果把不可信的网段加进去,攻击者就可以伪造XFF头部隐藏自己的真实IP。
四、结合mTLS与网格元数据做更精细的区分
在开启了mTLS的网格中,还有一个更强的识别手段:客户端证书。Istio的mTLS证书SAN中携带了Spiffe格式的服务身份,例如spiffe://cluster.local/ns/default/sa/frontend,这能精确标识调用方是哪个服务账号的sidecar。
Nginx側可以开启SSL双向认证并从证书中提取身份:
server {
listen 8443 ssl;
ssl_certificate /etc/nginx/tls/server.crt;
ssl_certificate_key /etc/nginx/tls/server.key;
ssl_client_certificate /etc/nginx/tls/istio-ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
log_format meshmtls '$remote_addr身份=$ssl_client_s_dn '
'reqid="$http_x_request_id" '
'"$request" $status';
}
这样日志里不仅知道请求来自sidecar,还能知道来自哪个命名空间下的哪个服务账号,排查跨服务调用问题时效率大幅提升。这种方式比XFF更可靠,因为证书是网格控制面签发的,外部流量无法伪造。
五、落地建议与常见坑
总结一下实践中的几个要点。第一,日志格式调整后要同步更新日志采集与分析平台的解析规则,否则xff字段里的逗号分隔IP会被当成异常格式丢弃。第二,警惕XFF被伪造的风险,任何基于$http_x_forwarded_for做访问控制的逻辑,都必须先经过realip模块的可信代理校验。第三,Sidecar与Nginx之间如果启用了HTTP/2,注意$server_protocol的值会变化,历史监控脚本可能需要适配。
还有一点常被忽略:Envoy转发大请求或长连接时行为与直连不同,例如会主动断开空闲连接,Nginx日志中会相应出现499状态码增多的现象。看到这类日志先别急着怀疑客户端,确认一下是否是sidecar的连接管理策略导致的,可以通过调整Envoy的idleTimeout与Nginx的keepalive配置使两端匹配。
通过头部透传、realip还原、端口特征和mTLS身份四层手段的组合,你可以在Nginx日志中准确区分网格内外流量、还原真实调用方,并与网格追踪体系打通。这套方案在灰度迁移阶段尤其有价值,它能帮你清楚看到哪些流量已经走网格、哪些还在直连,为后续全量接入提供数据支撑。
Nginx日志Service Meshsidecar修改时间:2026-09-14 06:36:41