Envoy Proxy 生产实战:从反向代理、流量治理到高可用网关
如果只看一份最小配置,Envoy 很像“另一个 Nginx”:监听一个端口,把请求转发给后端。但真正让 Envoy 成为云原生基础设施核心组件的,不是反向代理本身,而是它把路由、服务发现、负载均衡、故障隔离、安全和可观测性做成了一个可动态控制的数据平面。
这也是为什么 Envoy 会出现在 API Gateway、Kubernetes Gateway、Service Mesh、gRPC 基础设施、边缘代理和多集群流量治理中。
本文沿着一条生产链路展开:
- Envoy 到底解决什么问题,什么时候值得使用;
- 一个请求如何穿过 Listener、Filter、Route、Cluster 和 Endpoint;
- 怎样在本地跑起一个具有灰度路由、健康检查和异常摘除能力的代理;
- 怎样正确设计超时、重试、熔断、限流、鉴权和动态配置;
- 怎样在 Kubernetes 上用 Envoy Gateway 部署一个跨节点、可扩缩、可优雅升级的高可用入口;
- 上线前应该监控什么、演练什么,以及故障时如何定位。
本文基于 2026 年 9 月 4 日可用的官方资料与稳定版本:Envoy 1.39.1、Envoy Gateway 1.9.1。示例固定具体版本,避免生产环境无意间拉取变化中的 latest 镜像。
一、Envoy 是什么:数据平面,而不是业务框架
Envoy 是一个高性能 L4/L7 代理。它通常部署在应用请求路径中,接收下游连接,执行一组网络或 HTTP Filter,再把请求转发到上游服务。
官方术语里:
- Downstream:连接 Envoy 的客户端;
- Listener:Envoy 对外监听的 IP、端口或 Unix Domain Socket;
- Filter Chain:连接或请求依次经过的处理链;
- Route:根据域名、路径、Header 等条件选择转发动作;
- Cluster:一组逻辑等价的上游实例;
- Endpoint:Cluster 中真正接收流量的实例;
- Upstream:Envoy 最终连接的后端服务。
Envoy 本身一般是数据平面:它高频处理真实业务流量。控制面负责把 Listener、Route、Cluster、Endpoint 和 Secret 等配置下发给 Envoy。静态 YAML、Envoy Gateway、Istio、Consul 或自研 xDS 服务,都可以扮演配置来源。
一张图看懂 Envoy 的六类核心能力

这六类能力并不是彼此独立的功能开关,而是在一次请求中协同工作:
- 流量入口负责接收 TCP、HTTP、gRPC 等连接,完成 TLS 终止、协议识别和基础请求规范化;
- 流量治理根据路径、域名、Header、权重或 Hash 选择目标服务,并支持灰度、镜像和会话亲和;
- 可靠性通过 Timeout、Retry Budget、Circuit Breaker、Health Check 和 Outlier Detection 限定故障影响;
- 安全把 mTLS、JWT、RBAC、外部鉴权和限流放到统一的数据路径上执行;
- 动态配置通过 LDS、RDS、CDS、EDS、SDS 等 xDS API,在不中断现有流量的前提下更新拓扑和策略;
- 可观测性为每个 Listener、Route、Cluster 和 Endpoint 输出指标、访问日志、Trace 与 Response Flags。
可以把 Envoy 理解为一个可编程的网络执行引擎:控制面声明“希望流量怎样运行”,Envoy 在每个请求上执行策略并产生运行证据。后面的章节会分别把这六个能力域拆开。
Envoy、Envoy Gateway 与 Service Mesh 不是同一个东西
| 名称 | 核心职责 | 适合场景 | 你需要维护什么 |
|---|---|---|---|
| Envoy Proxy | 处理 L4/L7 流量的数据平面 | 独立代理、边缘网关、Sidecar、内部负载均衡 | Envoy 配置或 xDS 控制面 |
| Envoy Gateway | 把 Kubernetes Gateway API 翻译成 Envoy xDS 配置 | Kubernetes 南北向网关 | Gateway API、EnvoyProxy 策略和集群资源 |
| Service Mesh | 管理大量服务间东西向通信 | 跨语言微服务、统一 mTLS 和流量策略 | Mesh 控制面、代理生命周期与身份体系 |
| API Gateway 产品 | API 生命周期、开发者门户、Key、配额和商业策略 | 对外开放 API、合作伙伴平台 | 产品级 API 管理能力和数据面 |
一个常见误区是“用了 Envoy 就等于有了完整 API Gateway”。Envoy 提供路由、JWT、外部鉴权、限流等底层能力,但开发者门户、套餐、API Key 生命周期、计费和审批通常需要额外控制面或产品层。
二、什么时候应该使用 Envoy
2.1 高价值场景
| 场景 | Envoy 带来的价值 | 典型能力 |
|---|---|---|
| gRPC 网关或负载均衡 | 原生理解 HTTP/2、gRPC 状态与长连接 | gRPC 路由、健康检查、重试、统计 |
| Kubernetes Ingress / Gateway | 路由策略声明化,数据面动态更新 | Gateway API、xDS、TLS、灰度发布 |
| 多语言微服务 | 把网络治理从各语言 SDK 中抽离 | mTLS、超时、熔断、可观测性 |
| 边缘反向代理 | 高并发连接、细粒度 L7 路由 | HTTP/1.1、HTTP/2、HTTP/3、TLS、WAF 扩展 |
| 出口代理 | 统一控制服务访问外部网络 | Forward Proxy、DNS、访问审计、策略 |
| 金丝雀与蓝绿发布 | 不改业务代码即可按比例或条件分流 | Weighted Cluster、Header Match、Mirroring |
| 多可用区容灾 | 本地优先和跨区域故障转移 | Locality、Priority、健康检查、异常摘除 |
2.2 不一定需要 Envoy 的场景
下面这些情况,简单代理可能更划算:
- 只有一个静态网站和一个固定后端;
- 配置极少变化,不需要动态服务发现;
- 团队没有维护流量策略、证书、监控和升级的能力;
- 延迟预算极端苛刻,但又没有对真实 Filter Chain 做过压测;
- 只是想解决某个语言框架内部的一个小问题。
Envoy 的成本不是“多一个容器”这么简单。它会引入新的配置模型、故障域、指标体系、证书生命周期和发布流程。只有当统一治理带来的收益超过这些复杂度时,才值得引入。
三、请求生命周期:看懂配置前先看懂数据流
以一个 HTTPS API 请求为例,请求大致经历以下过程:

