从 Docker Compose 迁到 K8s:踩坑实录

从 Docker Compose 迁到 K8s:踩坑实录
踱鸽&水晶蟹从 Docker Compose 迁到 K8s:踩坑实录
迁移策略:哪些服务先动
迁移不是一次性的,我们采用的策略是无状态服务优先、按流量从小到大。
第一批先迁 user-service 和 product-service——无状态,没有持久化需求,出了问题回滚也容易。第二批迁 order-service 和 promo-service,有数据库依赖,需要把 PVC 配好。中间件(MySQL、Redis、RabbitMQ)保持 Docker Compose 运行,不在这次迁移范围里。
整个迁移前后花了大概 6 周,期间线上始终保持可用。
坑一:Pod 一直 CrashLoopBackOff
现象
order-service 部署上去,Pod 状态一直是 CrashLoopBackOff,kubectl logs 看到的错误是:
|
排查路径
先看 kubectl describe pod order-service-xxx,Events 里只显示容器反复重启,没有额外信息。从报错入手——UnknownHostException: mysql,说明应用找不到名为 mysql 的主机。
根因
在 Docker Compose 里,服务可以通过服务名(mysql)互相访问,因为它们在同一个用户定义网络里。K8s 里访问其他服务需要用 Service 名 + Namespace,或者如果在同一个 Namespace 里,直接用 Service 名也可以,但前提是那个 Service 已经存在。
问题出在两处:一是数据库还跑在 Docker Compose 里(宿主机),K8s 里的 Pod 根本找不到名叫 mysql 的 Service;二是配置文件里的数据库地址还是写的 mysql,没有改成宿主机 IP。
解决方案
数据库迁移前,应用里的 DB 连接地址改为宿主机 IP + 暴露端口。正式迁移数据库后,用 K8s Service(ExternalName 或 ClusterIP)统一管理。
坑二:服务间调用偶发超时
现象
order-service 调 inventory-service 时,偶发 Connection timed out,但重试之后就好了。这种偶发问题最难查,日志里大多数时间都是正常的。
排查路径
在 SkyWalking Trace 图里看慢请求,发现超时的请求在 DNS 解析阶段就花了很长时间(超过 500ms),而正常请求 DNS 解析只要 1ms。问题定位在 DNS 上。
用 kubectl exec 进容器测试:
|
根因
K8s 默认的 DNS 配置里,ndots 值是 5。这意味着对于 inventory-service 这样的短域名,Pod 会先依次尝试以下路径:
inventory-service.order-namespace.svc.cluster.localinventory-service.svc.cluster.localinventory-service.cluster.localinventory-service(最终找到)
每次调用都要经过多次 DNS 查询,CoreDNS 在高并发下出现了性能瓶颈,导致偶发超时。
解决方案
两个方向:
- 调用方改用 FQDN(全限定域名):
inventory-service.default.svc.cluster.local,直接命中,不走多次查询 - 在 Pod 的
dnsConfig里把ndots改小:
|
我们采用的是方案二,改完之后偶发超时消失了。
坑三:重启后数据丢失
现象
product-service 的 Pod 在某次节点迁移后重启,发现上传的商品图片都不见了。图片存的是本地文件系统(临时方案,还没迁 Minio)。
根因
Pod 里写入容器文件系统的数据,随着 Pod 重启就消失了。这是 K8s 和 Docker 最本质的区别之一:Docker 容器重启数据还在(除非 --rm),K8s 的 Pod 被重新调度到新节点,之前节点上的数据就没了。
更深层的问题:我们有给这个服务配 PVC,但 StorageClass 的 reclaimPolicy 默认是 Delete,Pod 删除后 PV 也跟着回收了。
解决方案
- 修改 PVC 对应的 StorageClass,把
reclaimPolicy改为Retain,确保 Pod 删除后 PV 数据不被自动清理 - 根本解法是把文件存储迁到对象存储(Minio / 云存储),不依赖节点本地磁盘
迁移完成后的真实状态
最终,无状态服务全部迁上了 K8s,有状态中间件(MySQL、Redis)还跑在原来的云主机上,短期内没有计划迁。
稳定之后,整体比 Docker Compose 阶段顺多了——自动扩缩容、滚动发布、故障自愈这些能力确实省了不少运维精力。但也有一些地方还在打补丁:日志采集方案换了两次才稳定下来,配置管理还有几个服务在用环境变量而不是 ConfigMap。
有一个服务最终没有迁:一个用 Python 写的数据处理脚本,迁移改造的成本高于收益,保持 Docker 单机跑了。
