120 个微服务零故障迁 K8s:3 个月实战迁移中没人讲的 6 个坑
概述 把 120 多个微服务从传统虚拟机部署迁移到 Kubernetes,听起来是个标准项目:写 Dockerfile、建集群、迁移服务、切流量。规划会议上人人都觉得三个月够了。实际干起来你会发现,真正的难点不在"怎么把服务跑起来"——那只是 helm install 的事——而在于那些迁移过程中没人提前告诉你的坑:服务依赖的循环引用、有状态服务的内存态丢失、CNI 插件在大规模下的性能拐点、灰度切流时的 DNS 缓存陷阱、配置管理的环境变量膨胀、监控指标在迁移后的断层。 这篇文章是我主导某新能源物流平台 120+ 微服务 K8s 全量迁移的复盘。3 个月,零故障(这里的"零故障"指迁移过程未引发 P0/P1 级业务中断,不是零报错),扩缩容从 25 分钟降到 1 分钟,发布效率提升 50%。但过程远没有结果看起来那么顺利——光服务依赖梳理就花了三周,CNI 性能问题排查折腾了一周,灰度切流时 DNS 缓存导致的间歇性 502 让团队熬了两个通宵。 我写这篇文章的目的不是教你 K8s 基础概念,而是把迁移过程中真正卡住我们的 6 个坑拆开讲清楚,每个坑都附上架构决策过程和替代方案对比。如果你正在做或准备做大规模微服务 K8s 迁移,这些经验能帮你少走弯路。 坑一:服务依赖梳理——你以为的线性依赖其实是张循环图 问题:依赖检测做了一半就发现了死循环 迁移第一步是梳理服务依赖关系,确定迁移顺序。我们的标准做法很简单:找出没有下游依赖的服务先迁,然后逐层往上推。用 grep 扫了一遍配置文件里的数据库连接和 API 调用地址,画出了依赖拓扑图。 第一周很顺利。第二周开始出问题了——配置文件里写的依赖只是冰山一角。运行时的动态依赖、通过服务发现(Eureka/Nacos)路由的调用、消息队列触发的异步链路,这些都不在静态配置里。等我们把所有依赖补完,发现依赖图里有三个循环引用:A 调 B,B 调 C,C 又调 A。 这不算什么新鲜事。微服务架构演进两三年后,循环依赖几乎是必然产物。但迁移时它是个致命问题:你不能分批迁移了。A、B、C 必须同时迁,否则跨平台调用(K8s 内服务调 K8s 外服务)的网络延迟和 DNS 解析会成为性能瓶颈。 解法:运行时调用链分析 + 分批策略调整 光看配置文件不够。我们最终用了两套工具配合: 第一套:Jaeger 分布式链路追踪 在迁移前一个月,给所有服务接入了 OpenTelemetry SDK,通过 Jaeger 采集全量调用链路。跑了一周,拿到了真实运行时的服务调用关系图。静态配置里没出现的依赖关系,在链路数据里暴露无遗——光是消息队列触发的异步调用链就有 14 条,其中 3 条是循环依赖。...