读这张图时要同时跟踪三条路径:
- 数据路径:客户端请求从左向右经过 Listener、Filter、Route 和 Cluster,最终进入健康 Endpoint;
- 控制路径:控制面通过 xDS 更新服务发现、路由、证书和策略,不直接承载业务请求;
- 证据路径:Envoy 把 Metrics、Access Logs 和 Traces 输出给可观测平台,用于判断策略是否生效以及故障发生在哪里。
下面的时序图进一步展开一次请求内部的先后顺序:
这里有几个生产上非常重要的事实:
- Filter 顺序会改变语义。 鉴权、限流、缓存、压缩和 Router 的先后次序不是装饰;Router 通常必须位于 HTTP Filter Chain 最后。
- 负载均衡发生在 Cluster 内部。 Route 先选 Cluster,再由 Cluster 的负载均衡策略选 Endpoint。
- 连接池属于 Worker Thread。 Envoy 是单进程多线程模型,每个 Worker 处理网络事件并维护自己的上游连接池。
- 一次请求可能产生多次上游尝试。 重试或 Hedging 会放大真实上游流量,不能只看下游 QPS。
- 健康检查与异常摘除是两条信号。 主动健康检查定期探测;Outlier Detection 根据真实请求结果做被动判断。
四、第一次实战:Docker Compose 跑起灰度代理
下面搭建一个最小但不玩具化的环境:
- Envoy 监听
10000; - 两个 Nginx 后端分别返回
app-v1和app-v2; - 90% 流量进入 v1,10% 进入 v2;
- 启用超时、有限重试、主动健康检查、熔断和异常实例摘除;
- Admin 端口只映射到宿主机回环地址。
4.1 compose.yaml
services:
envoy:
image: envoyproxy/envoy:v1.39.1
command: ["-c", "/etc/envoy/envoy.yaml", "--log-level", "info"]
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
ports:
- "10000:10000"
- "127.0.0.1:9901:9901"
depends_on:
- app-v1
- app-v2
app-v1:
image: nginx:1.27-alpine
command:
- /bin/sh
- -c
- |
printf 'app-v1\n' > /usr/share/nginx/html/index.html
exec nginx -g 'daemon off;'
app-v2:
image: nginx:1.27-alpine
command:
- /bin/sh
- -c
- |
printf 'app-v2\n' > /usr/share/nginx/html/index.html
exec nginx -g 'daemon off;'
4.2 envoy.yaml
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901
allow_paths:
- exact: /ready
- exact: /server_info
- prefix: /stats
- prefix: /clusters
- prefix: /config_dump
static_resources:
listeners:
- name: public_http
address:
socket_address:
address: 0.0.0.0
port_value: 10000
per_connection_buffer_limit_bytes: 32768
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
stat_prefix: public_http
use_remote_address: true
normalize_path: true
merge_slashes: true
path_with_escaped_slashes_action: UNESCAPE_AND_REDIRECT
common_http_protocol_options:
idle_timeout: 60s
headers_with_underscores_action: REJECT_REQUEST
stream_idle_timeout: 30s
request_timeout: 15s
route_config:
name: local_routes
virtual_hosts:
- name: application
domains: ["*"]
routes:
- match:
prefix: "/"
route:
timeout: 5s
retry_policy:
retry_on: "5xx,connect-failure,reset"
num_retries: 2
per_try_timeout: 1s
weighted_clusters:
clusters:
- name: app_v1
weight: 90
- name: app_v2
weight: 10
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
timestamp: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
protocol: "%PROTOCOL%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
duration_ms: "%DURATION%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
request_id: "%REQ(X-REQUEST-ID)%"
clusters:
- name: app_v1
type: STRICT_DNS
connect_timeout: 1s
per_connection_buffer_limit_bytes: 32768
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: app_v1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: app-v1
port_value: 80
health_checks:
- timeout: 1s
interval: 5s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1000
max_pending_requests: 1000
max_requests: 5000
max_retries: 100
outlier_detection:
consecutive_5xx: 5
interval: 5s
base_ejection_time: 30s
max_ejection_percent: 50
- name: app_v2
type: STRICT_DNS
connect_timeout: 1s
per_connection_buffer_limit_bytes: 32768
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: app_v2
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: app-v2
port_value: 80
health_checks:
- timeout: 1s
interval: 5s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 200
max_pending_requests: 200
max_requests: 1000
max_retries: 20
outlier_detection:
consecutive_5xx: 3
interval: 5s
base_ejection_time: 30s
max_ejection_percent: 100
本地示例为了从宿主机查看 Admin API,让 Admin 监听容器内 0.0.0.0,但端口只发布到宿主机 127.0.0.1。生产环境不要把 Admin 直接暴露到业务网络或公网,因为它没有内置身份认证,并且包含修改运行状态甚至关闭进程的操作。
4.3 先验证配置,再启动
docker run --rm \
-v "$PWD/envoy.yaml:/etc/envoy/envoy.yaml:ro" \
envoyproxy/envoy:v1.39.1 \
--mode validate -c /etc/envoy/envoy.yaml
docker compose up -d
验证流量分配:
for i in $(seq 1 30); do
curl -s http://127.0.0.1:10000/
done | sort | uniq -c
样本量只有 30 时,不保证刚好是 27:3。权重路由是概率分布,不是每十个请求固定一次 v2。
查看运行状态:
curl -s http://127.0.0.1:9901/ready
curl -s 'http://127.0.0.1:9901/stats?filter=cluster\.app_.*membership'
curl -s http://127.0.0.1:9901/clusters
curl -s http://127.0.0.1:9901/config_dump
4.4 做一次真实故障实验
docker compose stop app-v2
for i in $(seq 1 20); do
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:10000/
done | sort | uniq -c
curl -s 'http://127.0.0.1:9901/stats?filter=cluster\.app_v2'
你应该观察到 v2 健康检查失败。此时被权重算法选中 v2 的请求仍可能返回 503 no healthy upstream:Weighted Cluster 负责分流,不等于跨 Cluster 故障转移,普通重试默认也不会重新选择另一个 Weighted Cluster。生产灰度应由控制面及时把 v2 权重降为 0,或显式设计 Priority、Aggregate / Composite Cluster 等 Failover 机制。
这里真正需要验证的不是“页面大部分时候还能打开”,而是:
- v2 是否变成不健康;
- 失败窗口内产生了多少 5xx;
- 重试是否把请求救回;
- 重试是否造成明显流量放大;
- v1 的容量是否足以接管全部流量。
最后恢复并清理:
docker compose start app-v2
docker compose down
五、配置心智模型:从静态 YAML 到动态 xDS
5.1 Bootstrap 只负责“Envoy 怎样启动”
Envoy 启动时读取 Bootstrap 配置,内容通常包括:
- Node 身份;
- Admin 接口;
- 静态 Listener 和 Cluster;
- 如何连接 xDS 或 SDS;
- Runtime 层;
- Overload Manager;
- Stats Sink。
小规模部署可以把所有配置写在一个文件里。规模上升后,更常见的做法是只在 Bootstrap 中保留连接控制面的最小资源,其余配置通过 xDS 动态下发。
5.2 xDS 不是一个接口,而是一组 Discovery Service
| API | 资源 | 解决的问题 |
|---|---|---|
| LDS | Listener | Envoy 监听什么地址,使用什么 Filter Chain |
| RDS | RouteConfiguration | HTTP 请求怎样匹配、转发、重试或重定向 |
| CDS | Cluster | 有哪些上游服务及其连接策略 |
| EDS | ClusterLoadAssignment | 每个 Cluster 当前有哪些 Endpoint |
| SDS | Secret | TLS 证书、私钥、信任根怎样动态轮换 |
| RTDS | Runtime | 不重启进程怎样调整运行时开关 |
| ECDS | Extension Config | Filter 扩展配置怎样独立更新 |
标准 xDS 的 State of the World 模式会发送订阅范围内的完整状态;Delta xDS 只发送新增、修改和删除的资源,更适合资源数量很大的控制面。ADS 则把不同资源类型放在一条 gRPC 流中,方便控制更新顺序。
5.3 动态配置不等于“配置永远正确”
生产控制面至少需要处理:
- 版本与 ACK/NACK:记录每个 Envoy 接受或拒绝了哪个版本;
- 资源依赖顺序:例如先准备 Cluster,再让 Route 引用它;
- 最后可用配置:控制面故障时,已有 Envoy 应继续承载当前流量;
- 灰度下发:先给一小组代理实例更新,再逐步扩大;
- 快速回滚:回滚的是一组一致资源,而不是只恢复某一个 Route;
- 配置规模治理:避免给每个 Envoy 推送与它无关的全部资源;
- 身份与加密:xDS 和 SDS 通道同样需要 TLS、身份认证和权限边界。
如果团队没有能力维护这些语义,不要因为“动态配置很高级”就自研 xDS 控制面。Kubernetes 场景优先评估 Envoy Gateway 或成熟 Service Mesh。
六、高阶流量治理:四层防线必须一起设计
生产稳定性不是打开一个 retry_on: 5xx。至少要把超时、重试、熔断和负载卸载放在同一张图上设计。
6.1 第一层:Deadline 与 Timeout
常见超时包括:
- 连接上游的
connect_timeout; - 整个 Route 的总
timeout; - 单次尝试的
per_try_timeout; - 请求或响应没有进展的
idle_timeout; - HTTP Connection Manager 的
stream_idle_timeout; - 长连接和连接池的空闲超时。
一个可执行的预算示例:
客户端 Deadline 2000 ms
└─ 网关排队与处理 100 ms
└─ 第一次上游尝试 700 ms
└─ 退避 50 ms
└─ 第二次上游尝试 700 ms
└─ 返回链路与余量 450 ms
外层 Deadline 必须大于内部所有尝试和网络余量,但不能无限大。流式接口尤其需要注意:Envoy Route Timeout 默认语义针对完整响应,官方文档特别提醒,长流通常应关闭总 Route Timeout,并使用 Stream Duration 或 Idle Timeout 约束“长期无进展”。
6.2 第二层:有预算的重试
重试适合短暂网络错误、连接失败和明确可安全重放的操作。不要默认重试所有 5xx,更不要在网关、SDK 和服务内部同时无限重试。
retry_policy:
retry_on: "connect-failure,reset,refused-stream,retriable-status-codes"
retriable_status_codes: [503]
num_retries: 2
per_try_timeout: 300ms
retry_back_off:
base_interval: 25ms
max_interval: 250ms
生产规则:
- GET、HEAD 等幂等请求更适合自动重试;
- POST 只有在存在 Idempotency Key 或业务幂等约束时才考虑;
- 使用 Retry Budget 或
max_retries熔断限制重试比例; - 总 Route Timeout 包含所有重试;
- 监控“下游请求数”和“上游尝试数”的比值;
- 遇到容量故障时,重试可能把一次故障放大成重试风暴。
6.3 第三层:Circuit Breaker
Envoy 的 Circuit Breaker 是每个 Cluster、每个 Priority 的分布式资源上限,常见阈值包括:
- 最大上游连接数;
- 最大等待请求数;
- 最大并发请求数;
- 最大并发重试数;
- 最大连接池数量。
熔断不是为了让请求成功,而是为了让系统在容量耗尽时快速、可预测地失败,保护上游和 Envoy 自身不进入级联故障。
阈值不能照抄示例。应从后端实例数、每实例安全并发、连接池模型、协议和峰值流量反推,并通过压测验证。
6.4 第四层:Outlier Detection 与主动健康检查
主动健康检查回答“探测请求是否成功”;Outlier Detection 回答“这个实例处理真实业务流量时,是否显著异常”。
两者结合时需要防止过度摘除:
max_ejection_percent限制最多摘除多少实例;base_ejection_time决定基础隔离时间;consecutive_5xx不应小到被短暂抖动触发;- 恢复流量可以配合 Slow Start,避免刚恢复的实例瞬间被打满;
- 剩余实例必须有接管容量,否则“摘除坏实例”会变成“压垮好实例”。
6.5 Envoy 自身的 Overload Manager
Circuit Breaker 主要保护上游;Overload Manager 保护 Envoy 进程自身,可以监测内存、CPU 或文件描述符压力,并采取关闭 Keep-Alive、停止接受新请求、拒绝新连接、缩短超时或释放堆内存等动作。
overload_manager:
refresh_interval: 0.25s
resource_monitors:
- name: envoy.resource_monitors.cgroup_memory
typed_config:
"@type": type.googleapis.com/envoy.extensions.resource_monitors.cgroup_memory.v3.CgroupMemoryConfig
max_memory_bytes: 1073741824
actions:
- name: envoy.overload_actions.disable_http_keepalive
triggers:
- name: envoy.resource_monitors.cgroup_memory
threshold:
value: 0.90
- name: envoy.overload_actions.stop_accepting_requests
triggers:
- name: envoy.resource_monitors.cgroup_memory
threshold:
value: 0.95
阈值必须结合容器 Memory Limit、Envoy 实际堆占用和突发流量测试确定。不要把 max_memory_bytes 设置得高于容器真正可用内存,否则可能先被 OOM Kill,Overload Manager 根本来不及工作。
七、灰度、会话保持、镜像与故障注入
7.1 按权重灰度
route:
weighted_clusters:
clusters:
- name: orders_v1
weight: 95
- name: orders_v2
weight: 5
适合整体抽样灰度,但同一个用户的多个请求可能落到不同版本。
7.2 按 Header 精准灰度
- match:
prefix: /api/orders
headers:
- name: x-canary
string_match:
exact: "true"
route:
cluster: orders_v2
- match:
prefix: /api/orders
route:
cluster: orders_v1
适合内部账号、自动化测试或指定租户。生产上必须防止外部用户伪造灰度 Header,通常由可信入口先完成身份校验,再写入内部 Header。
7.3 一致性哈希
购物车、会话缓存或分片场景可以使用 Ring Hash 或 Maglev,根据 Cookie、Header 或源地址生成 Hash Key。它能减少 Endpoint 变化时的重映射,但不能替代真正的共享会话存储;扩缩容、故障摘除仍会改变映射。
7.4 Traffic Mirroring
镜像流量可以把生产请求的副本发送给新版本,用于兼容性验证。镜像响应不会返回给用户,但副本依然会产生真实副作用。因此写请求必须在影子环境禁用外部通知、扣费、发货等动作,或由业务显式识别 Shadow 请求。
7.5 Fault Injection
Envoy 可以按比例注入延迟或错误,用于验证超时、熔断和降级链路。故障注入 Filter 不应对所有人开放,建议同时具备:
- 只允许测试身份或指定 Header 触发;
- 限制最大比例和最长持续时间;
- 变更审计与自动过期;
- 明确的停止按钮;
- 与真实故障指标区分的标签。
八、安全:TLS 只是起点
8.1 下游 TLS 与上游 TLS 是两段连接
Envoy 可以在 Listener 终止客户端 TLS,也可以在连接上游时重新发起 TLS:
Client -- TLS A --> Envoy -- TLS B / mTLS --> Backend
不要因为入口有 HTTPS,就默认 Envoy 到后端也是加密的。
8.2 生产 TLS 基线
- 校验证书链和 SAN,不只检查“握手成功”;
- 明确最小 TLS 版本与允许的 Cipher;
- 内部可控链路优先使用 mTLS;
- 证书和私钥不要写入普通 ConfigMap 或镜像;
- 使用 SDS、Secret 挂载或工作负载身份系统完成轮换;
- 监控证书到期时间和 SDS 更新失败;
- xDS、SDS、外部鉴权和限流服务本身也要加密认证。
官方 SDS 支持控制面推送证书,Envoy 可以在不重启数据面的情况下切换新证书。若使用文件轮换,采用新目录加原子切换 Symlink 的方式,避免读到只更新一半的证书与私钥。
8.3 HTTP 边缘安全基线
Envoy 官方 Edge Proxy 最佳实践特别建议:
use_remote_address: true,并明确可信代理跳数;- 拒绝含下划线的 Header,避免不同组件解释不一致;
- 开启 Path Normalization 和 Slash 合并;
- 对转义斜杠选择明确策略;
- 限制请求 Header、Body、连接和 Buffer;
- 对 HTTP/2、HTTP/3 设置并发 Stream 上限;
- Admin 只绑定 localhost 或隔离管理网络;
- 入口认证、授权、限流采用清晰的 Fail-Open / Fail-Closed 策略。
鉴权服务不可用时是否放行,不存在统一答案:支付、管理后台通常 Fail-Closed;低风险公共读取接口可能选择受限 Fail-Open。关键是显式配置、分路由决策,并为降级行为设置指标和告警。
九、生产部署选择:原生 Envoy 还是 Envoy Gateway
| 条件 | 推荐路径 |
|---|---|
| 单机、VM、边缘节点,配置变化少 | 原生 Envoy + 静态配置 + Hot Restart |
| VM 集群且已有服务注册中心 | 原生 Envoy + 成熟或自研 xDS 控制面 |
| Kubernetes 南北向网关 | Envoy Gateway + Gateway API |
| Kubernetes 东西向治理、统一身份 | Istio 等成熟 Mesh,Envoy 作为数据面 |
| 只需要一个很简单的静态反向代理 | 先评估更简单的代理,避免过度建设 |
9.1 单独部署 Envoy 完全可行
答案是可以,而且这是 Envoy 的原生使用方式之一。Envoy Proxy 是独立的数据平面进程,不要求 Envoy Gateway、Istio、Kubernetes 或 Service Mesh 才能启动。只要提供一份 Bootstrap 配置,它就可以直接运行在物理机、VM、Docker、边缘节点或普通 Kubernetes Deployment 中。

