把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)