当服务数量从十几个增长到上百个,每个服务都需要重试、超时、熔断、TLS、追踪、限流——把这些能力在每个应用里各实现一遍,会得到一堆行为不一致、版本各异的实现。服务网格的思路是把这些通用能力下沉到基础设施层,由与应用同生命周期的边车代理统一提供。Nginx 作为成熟的七层代理,在这个体系里既可以是数据面的实现者,也可以是网关层的补充。本文讨论 Nginx 在网格中的实际角色、能覆盖哪些能力、以及在与 Envoy 竞争时该怎么选。
一句话总结: Nginx 与 Envoy 都能做数据面边车,差别在于 Envoy 为网格场景原生设计(动态配置、热更新、xDS),而 Nginx 的优势在成熟稳定与运维经验复用。
1. 服务网格要解决的问题
一句话总结: 服务网格把服务间通信的可靠性、安全与可观测性从应用代码中剥离,交给统一的数据面代理处理。
网格解决的核心问题是「跨语言、跨团队的通信治理一致性」:
可靠性
超时、重试、熔断、故障注入、负载均衡策略
安全性
服务间 mTLS、身份认证、细粒度授权、证书轮换
可观测性
统一指标、分布式追踪、访问日志、拓扑发现
流量治理
灰度发布、流量镜像、按权重路由、区域感知路由
在传统架构里,这些能力要么由每个应用自行实现(语言各异、行为不一致),要么集中在一个中心网关(东西向流量绕行、单点瓶颈)。边车模式的关键改进是让代理贴着应用部署:每个 Pod 里跑一个代理实例,所有进出该服务的流量都经过它,策略由控制面统一下发。
2. Nginx 作为边车代理
一句话总结: Nginx 可以作为边车接管 Pod 的入向与出向流量,但需要外部工具完成配置下发与动态更新,因为它本身没有 xDS 那样的动态配置协议。
2.1 边车模式与流量劫持
一句话总结: 边车通过 iptables 或 eBPF 劫持 Pod 内的流量,应用无需改动代码即可获得代理能力。
Pod 内流量走向(入向):
外部请求 -> Pod IP:8080
| iptables 重定向
v
边车代理 :15006 --[mTLS 解密、鉴权、路由]--> 应用 :8080
Pod 内流量走向(出向):
应用 -> 目标服务 DNS
| iptables 重定向
v
边车代理 :15001 --[mTLS 加密、重试、追踪]--> 目标 Pod 的边车
流量劫持通常由网格的控制面组件(如 Istio 的 istio-init 或 CNI 插件)注入 iptables 规则完成。Nginx 作为边车时,这些规则同样适用——它对 Nginx 而言是透明的,Nginx 只需要监听对应的端口并正确处理 HTTP 与 TLS。
# 边车代理配置骨架:入向
server {
listen 15006;
# 网格内部流量使用 HTTP/1.1 或 HTTP/2,长连接复用
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Request-Id $request_id;
# 网格内必须关闭重试之外的缓冲,减少延迟
proxy_buffering off;
proxy_connect_timeout 1s;
proxy_read_timeout 15s;
proxy_next_upstream error timeout http_502 http_503;
}
}
2.2 东西向流量的代理配置
一句话总结: 东西向流量量大且调用链深,配置重点是低延迟、连接复用与快速失败,而不是像南北向那样追求缓存与压缩。
# 出向代理:把服务名解析为集群内的目标
upstream payment_service {
server payment.default.svc.cluster.local:8080 max_fails=2 fail_timeout=5s;
keepalive 128; # 连接池,显著降低握手开销
keepalive_requests 10000;
keepalive_timeout 60s;
}
server {
listen 15001;
location /payment/ {
proxy_pass http://payment_service/;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 快速失败:网格内的故障应尽快暴露而不是排队
proxy_connect_timeout 500ms;
proxy_read_timeout 5s;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
}
}
与南北向网关的差别在于:东西向不需要压缩(内网带宽充足且压缩增加 CPU)、不需要缓存(数据多为个性化)、超时应更激进(快速失败优于长时间等待)。keepalive 连接池在这里的价值尤其大,因为服务间调用频率远高于外部请求。
3. mTLS 与服务身份
一句话总结: 网格内所有通信都应启用 mTLS,证书由控制面统一签发与轮换,Nginx 侧只需正确加载证书并校验证书链。
3.1 双向认证与证书轮换
一句话总结: 服务端要求客户端证书并校验其身份,客户端校验服务端身份,证书有效期通常很短因此必须支持热轮换。
server {
listen 15006 ssl;
http2 on;
# 服务端证书
ssl_certificate /etc/certs/tls.crt;
ssl_certificate_key /etc/certs/tls.key;
# 双向认证:要求并校验客户端证书
ssl_client_certificate /etc/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
# 证书链校验失败时的状态码
ssl_handshake_timeout 10s;
location / {
# 把客户端身份透传给应用
proxy_set_header X-Client-Cert-Subject $ssl_client_s_dn;
proxy_set_header X-Client-Cert-Serial $ssl_client_serial;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_pass http://127.0.0.1:8080;
}
}
证书轮换是网格里最容易出问题的环节:短有效期证书(如 24 小时)需要频繁更新文件并 reload。Nginx 不像 Envoy 那样支持通过 SDS 动态更新证书,通常的做法是边车容器内跑一个 sidecar 进程监听证书变化并触发 nginx -s reload。reload 会创建新 worker 并优雅关闭旧 worker,对已有连接无影响,但高频 reload 会带来额外的内存与 CPU 开销,因此轮换周期不宜过短。
3.2 SPIFFE 与身份传递
一句话总结: 网格中的服务身份通常用 SPIFFE ID 表示,Nginx 通过证书的 SAN 字段提取身份并作为头部或鉴权依据透传。
SPIFFE ID 形如:
spiffe://cluster.local/ns/default/sa/payment
证书的 SAN 字段中包含该 URI
X509v3 Subject Alternative Name:
URI:spiffe://cluster.local/ns/default/sa/payment
# 从客户端证书提取身份并做基础鉴权
map $ssl_client_s_dn $client_service {
default "";
"~CN=payment" "payment";
"~CN=order" "order";
}
server {
listen 15006 ssl;
ssl_verify_client on;
# 只允许 payment 服务调用退款接口
location /internal/refund/ {
if ($client_service != "payment") {
return 403;
}
proxy_pass http://127.0.0.1:8080;
}
}
用 if 做鉴权在生产配置里通常不推荐(if 是 rewrite 模块的一部分,在 location 内使用有语义陷阱),更稳妥的方式是用 map 生成变量后再配合 auth_request 或 deny 使用。上面的写法在简单场景下可用,但复杂授权逻辑应交给专门的授权服务。
4. 与 Envoy 和 Istio 的取舍
一句话总结: Envoy 是为网格原生设计的代理(xDS 动态配置、热更新、丰富过滤器),Nginx 的优势在于成熟稳定、运维经验可复用、调试工具链完善。
4.1 能力矩阵对比
一句话总结: 动态配置能力是两者的分水岭,其余能力各有胜负,选型应以此为核心判据。
维度 Nginx(含 OpenResty) Envoy
动态配置 需重载或第三方方案 xDS 原生支持,无重载
证书热更新 reload 或 Lua 实现 SDS 原生支持
可编程性 Lua 成熟,C 模块生态好 WASM 过滤器
协议支持 HTTP/1.1、HTTP/2、HTTP/3、gRPC、TCP/UDP 同为全面
可观测性 Prometheus 需额外模块 原生统计与追踪集成
资源占用 较低 相对更高
学习曲线 配置语法简单,生态文档多 配置模型复杂(xDS 概念多)
运维经验 极广,排错资料丰富 相对较新
4.2 选型建议
一句话总结: 已有 Nginx 运维体系且服务规模中等时,用 Nginx 做边车可复用经验;追求动态配置与完整网格能力时选 Envoy 系。
选 Nginx 的场景
- 团队已有成熟的 Nginx 运维与配置管理体系
- 服务数量中等(几十个),配置变更频率不高
- 需要复用已有的 Lua 脚本与自定义模块
- 边车之外还要兼顾南北向网关,希望技术栈统一
选 Envoy 的场景
- 需要频繁的动态配置变更且不能接受 reload
- 需要完整的网格能力(流量镜像、故障注入、细粒度遥测)
- 已采用 Istio 等以 Envoy 为数据面的控制面
- 需要 WASM 做自定义过滤器
现实中两者并不互斥:常见架构是用 Istio 加 Envoy 处理东西向网格,同时在入口用 Nginx Ingress 或 Nginx 网关处理南北向,因为 Nginx 在入口场景的成熟度与可定制性仍然有优势。
5. 可观测性
一句话总结: 边车是观测数据的最佳采集点,Nginx 侧应输出结构化日志、Prometheus 指标与追踪头透传,三者缺一不可。
log_format mesh '$remote_addr "$request" $status '
'upstream=$upstream_addr ut=$upstream_response_time '
'rt=$request_time trace=$http_x_b3_traceid '
'span=$http_x_b3_spanid';
server {
listen 15006;
access_log /dev/stdout mesh;
location / {
# 透传分布式追踪头,缺失时生成新的
proxy_set_header X-B3-TraceId $http_x_b3_traceid;
proxy_set_header X-B3-SpanId $http_x_b3_spanid;
proxy_set_header X-Request-Id $request_id;
proxy_pass http://127.0.0.1:8080;
}
}
# Prometheus 指标暴露(需要 nginx-prometheus-exporter 或 stub_status)
server {
listen 9113;
location /metrics {
stub_status;
allow 10.0.0.0/8;
deny all;
}
}
$upstream_response_time 是网格场景最有价值的字段之一:它区分了「应用慢」与「网络慢」。若 $request_time 远大于 $upstream_response_time,说明时间花在 Nginx 侧(排队、缓冲、客户端慢);两者接近则问题在上游应用。日志输出到 stdout 让容器日志采集统一处理,是云原生环境的标准做法。
6. 典型落地架构
一句话总结: 常见做法是「入口 Nginx 网关 + 网格内 Envoy 边车」,用两套组件各取所长,而不是强求一套技术栈打通南北与东西。
互联网
|
v
[Nginx 入口网关] <- 南北向:TLS 终止、WAF、限流、缓存、路由
|
v
Kubernetes 集群
|
+-- Pod A [Envoy 边车] --[mTLS]--> Pod B [Envoy 边车]
| |
| v
| 应用容器 :8080
|
+-- 控制面(Istio/自研):证书签发、策略下发、遥测收集
如果坚持用 Nginx 做边车,控制面需要自行解决三个问题:配置如何下发(挂载 ConfigMap 加 reload,或对接 Consul 模板)、证书如何轮换(sidecar 进程监听加 reload)、健康检查如何做(/healthz 探针覆盖 Nginx 与应用)。这三件事 Envoy 体系里都有现成方案,自研成本需要提前评估。
7. 常见陷阱
一句话总结: 网格落地的坑集中在「劫持规则与端口冲突、reload 与流量抖动、健康检查误判、诊断困难」四类。
[陷阱一] 劫持规则与 Nginx 监听端口冲突
表现:边车启动后应用完全不可达
原因:iptables 规则把边车自身流量也劫持了,形成环路
规避:给边车进程的 UID 配置豁免规则
[陷阱二] 频繁 reload 引发连接抖动
表现:证书轮换期间出现间歇性 502
原因:reload 频率过高,旧 worker 关闭时正在处理的请求被中断
规避:拉长轮换周期,或改用 Lua 动态加载证书
[陷阱三] 健康检查探针打在应用而非边车
表现:应用正常但探针失败,Pod 被反复重启
原因:探针流量经过劫持后被转发链路放大
规避:探针端口加入劫持豁免,或让探针直连应用端口
[陷阱四] 诊断时无法区分边车与应用
表现:出现 5xx 时不知道是代理拒绝还是应用报错
原因:日志未记录 upstream 状态与响应时间
规避:日志中同时记录 upstream_addr 与 upstream_status
第一类与第三类都源于同一个根因——iptables 劫持规则没有正确豁免必要流量。这类问题往往在部署后立即暴露,属于「难配置但不难发现」;第二类与第四类则更隐蔽,通常在流量高峰或故障时才浮现,需要提前在日志与配置上做好准备。
8. 总结
| 环节 | 要点 |
|---|---|
| 网格价值 | 把可靠性、安全、可观测性从应用代码下沉到基础设施 |
| 边车模式 | 代理与应用同 Pod,iptables 劫持流量,应用零改动 |
| 东西向配置 | 低延迟、连接复用、快速失败,不追求缓存与压缩 |
| mTLS | 双向认证加证书链校验,短周期证书必须支持热轮换 |
| 服务身份 | 从证书 SAN 提取 SPIFFE ID,据此做授权判断 |
| 与 Envoy 对比 | 动态配置是分水岭,Nginx 胜在成熟与运维经验 |
| 可观测性 | 结构化日志加指标加追踪头透传,upstream 字段最关键 |
| 常见陷阱 | 劫持豁免、reload 抖动、探针路径、诊断信息缺失 |
服务网格不是「必须上」的架构,而是一种「当服务数量与治理复杂度超过阈值时更划算」的选择。对多数团队来说,更务实的路径是先用 Nginx 把南北向网关做好,把限流、鉴权、TLS 这些通用能力集中起来;当东西向的治理需求(跨语言一致的 mTLS、细粒度遥测、灰度路由)确实成为瓶颈时,再引入网格。理解 Nginx 在网格中的位置与边界,比盲目追随架构潮流更重要——这也正是本专题希望传达的态度:工具的能力边界,永远比工具本身更值得先弄清楚。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。