不过要区分两个概念:
- 独立部署:不依赖 Gateway 或 Mesh 控制面,由团队直接管理 Envoy;
- 单实例部署:只有一个 Envoy 进程,进程、主机或可用区故障都会中断入口流量。
前者在生产上完全成立,后者通常只适合开发、测试或可以接受短暂停机的内部系统。生产入口更常见的结构是:外部 L4 Load Balancer 后面放置至少两个 Envoy 实例,跨节点或可用区部署;Envoy 实例保持无状态,共享同一套经过版本化的配置,但不共享进程内连接池和运行状态。
9.2 独立 Envoy 的三种配置管理方式
| 方式 | 工作方式 | 适用范围 | 主要代价 |
|---|---|---|---|
| 静态 Bootstrap | Listener、Route、Cluster 和 Endpoint 全部写入启动配置 | 路由少、变化慢的 VM 或边缘节点 | 变更通常需要滚动替换或 Hot Restart |
| 静态 Bootstrap + 文件型动态资源 | Bootstrap 固定,RDS/CDS 等资源从本地文件读取并原子替换 | 已有配置分发系统、变化频率中等 | 需要自己保证文件完整性、顺序和回滚 |
| 静态 Bootstrap + xDS | Bootstrap 只声明 Node 身份和控制面地址,资源由 xDS 下发 | 实例多、路由多、Endpoint 高频变化 | 需要维护控制面 HA、ACK/NACK、版本和权限 |
并不是一旦独立部署就必须自研 xDS。一个务实的演进顺序是:
- 先用静态配置跑通数据路径,把配置和 Envoy 镜像一起版本化;
- 用 CI 执行 Schema、
envoy --mode validate、集成测试和安全策略检查; - 通过负载均衡逐台摘流,滚动替换 Envoy 实例,保留上一版本快速回滚;
- 当 Route、Cluster 或 Endpoint 的变化速度超过静态发布能力时,再把对应资源迁移到文件订阅或 xDS;
- 证书需要高频轮换时,单独引入 SDS,不必一次性把所有配置都动态化。
9.3 独立部署的生产高可用基线
如果 Envoy 是公网或核心内部流量入口,至少补齐下面这些能力:
- 入口冗余:两个以上 Envoy 实例位于 L4 Load Balancer、Anycast 或 DNS 流量管理之后;
- 故障域隔离:实例跨主机、机架或可用区,剩余实例容量能够接管故障域流量;
- 健康检查:业务 Listener 和管理接口分离,负载均衡器只探测专用健康端点,不把完整 Admin 暴露出去;
- 配置供应链:配置进入 Git,发布前验证,分批生效,并记录配置版本、ACK/NACK 和回滚证据;
- 证书生命周期:使用 SDS 或原子文件轮换,监控证书过期和加载失败;
- 优雅摘流:发布前停止接收新流量,触发 Listener Drain,等待长连接退出,再终止旧进程;
- 容量保护:根据后端安全并发设置 Circuit Breaker,根据 Envoy 内存和连接模型配置 Overload Manager;
- 统一观测:集中采集所有实例的 Metrics、Access Logs、Traces、Response Flags 和配置版本。
单机上的最小发布门禁可以从下面三项开始:
# 1. 发布前拒绝非法配置
envoy --mode validate -c /etc/envoy/envoy.yaml
# 2. 启动后只从本机检查 Admin Readiness
curl -fsS http://127.0.0.1:9901/ready
# 3. 替换实例前由本机 Supervisor 触发优雅 Drain
curl -fsS -X POST 'http://127.0.0.1:9901/drain_listeners?graceful'
第三步之后还要由 Supervisor 或发布系统观察活跃连接,并在 Drain Deadline 到达后结束旧进程。不要让外部发布系统直接访问 Admin API;更安全的做法是在主机本地运行受限的部署代理或进程管理器。
9.4 什么时候应该升级到 Envoy Gateway 或成熟控制面
下面这些信号说明“自己管理原生 Envoy”的成本开始超过收益:
- Kubernetes 中有多个团队频繁创建和修改域名、证书与 Route;
- 需要标准化的 GatewayClass、Gateway、HTTPRoute 权限和状态模型;
- Envoy 实例数量很大,需要按租户或区域做精细配置下发;
- 团队正在重复实现服务发现、证书轮换、策略 CRD、状态回写和配置回滚;
- 需要 Service Mesh 提供的工作负载身份、东西向 mTLS 和统一服务治理。
这时引入 Envoy Gateway 或成熟 Service Mesh,不是因为 Envoy 不能单独工作,而是为了把控制面运维从业务团队手里收敛到一个标准化平台。
Envoy 的 Hot Restart 可以在新进程初始化完成后接管监听 Socket,再让旧进程 Drain 已有连接。但已有连接不会迁移到新进程,只能在 Drain 窗口内自然完成或最终终止。
Kubernetes 中通常不需要自己拼 Hot Restart 容器模型;更常见的是多副本 RollingUpdate、Readiness 摘流和 Graceful Drain。
十、Kubernetes 高可用实战:Envoy Gateway
一个生产网关至少要分别考虑三层高可用:
- 外部入口层:云负载均衡或硬件 LB;
- Envoy 数据平面:真正承载请求的 Proxy Pod;
- Envoy Gateway 控制平面:观察 Gateway API 并生成 xDS 配置。

