2022 年我们公司上了 Istio,本以为"流量管理 + 安全 + 可观测性"一站式解决。结果跑了 14 个月之后撤下,改回 Spring Cloud Gateway + 应用埋点。撤下的原因不是 Istio 不好,而是我们用不到那 80% 的能力,却被那 20% 的复杂性反复打击。本文写实记录 Istio 上线和下线的全过程,给评估 Service Mesh 的团队做参考。
当初上 Istio 的理由
2022 年技术决策书摘要:
- 微服务 50+ 个,语言混杂(Java / Go / Python / Node)
- 想要统一的灰度发布 / A/B 测试
- 想要 mTLS 加密内部流量
- 想要 distributed tracing 透明接入
- 想要 fault injection / circuit breaker 不写业务代码
- 看到大厂分享(Lyft / Google)说效果很好
结论:全面接入 Istio,踩坑大厂踩过的路就行
实际遇到的问题
问题 1:sidecar 内存 + CPU 开销
每个 Pod 多一个 envoy sidecar:
- 内存:50-200MB(依配置复杂度)
- CPU 空载:5-10%,有流量时 15-30%
- 50 个微服务 × 平均 8 个 Pod = 400 个 envoy
- 集群总开销:400 × 100MB = 40GB 内存,40 vCPU
业务代码本身只用 25vCPU + 30GB 内存
sidecar 的开销 比业务本身还大,资源利用率被腰斩
问题 2:延迟增加
实测延迟数据(同集群跨节点):
无 Mesh 有 Istio
p50 单次 RPC 0.8ms 2.5ms (+1.7ms)
p99 单次 RPC 5ms 15ms (+10ms)
p999 单次 RPC 20ms 80ms (+60ms)
业务调用链平均 4-5 跳,p99 延迟从 30ms 涨到 120ms
问题 3:故障域扩大
某天集群 istiod 节点 OOM 重启:
14:23:01 istiod 重启
14:23:05 新部署的 Pod 拿不到 xDS 配置
14:23:05 部分服务 503
14:23:15 envoy 缓存的旧配置过期,部分调用 mTLS 握手失败
14:23:30 业务受影响,客服收到投诉
14:25:00 istiod 恢复,xDS 重新下发
事后:
- 单点 istiod 是故障域
- envoy 配置变更高峰 CPU 飙升
- mTLS 证书过期问题排查 1 小时
问题 4:学习曲线 + 排查成本
线上事故:服务 A 调用服务 B 偶发 503
排查过程:
1. 看 A 应用日志 — 正常,只看到下游 503
2. 看 B 应用日志 — 没收到请求(?)
3. 看 A 的 envoy access log — 发出去了
4. 看 B 的 envoy access log — 没收到
5. 看 istiod 日志 — 一切正常
6. 翻 Istio 文档 / 社区 issue
7. 用 istioctl proxy-config endpoint 看路由
8. 发现 DestinationRule 的 trafficPolicy 配错
9. 修复
总耗时:3 小时
若无 Istio:看 nginx 日志,30 分钟内能定位
问题 5:用不到的 80% 功能
我们实际用到的:
✓ mTLS (但内网本来就有 VPN,价值小)
✓ 基础 trace (但 Skywalking 已经做了)
✓ 流量分流(灰度) (但 5 次 / 月,Spring Cloud 也能做)
我们没用到的:
✗ Fault injection (业务团队拒绝在 prod 注入故障)
✗ Circuit breaker (应用层 Resilience4j 已经做了)
✗ Rate limiting (网关层已经做了)
✗ JWT 认证 (业务方有自己的 SSO)
✗ Wasm extension (没人会写)
✗ Service Entry (外网调用走专门网关)
✗ Multi-cluster mesh (单集群足够)
结论:Istio 给我们的核心价值 ≈ Spring Cloud Gateway + Skywalking 已经提供的
撤下决策
2023 Q4 评审会:
正方:已经投入了,沉没成本
反方:开销大、故障域宽、排查难、用不到大部分功能
最终决策:撤下 Istio,改回:
- 流量管理: Spring Cloud Gateway + Nacos
- 服务调用: OpenFeign + Resilience4j
- mTLS: 关闭内部 mTLS,网关层用 HTTPS 终止
- Trace: Skywalking 应用层埋点
- 灰度: Spring Cloud Gateway 路由规则
预期:延迟降 30%、资源节省 40GB 内存、运维门槛降低
撤下过程
阶段 1:停止扩张
# 1. 标记不再注入 sidecar
$ kubectl label namespace business-prod istio-injection=disabled --overwrite
# 2. 新 Pod 不再注入
# 3. 老 Pod 还有 sidecar,继续跑
# 4. 业务团队评估各服务依赖 Istio 的程度
# 输出:依赖矩阵
service-a: 仅用 mTLS (低依赖)
service-b: 用 VirtualService 灰度 (中依赖)
service-c: 仅用 trace (低依赖)
service-x: 用 Wasm 自定义 filter (高依赖,需要改造)
阶段 2:替换核心能力
# 灰度发布:从 VirtualService 改 Spring Cloud Gateway
# 老的 VirtualService:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts: [order-service]
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: order-service
subset: canary
- route:
- destination:
host: order-service
subset: stable
# 改 Spring Cloud Gateway 路由:
spring:
cloud:
gateway:
routes:
- id: order-canary
uri: lb://order-service-canary
predicates:
- Path=/api/order/**
- Header=X-Canary, true
- id: order-stable
uri: lb://order-service-stable
predicates:
- Path=/api/order/**
// 熔断:Resilience4j 取代 Istio 的 outlierDetection
@RestController
public class OrderController {
private final CircuitBreaker cb = CircuitBreaker.ofDefaults("orderService");
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
return cb.executeSupplier(() -> orderClient.getOrder(id));
}
}
// application.yml
resilience4j.circuitbreaker:
instances:
orderService:
failureRateThreshold: 50
slowCallRateThreshold: 50
slowCallDurationThreshold: 2s
slidingWindowType: COUNT_BASED
slidingWindowSize: 100
minimumNumberOfCalls: 50
waitDurationInOpenState: 30s
// 重试:Spring Retry
@Retryable(value = {ConnectException.class},
maxAttempts = 3, backoff = @Backoff(delay = 100, multiplier = 2))
public Order getOrder(Long id) { ... }
阶段 3:逐 namespace 卸载 sidecar
# 1. 滚动重启 namespace 下所有 deployment
$ kubectl rollout restart deployment -n business-prod
# 2. 新 Pod 不带 sidecar 起来,老 Pod 才被替换
# 3. 监控期 24 小时,无异常进入下一个 namespace
# 4. 卸载 Istio control plane(最后一步)
$ istioctl uninstall --purge -y
$ kubectl delete namespace istio-system
# 流量从 Istio mTLS 平滑切到普通 HTTP 的方法:
# 1. PeerAuthentication 改 PERMISSIVE(同时接受 mTLS 和明文)
$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: PERMISSIVE
EOF
# 2. 业务 client 都改用明文(去掉 mTLS 标记)
# 3. 全部切完后,直接撤 sidecar
撤下后的实际收益
指标 Istio 时期 撤下后 变化
=====================================================
集群内存使用 220GB 170GB -50GB
集群 CPU 使用 180 vcore 110 vcore -70 vcore
平均 RPC p99 延迟 120ms 45ms -63%
排障平均时间 平均 1.5h 平均 30 min -67%
基础设施学习成本 高 (两周入门) 中 (1周)
运维人员需求 3人 1人
什么时候应该上 Istio
不是说 Istio 不好,而是要看场景。该上 Istio 的几个标志:
- 微服务 > 200 个,跨多个语言栈,统一治理价值大
- 有专职 Service Mesh / 平台团队(3-5 人)
- 核心需求是多集群联邦 / 跨语言一致性
- 组织内有强需求做 chaos engineering,需要 fault injection
- 对 mTLS 有合规要求(金融 / 医疗)
- 资源充裕(集群 1000+ Pod 起步)
什么时候不该上
- 微服务 < 100,Spring Cloud / Dubbo 治理足够
- 没有专职运维团队,业务团队兼职
- 主要语言栈单一(全是 Java),框架能覆盖大部分需求
- 延迟敏感(网关 + RPC,p99 要求 < 50ms)
- 资源紧张,sidecar 30% overhead 无法接受
- 团队还在快速迭代期,Istio 升级会成阻碍
替代方案对比
需求 Istio Spring Cloud 自研
==============================================
mTLS ✓✓✓ ✗ ✗
灰度发布 ✓✓ ✓✓ ✓
熔断 ✓ ✓✓ (R4j) ✓
trace ✓✓ ✓ (sleuth) ✓ (sw)
跨语言 ✓✓✓ ✗ ✗
学习成本 高 中 低
运维成本 高 低 视情况
性能开销 高 低 低
反思
这次 Istio 上线 + 下线,从技术选型角度学到的教训:
- 不要为了技术而技术。问"业务需要什么",不是问"业界用什么"
- 评估时看自己的 80% 场景,不要被官方的 100% 功能矩阵迷惑
- 新技术的引入成本要看 3 年,不只是上线时
- 大厂分享的最佳实践不一定适合中小团队,他们有专职团队
- 架构决策要有退路设计:上线时就规划好怎么撤下
- 关键指标:每个能力的实际使用率,30% 以下的功能就别上
Istio 是个工程伟大的产品,但工程上的伟大不等于业务上的合适。我们撤下不是 Istio 的失败,而是我们对自己需求的重新认知。技术选型最重要的是"诚实",承认自己不需要的功能就别买单。希望这篇能帮到正在评估 Service Mesh 的团队。
—— 别看了 · 2026