起因
微服务架构需要 API gateway 做:
- 路由(path → service)
- LB
- 限流 / 熔断
- TLS termination
- 鉴权(OIDC / API key)
- observability (metrics / traces)
老办法 nginx + Lua / openresty,扩展靠 module / 第三方 script。
Envoy(Lyft 出,CNCF)是云原生 API gateway 标准:
- xDS API 动态配置(控制面 push 配置)
- 内置 metric / trace
- HTTP/2 / HTTP/3 / gRPC 一等公民
- Istio / Consul Service Mesh 底层用它
装 / 跑
docker run -d -p 10000:10000 \
-v $(pwd)/envoy.yaml:/etc/envoy/envoy.yaml \
envoyproxy/envoy:v1.30.0
最小配置
# envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 }
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: ingress_http
route_config:
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: backend_service }
http_filters:
- name: envoy.filters.http.router
clusters:
- name: backend_service
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: backend_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: backend, port_value: 8080 }
监听 10000,转发到 backend:8080。
路由
routes:
- match: { prefix: "/api/users" }
route: { cluster: user_service }
- match: { prefix: "/api/orders" }
route: { cluster: order_service }
- match:
prefix: "/admin"
headers:
- name: x-internal-tool
string_match: { exact: "true" }
route: { cluster: admin_service }
- match: { prefix: "/" }
route: { cluster: web_frontend }
prefix / path / regex / header / query 匹配,路由到不同 cluster。
LB 策略
clusters:
- name: api_service
lb_policy: LEAST_REQUEST # ROUND_ROBIN / LEAST_REQUEST / RANDOM / RING_HASH
load_assignment:
endpoints:
- lb_endpoints:
- endpoint: { address: { socket_address: { address: api-1, port_value: 8080 }}}
- endpoint: { address: { socket_address: { address: api-2, port_value: 8080 }}}
LEAST_REQUEST 比 round-robin 更智能(少请求的 instance 接新请求)。
限流
http_filters:
- name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100
tokens_per_fill: 100
fill_interval: 1s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value: { numerator: 100 }
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value: { numerator: 100 }
每秒 100 req,超出 429。
或者全局限流(多 envoy 共享)→ 外接 rate limit service(如 lyft/ratelimit)。
熔断
clusters:
- name: backend
circuit_breakers:
thresholds:
- max_connections: 100
- max_pending_requests: 50
- max_requests: 100
- max_retries: 3
backend 慢 / 死 → envoy 主动拒新请求(避免雪崩)。
retry
routes:
- match: { prefix: "/" }
route:
cluster: backend
retry_policy:
retry_on: "5xx,reset,connect-failure"
num_retries: 3
per_try_timeout: 5s
5xx / connection reset 自动重试 3 次。
对 idempotent 请求安全;POST / 写要小心。
TLS
listeners:
- address:
socket_address: { address: 0.0.0.0, port_value: 443 }
filter_chains:
- transport_socket:
name: envoy.transport_sockets.tls
typed_config:
common_tls_context:
tls_certificates:
- certificate_chain: { filename: /etc/certs/cert.pem }
private_key: { filename: /etc/certs/key.pem }
ALPN / SNI 自动。HTTP/2 自动协商。
HTTP/3 / QUIC 也支持(额外配 udp_listener_config)。
鉴权:OAuth2 / JWT
http_filters:
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
my_provider:
issuer: https://auth.example.com
remote_jwks:
http_uri:
uri: https://auth.example.com/.well-known/jwks.json
cluster: jwks_cluster
timeout: 5s
cache_duration: 600s
forward: true
rules:
- match: { prefix: "/api" }
requires: { provider_name: my_provider }
envoy 自动验 JWT,无效 401。验过的 claim 转发给 backend。
backend 不必再写 JWT 验逻辑。
ext_authz(外部鉴权 service)
复杂鉴权(动态 ACL / per-resource permission)→ envoy 调外部 service:
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
grpc_service:
envoy_grpc:
cluster_name: ext_authz_cluster
每请求 envoy → gRPC 调 authz service → allow/deny。
集中 policy decision,service 不写 authz。
observability
# admin endpoint
admin:
address:
socket_address: { address: 0.0.0.0, port_value: 9901 }
http://localhost:9901/stats → 几百个 metric。
http://localhost:9901/clusters → 当前 cluster 状态。
Prometheus scrape /stats/prometheus。
tracing
tracing:
http:
name: envoy.tracers.opentelemetry
typed_config:
grpc_service:
envoy_grpc:
cluster_name: otel_collector
service_name: my-gateway
每请求自动产生 span,发 OTEL collector → Jaeger / Tempo。
backend trace 跟 gateway 串联。
动态配置(xDS)
静态 envoy.yaml 改配置要重启。
xDS API 让 envoy 从 control plane(Istio / Consul / 自家)pull 配置:
dynamic_resources:
ads_config:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: xds_cluster
router / cluster / listener 全运行时改,无中断。
大规模 / 多 envoy 必备。
vs nginx
| nginx | Envoy | |
|---|---|---|
| 配置 | nginx.conf(声明式) | yaml + xDS |
| 动态更新 | reload(断老连接) | xDS 真 hot reload |
| HTTP/2 / 3 | 支持但 retrofit | first-class |
| gRPC | 透传 | 原生(gRPC routing) |
| observability | 第三方 | 内置 metric + trace |
| 学习曲线 | 中 | 高(yaml 长) |
| 资源 | 小 | 中 |
简单场景 nginx 够。微服务 + mesh / 动态路由 / gRPC → Envoy。
与 API gateway product 对比
- Kong:基于 nginx + 插件
- Tyk:Go 写,open source
- APISIX:基于 nginx + etcd
- Envoy + Istio:service mesh 体系
Envoy 是底层 + 通用。Kong/APISIX/Tyk 是产品(envoy 上层封装)。
要 admin UI / 插件市场 → API gateway 产品。
完全自控 → envoy 自配。
真实部署
我们一个 k8s 集群,envoy 当 ingress(替代 nginx-ingress):
- 200 微服务
- 10w QPS 峰值
- envoy 2 replica(HPA 到 8)
- xDS 控制面 = Istio + 自家 control plane
效果:
- 配置变更 1 秒 propagate(vs nginx reload 几秒中断)
- gRPC 路由原生(vs nginx 需要插件)
- per-service mTLS 自动(mesh 模式)
踩过的坑
-
yaml 配置长:1000+ 行常见。用 helm / kustomize 模板化。
-
memory 使用:默认 envoy 几百 MB;大 route 表几 GB。监控 +
adjust。 -
JWKS cache miss:JWT auth 频繁拉 JWKS endpoint → IdP rate limit。
cache_duration: 600s加大。 -
route order:从上到下匹配第一个。
/prefix 放最前 → 后面
都不生效。常见错。 -
TLS cert reload:file 改了 envoy 不自动 reload。SDS (Secret
Discovery Service) 动态推或者用 cert-manager + restart envoy
pod。
登录后参与评论。