这张拓扑图强调的是故障域分离,而不只是把副本数改成 3:
- 外部流量先经过 DNS 或 Global Traffic Manager,再由区域负载均衡只转发到健康的 Envoy 实例;
- Envoy Proxy 数据面跨三个可用区分布,任一区域失效时,其余区域仍有接管容量;
- Envoy Gateway 控制面采用独立副本并通过 Leader Election 协调,但它不进入业务数据路径;
- HPA 负责容量扩缩,PDB 约束自愿中断,Topology Spread 控制副本分散,Graceful Drain 处理发布和缩容期间的存量连接;
- Prometheus、OpenTelemetry 和日志平台需要跨实例聚合证据,避免只观察单个 Pod 得出错误结论。
图片表达部署边界,下面的 Mermaid 图则保留可复制、可维护的逻辑拓扑:
10.1 安装两个控制面副本
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.9.1 \
--namespace envoy-gateway-system \
--create-namespace \
--set deployment.replicas=2
kubectl wait --timeout=5m \
--namespace envoy-gateway-system \
deployment/envoy-gateway \
--for=condition=Available
Envoy Gateway Kubernetes Provider 默认使用 Leader Election。两个副本解决控制面进程或节点故障,但控制面副本增加并不等于数据面副本增加,需要单独配置 EnvoyProxy。
升级前必须核对 Gateway API CRD 兼容矩阵。官方 1.9.1 文档特别要求升级时先升级兼容的 CRD,再升级 Envoy Gateway Controller,顺序反了可能导致资源无法协调。
10.2 创建高可用 EnvoyProxy 策略
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: production-edge
namespace: edge-system
spec:
provider:
type: Kubernetes
kubernetes:
envoyDeployment:
container:
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 1Gi
pod:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
gateway.envoyproxy.io/owning-gateway-name: production-edge
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
gateway.envoyproxy.io/owning-gateway-name: production-edge
envoyHpa:
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
envoyPDB:
minAvailable: 2
shutdown:
drainTimeout: 90s
minDrainDuration: 15s
healthCheckFailureDelay: 5s
这里的值是一个起点,不是通用答案:
minReplicas: 3让三个可用区各有机会放置一个实例;PDB minAvailable: 2限制节点维护等自愿中断期间至少保留两个;- HPA 以 CPU 为起点,但大流量网关最好补充 RPS、活跃连接或并发 Stream 等自定义指标;
DoNotSchedule要求集群确实存在足够可用区和容量,否则 Pod 会 Pending;terminationGracePeriodSeconds必须大于 Drain Timeout 和清理余量;- PDB 不能阻止节点宕机等非自愿中断,也不会替代多可用区部署。
应用前先检查 CRD 是否支持字段:
kubectl explain envoyproxy.spec.provider.kubernetes.envoyHpa
kubectl explain envoyproxy.spec.provider.kubernetes.envoyPDB
kubectl apply --server-side --dry-run=server -f envoy-proxy.yaml
kubectl apply -f envoy-proxy.yaml
10.3 创建 GatewayClass、Gateway 和灰度 Route
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg-production
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-edge
namespace: edge-system
spec:
gatewayClassName: eg-production
infrastructure:
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: production-edge
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: api.example.com
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: api-example-com-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders
namespace: edge-system
spec:
parentRefs:
- name: production-edge
sectionName: https
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /orders
backendRefs:
- name: orders-v1
port: 8080
weight: 95
- name: orders-v2
port: 8080
weight: 5
应用并检查资源状态,而不是只看 kubectl apply 返回成功:
kubectl apply -f gateway.yaml
kubectl get gatewayclass eg-production -o yaml
kubectl -n edge-system get gateway production-edge -o yaml
kubectl -n edge-system get httproute orders -o yaml
kubectl -n envoy-gateway-system get deploy,pod,svc,hpa,pdb
重点查看 status.conditions 中 Accepted、Programmed、ResolvedRefs 等条件。声明已经写入 API Server,不代表 Listener 已经建立,也不代表 Backend 引用有效。
10.4 优雅关闭为什么重要
Envoy Gateway 的 Shutdown Manager 在 Pod 终止时会:
- 触发 Listener Graceful Drain;
- 让 Readiness 失败,从负载均衡目标中摘流;
- 监控剩余连接;
- 等连接归零或达到 Drain Timeout 后退出。
HTTP/1.1 会通过 Connection: close 引导客户端重连,HTTP/2 会发送 GOAWAY。对于 WebSocket、gRPC Stream、SSE 和大文件上传,90 秒未必够;需要从真实连接时长分布决定 Drain Timeout,而不是盲目追求“永不断连”。
十一、可观测性:必须同时观察流量、代理、上游与控制面
11.1 四类信号
| 层次 | 关键问题 | 代表指标或证据 |
|---|---|---|
| 下游请求 | 用户是否成功 | RPS、4xx/5xx、P50/P95/P99、活跃连接 |
| Envoy 自身 | 代理是否过载 | CPU、内存、FD、Worker 饱和、Overload Action |
| 上游服务 | 后端是否健康 | Healthy Endpoint、连接失败、超时、重试、Outlier Ejection |
| 控制面 | 配置是否一致 | xDS 连接、ACK/NACK、Update Rejected、配置版本 |
建议至少启用结构化 Access Log、Prometheus Metrics、OpenTelemetry Trace、Envoy 应用日志和控制面协调指标。
Access Log 推荐保留:
x-request-id或 Trace ID;- 下游与上游协议;
- Route、Cluster、Endpoint;
- 响应码与 Response Flags;
- 总耗时和上游耗时;
- 重试次数;
- TLS、SNI 或身份信息中的非敏感部分。
不要记录 Authorization、Cookie、Token、完整查询参数或个人敏感信息。
11.2 Envoy Gateway 接入 Prometheus
Envoy Gateway 默认提供控制面和数据面指标。已有 Prometheus Operator 时,可以用 PodMonitor 抓取 Proxy 的 metrics 端口和 /stats/prometheus 路径;官方示例中的 Proxy Metrics 端口为 19001。
排障时可以临时端口转发:
ENVOY_POD_NAME=$(kubectl get pod \
-n envoy-gateway-system \
-l gateway.envoyproxy.io/owning-gateway-name=production-edge \
-o jsonpath='{.items[0].metadata.name}')
kubectl -n envoy-gateway-system \
port-forward pod/${ENVOY_POD_NAME} 19001:19001
curl -s http://127.0.0.1:19001/stats/prometheus | head
11.3 一组实用告警
| 告警 | 意义 | 避免的误判 |
|---|---|---|
| 5xx 比例持续升高 | 用户错误率恶化 | 区分 Envoy Local Reply 和 Upstream 5xx |
UF、UH、UT、UO 增长 | 连接失败、无健康实例、超时或溢出 | 结合 Cluster 和 Route 维度 |
| 重试率或上游/下游请求比上升 | 故障被重试掩盖或放大 | 不只看最终成功率 |
| Healthy Endpoint 下降 | 后端容量减少 | 结合发布与健康检查日志 |
| xDS Update Rejected / NACK | 新配置未生效 | 告警中携带资源名和版本 |
| Envoy 内存接近上限 | OOM 与负载卸载风险 | 同时看 Overload Manager 是否触发 |
| 活跃连接逼近容量 | 新连接失败风险 | 按协议和 Listener 拆分 |
| 证书即将过期 | TLS 中断风险 | 区分 Listener 与 Upstream 证书 |
十二、性能调优:不要相信脱离配置的 QPS 数字
官方性能建议明确指出,不存在一个可以代表 Envoy 的统一 QPS 或延迟数字。结果会被下面因素显著改变:
- HTTP/1.1、HTTP/2、HTTP/3 或 TCP;
- 短连接、Keep-Alive、WebSocket 或 gRPC Stream;
- TLS、mTLS 和证书算法;
- Filter 数量以及是否调用外部服务;
- Access Log 格式与 Sink;
- 请求体和响应体大小;
- Worker 数、CPU Pinning、NUMA 和网卡;
- Endpoint 数、连接池和负载均衡算法;
- 重试、镜像和 Trace 采样比例。
12.1 压测方法
- 使用 Release 镜像和固定版本;
- 复刻生产协议、Filter Chain、TLS 与消息大小;
- 同时记录客户端、Envoy、宿主机和上游指标;
- 从低负载阶梯式提高,找到延迟拐点,而不是只找最大 QPS;
- 测正常、单实例故障、可用区故障和控制面断开;
- 测发布 Drain 期间长连接是否按预期结束;
- 以 P99、错误率、CPU、内存和连接数共同定义容量;
- 保留满足故障接管所需的容量余量,具体值由业务 SLO 和容灾模型决定。
12.2 --concurrency 不要随意设置
Envoy 默认按可见逻辑 CPU 数创建 Worker。容器环境必须确认 CPU Limit、CPUSet 和 Envoy 实际看到的核心数一致。Worker 太少无法利用 CPU,太多则增加连接池、内存和上下文切换成本。
对少量超长 HTTP/2 或 gRPC 连接,还要关注连接是否均匀分配到 Worker;仅提高 Worker 数不保证已有长连接重新平衡。
十三、发布与回滚:配置变更和二进制升级是两种风险
13.1 配置发布流水线
原生 Envoy 静态配置至少执行:
envoy --mode validate -c /etc/envoy/envoy.yaml
Envoy Gateway 配置至少执行:
kubectl apply --server-side --dry-run=server -f gateway.yaml
egctl x translate --from gateway-api --to xds -f gateway.yaml
egctl 的具体参数随版本变化,流水线应固定与 Envoy Gateway 相同版本并以该版本帮助信息为准。
13.2 二进制升级
- 阅读目标版本的 Breaking Change、Deprecated 和 Security Notes;
- 先升级非关键环境,再升级一小组数据面;
- 检查所有扩展、Wasm、Lua、Dynamic Module 的兼容性;
- 保持旧镜像和旧配置可快速回滚;
- 观察配置拒绝、连接重建、TLS 握手和延迟;
- 不要同时升级控制面、数据面、证书体系和所有流量策略。
13.3 CRD 升级不是普通应用升级
Helm 对 /crds 目录中的 CRD 不会像普通模板一样自动更新。Envoy Gateway 升级必须先核对官方兼容矩阵和版本迁移说明,保存现有资源,先升级 CRD,再升级 Controller,并验证现有 Route 状态和数据面配置。
十四、生产故障演练清单
高可用不是 YAML 属性,而是经过验证的系统行为。上线前至少演练以下故障。
14.1 后端实例故障
操作:终止一个 Backend Pod 或让健康接口返回失败。
验证:Endpoint 多久被摘除、窗口内错误数、是否出现重试风暴、剩余实例 CPU 和延迟,以及恢复后是否需要 Slow Start。
14.2 Envoy Proxy Pod 故障
操作:随机删除一个 Proxy Pod。
验证:LoadBalancer 摘除速度、连接中断数量、Deployment 补副本速度、PDB 和 Topology Spread 是否生效。
14.3 节点或可用区故障
操作:在测试环境隔离节点,或使用云平台故障演练能力。
验证:是否仍有跨区 Proxy、外部 LB 是否只发送到健康目标、后端是否有足够跨区容量、跨区带宽和延迟是否成为新瓶颈。
14.4 控制面不可用
操作:停止 Envoy Gateway Controller 或隔离 xDS 通道。
验证:已有数据面是否继续使用最后接受的配置、新 Proxy 是否能启动并获得配置、配置变更是否明确失败、xDS 断连告警是否触发、恢复后是否自动收敛。
14.5 错误配置
操作:向测试组下发引用不存在 Cluster 的 Route 或非法字段。
验证:Envoy 是否 NACK、旧配置是否继续工作、告警是否包含版本和资源名、自动化是否停止扩大发布。
14.6 证书轮换
操作:轮换 Listener、Upstream 和 xDS/SDS 证书。
验证:新旧连接是否连续、SAN 和信任根切换顺序是否正确、失败时是否可回滚、到期和加载失败指标是否有效。
14.7 过载与负载卸载
操作:逐步提高连接数、请求并发或大 Body 比例。
验证:Circuit Breaker 是否先于上游崩溃触发、Overload Manager 是否先于 OOM 触发、拒绝是否快速且可识别、故障解除后是否自动恢复。
十五、常见反模式
反模式 1:没有超时,只配重试
没有总时间预算的重试会堆积请求,并把后端变慢放大成整个系统的资源耗尽。
反模式 2:健康检查只返回进程存活
进程活着但依赖全部不可用时,继续接流量只会稳定地产生错误。Readiness 应反映“是否适合接收新流量”,Liveness 才回答“是否需要重启”。
反模式 3:把 Admin 暴露给 Prometheus 所在的大网络
Admin 不只是 Metrics 接口。优先使用独立 Metrics Listener、端口转发、Sidecar 或严格 Network Policy,不要把完整 Admin 当成普通监控端点。
反模式 4:所有路由共享同一套重试和超时
查询接口、支付写入、文件上传、SSE 与 gRPC Stream 的语义完全不同,应按路由分类设计。
反模式 5:HPA 只看 CPU,最小副本是 1
长连接、TLS 握手或连接数可能先成为瓶颈。一个副本也无法在滚动升级、节点维护或突发故障时提供高可用。
反模式 6:控制面 HA,数据面单副本
两个 Controller 不能替一个承载流量的 Envoy Pod 接管请求。控制面和数据面必须分别设计容量与容灾。
反模式 7:用流量镜像测试真实写操作
影子请求如果没有业务隔离,可能重复发消息、扣库存或触发第三方调用。
反模式 8:直接复制官方 Edge 配置上线
官方示例给出安全方向,不知道你的连接时长、Body、内存、协议和容量。所有阈值都必须经压测和故障接管测试校准。
十六、一份可执行的生产检查表
架构
- 明确 Envoy 是 Edge、Sidecar、Egress 还是内部 LB;
- 控制面和数据面分别有高可用设计;
- 至少跨节点部署,关键入口跨可用区;
- 依赖的 DNS、xDS、SDS、鉴权和限流服务有独立容灾;
- 单区故障后剩余容量仍满足 SLO。
流量策略
- 每类 Route 有明确 Timeout;
- 重试只用于可安全重放的错误与请求;
- 配置 Retry Budget 或重试熔断;
- Circuit Breaker 阈值来自容量模型;
- 主动健康检查和 Outlier Detection 经过故障测试;
- 长连接、流式响应和大文件有独立策略。
安全
- 下游和上游 TLS 分别建模;
- 校验证书链与 SAN;
- 证书可以自动轮换并有到期告警;
- Admin 不暴露到公网或普通业务网络;
- Path、Header、Body 和 Buffer 有限制;
- 鉴权与限流依赖有明确 Fail-Open / Fail-Closed 策略。
Kubernetes
- 数据面最小副本满足故障域要求;
- HPA 有正确 Resource Request,并验证扩容速度;
- PDB、Topology Spread、RollingUpdate 配置一致;
- Drain Timeout 与
terminationGracePeriodSeconds匹配; - Gateway、Route 和 Backend 的 Status Condition 进入预期状态;
- CRD 与 Envoy Gateway 版本兼容。
可观测与发布
- Access Log、Metrics、Trace 能用 Request ID 关联;
- 区分 Local Reply 与 Upstream Error;
- 监控 ACK/NACK、配置版本和 Update Rejected;
- 配置与镜像都能独立灰度、独立回滚;
- 已完成 Backend、Proxy、Node、Zone、Control Plane 和证书演练;
- Runbook 包含常见 Response Flag 和排障命令。
十七、总结:Envoy 的价值是可编程的可靠性边界
学习 Envoy 最容易走进两个极端:要么只停留在反向代理 YAML,要么一开始就陷入 xDS Proto 和控制面实现。
更有效的路径是分四步:
- 用静态配置掌握 Listener、Filter、Route、Cluster 和 Endpoint;
- 加入超时、重试、熔断、健康检查和异常摘除,并主动制造故障;
- 建立 Metrics、Access Log、Trace、配置版本和告警体系;
- 当配置规模和变化频率真正需要时,再引入 Envoy Gateway、Service Mesh 或自研 xDS。
Envoy 不会自动让系统高可用。真正的高可用来自:多个独立故障域、足够的接管容量、受控的失败方式、可回滚的配置、可观察的状态,以及反复演练过的恢复流程。
当这些部分都存在时,Envoy 才不只是一个代理,而是服务与故障之间的一道可编程可靠性边界。
官方资料
- Envoy 官方文档与稳定版本列表
- Envoy:Life of a Request
- Envoy:Edge Proxy 最佳实践
- Envoy:xDS Dynamic Configuration
- Envoy:xDS REST and gRPC Protocol
- Envoy:Circuit Breaking
- Envoy:Health Checking
- Envoy:Outlier Detection
- Envoy:Overload Manager
- Envoy:TLS 与 SDS
- Envoy:Hot Restart 与 Draining
- Envoy:Admin Interface
- Envoy Gateway:Helm 安装
- Envoy Gateway:Customize EnvoyProxy
- Envoy Gateway:Graceful Shutdown
- Envoy Gateway:Proxy Metrics
- Kubernetes:Pod Disruptions 与 PDB
- Kubernetes:Pod Topology Spread Constraints