把Nginx换成Envoy后,我们踩到的7个坑
背景交代:
公司ToB SaaS业务,QPS峰值8k,原架构是Nginx + Lua做流量网关。为了对接Istio、顺手蹭Envoy的observability,我们拍板——“无痛”迁移。结果从立项到全量灰度花了6周,踩坑无数。下面把血泪笔记整理出来,供后来人“无痛”翻坑。
1. 为什么要换?
坦白说,最初的理由只有两个:
- 老板在PPT上看到Envoy能“自动熔断”和“分布式tracing”。
- 运维受够了Nginx reload时5~10秒的502高峰。
技术选型会上,我默默列了张对比表,原以为能劝退,结果老板一句“长期价值”就给过了……
维度 | Nginx (1.24) | Envoy (1.28) | 备注 |
---|---|---|---|
热更新 | reload 5-10s | 零中断 | 运维最爱 |
熔断/重试配置粒度 | location级 | 路由级 | 细到header |
可观测性 | stub_status | 原生Prometheus | 省一个Exporter |
Lua扩展 | 有 | 没有 | 后面细说 |
内存占用 | ~30 MB | ~90 MB | 贵了三倍 |
2. 坑1:Lua脚本没法平迁
Nginx时代我们写了3k行Lua做鉴权、灰度、ABTest。Envoy官方推荐用Lua Filter,但实测发现:
- Lua版本只支持5.1,连
table.pack
都没有。 - 每个worker独立VM,全局缓存要用
shared dict
,迁移成本爆炸。
最终妥协:把最重的鉴权逻辑拆成Go的External Authorization Server,Envoy只留轻量级灰度逻辑。多了一次RTT,但CPU反而降了5%,因为Go的垃圾回收比LuaJIT的GC压力小。
3. 坑2:metrics名字长到让Grafana崩溃
Envoy默认metrics格式长成这样:
envoy_cluster_upstream_rq_retry{cluster="user-svc",env="prod"} 123
Prometheus一拉就是3w+条。我们用了官方的stats-matcher
正则过滤,把.*circuit_breakers.*
等不关心的维度全干掉,磁盘瞬间少了70%。过滤前后对比如下:
指标 | 过滤前 | 过滤后 |
---|---|---|
活跃metrics数 | 31,042 | 4,198 |
5分钟抓取耗时 | 4.8s | 0.9s |
SSD占用/天 | 2.1GB | 680MB |
4. 坑3:HTTP/2 并发流控被打爆
上线第二天,用户反馈上传大文件必超时。抓包一看:Envoy默认initial_window_size
只有64 KB,文件上传时流控窗口瞬间被耗尽。
解决办法:把initial_window_size
调到1 MB,同时把max_concurrent_streams
从100提到500(再高客户端hold不住)。
调优后,99线RT从3.2s降到1.1s。
5. 坑4:TLS指纹导致老安卓App握手失败
Nginx用的是OpenSSL 1.1.1,Envoy默认BoringSSL。两者TLS指纹不一致,结果线上统计到Android 7.0以下机型握手失败率2.8%。
临时方案:
- 在Listener层加
match_typed_filter_chain
,降级到TLS 1.2; - 对老UA单独打标,切到“兼容”集群。
一周后失败率降到0.1%。
6. 坑5:灰度发布忘记关“全局限速”
Envoy的local_rate_limit
是全局token桶,上线时直接套用了demo配置:100 rps。结果我们8k QPS一进来直接熔断,整站503两分钟。
复盘:任何灰度发布,先把限流调到10倍QPS再缩回去,别嫌麻烦。
7. 坑6:日志格式不兼容ELK
Nginx日志是经典的$remote_addr - $user [$time_local]
格式,而Envoy默认JSON,字段名全是request_id
、upstream_cluster
之类的驼峰。
我们写了个Logstash Ruby filter做字段映射,跑了两天又发现:某些字段值里有未转义的双引号,导致JSON解析失败。
最后干脆放弃,改走Envoy的access_log_formatter
自定义成Nginx兼容格式,ELK侧零改动。
8. 坑7:控制面竟比数据面先挂
Envoy依赖Pilot(Istiod)下发xDS。压测时发现:Istiod OOM重启期间,Envoy拿不到新路由,老路由还缓存着,导致切流失败。
解决:
- 把Istiod内存从2G调到8G(简单粗暴);
- 给Envoy加
--concurrency=4
限制线程,减少xDS推送压力; - 预热阶段用静态bootstrap兜底,防止完全失联。
总结:一句话看清ROI
项目 | 数值 |
---|---|
迁移周期 | 6周 |
人肉投入 | 3人*6周 ≈ 432工时 |
P99延迟下降 | 25% |
502错误率下降 | 从0.8%到0.02% |
运维人肉reload次数 | ∞ → 0 |
值不值?老板看完报表说“明年给网关团队加预算”。
我的教训是:别信“无痛”两个字,把预期调到“有痛可救”,才是Envoy的正确打开方式。
–
- 点赞
- 收藏
- 关注作者
评论(0)