1. 从一次真实的迁移需求说起
去年底,我接手了一个挺有意思的活儿:把一套跑在单节点 Kubernetes 上的若依微服务整套环境,准不停服、不丢数据地迁移到云上 ECS。这个需求和 QQ 全量上云在本质上是同一类问题——都是把原本跑在物理机或自建机房里的核心业务,平滑搬到云上,同时保证业务不中断、数据不丢失。区别只是规模大小:QQ 是亿级用户的全量上云,我们这套若依环境是几十个微服务的中小规模迁移,但底层要解决的技术问题是一样的。
如果你正在做类似的事情——不管是把自建机房的业务往云上搬,还是把一套微服务从一台机器挪到另一台机器,又或者你只是好奇“全量上云”这四个字背后到底藏着多少技术细节——那这篇内容应该能给你一些直接能用的参考。我会从整体设计思路、核心组件选型、实操迁移步骤、压测验证方法、常见问题排查这几个维度,把整个迁移过程拆开来讲清楚。涉及到的关键词包括 VPC、CLB、Kubernetes、ECS、JMeter 压测等,这些我都会结合实际操作来说明它们在整个链路里扮演什么角色。
先说清楚一个前提:全量上云不是把镜像打包往云上一扔就完事了。它涉及到网络架构的重新设计、流量入口的切换、数据的一致性保障、回滚方案的准备,以及迁移后的承载能力验证。任何一个环节出问题,都可能导致业务中断或者数据丢失。所以下面我会按照实际操作的顺序,一层一层往下拆。
2. 整体迁移方案的设计思路与选型考量
2.1 为什么选择“准不停服”而不是“完全不停服”
先解释一下“准不停服”这个概念。完全不停服意味着在整个迁移过程中,用户请求一秒都不能断,这对架构的要求极高,通常需要双活或者灰度切流的方案,成本很高。而“准不停服”允许在切换的瞬间有极短的服务不可用窗口(比如几秒到几十秒),但数据绝对不能丢,业务恢复后用户无感知。
对于若依微服务这套环境来说,选择准不停服是性价比最高的方案。原因有三点:第一,若依的微服务架构本身支持多实例部署,可以通过滚动更新的方式减少中断时间;第二,单节点 K8s 意味着没有高可用集群,无法做到真正的零中断;第三,迁移窗口可以安排在业务低峰期,几十秒的中断对业务影响可控。
注意:如果你面对的是金融交易类或者医疗类业务,准不停服可能不够,需要设计双写或者双活的方案。但对于大多数企业内部管理系统、电商后台、SaaS 应用来说,准不停服完全够用。
2.2 网络架构:VPC 是整个迁移的地基
上云第一步不是搬应用,而是规划网络。VPC(Virtual Private Cloud,虚拟私有云)是云上网络的基石,它决定了你的 ECS、数据库、负载均衡等资源怎么互联互通。
在自建机房里,你可能习惯了用物理交换机划分 VLAN 来隔离网络。VPC 和 VLAN 的核心差异在于:VLAN 是二层隔离,VPC 是三层隔离且天然支持跨可用区。VPC 内部可以再划分子网(Subnet),每个子网绑定一个可用区,这样你的应用可以跨可用区部署,获得更高的可用性。
我的规划是这样的:创建一个 VPC,网段选 10.0.0.0/16,然后在里面划分三个子网——一个公网子网放 CLB 和跳板机,一个私网子网放 ECS 应用节点,另一个私网子网放数据库和 Redis。公网子网的路由表指向互联网网关,私网子网的路由表指向 NAT 网关(用于出站访问)。这样应用节点不直接暴露在公网,安全性更高。
2.3 流量入口:CLB 承担了什么角色
CLB(Configurable Load Balancer,可配置负载均衡)是云上的流量入口。在自建机房里,你可能用 Nginx 或者 LVS 来做负载均衡,上云之后 CLB 接管了这个职责。
为什么不用自己搭 Nginx 做负载均衡?因为 CLB 是云平台托管的服务,它自带高可用、自动扩缩容、健康检查、SSL 卸载等功能。你不需要担心 Nginx 单点故障,也不需要自己维护 Keepalived 做 VIP 漂移。CLB 后面可以挂多台 ECS,流量按你配置的权重或者轮询策略分发。
在迁移过程中,CLB 还有一个关键作用:它可以让流量切换变得非常平滑。你先把新的 ECS 节点挂到 CLB 后端,等健康检查通过后,再逐步把流量从旧节点切到新节点。整个过程对用户来说几乎无感知。
2.4 容器编排:单节点 K8s 的局限与应对
单节点 K8s 意味着整个集群只有一个 Master 节点,同时也承担 Worker 的角色。这种架构的优点是部署简单、资源占用少,缺点是没有任何高可用能力——节点一挂,整个集群就不可用。
迁移到云上 ECS 之后,我建议至少把 Master 和 Worker 分开,或者直接使用云平台托管的 K8s 服务。如果预算有限,至少要做到:Master 节点配置稍高一些(4C8G 起步),Worker 节点根据业务负载来定;etcd 数据定期备份到对象存储;关键微服务至少两个副本,利用 K8s 的调度能力分散到不同节点。
实操心得:单节点 K8s 迁移时,最大的坑是 PV(Persistent Volume)的迁移。如果你的微服务用了本地存储或者 NFS,迁移时需要先把数据同步到云上的 NAS 或者云盘,再重新挂载。千万不要直接复制 Pod 的 YAML 就完事,存储卷的配置一定要仔细核对。
3. 核心组件拆解与迁移前的准备工作
3.1 若依微服务的架构梳理
若依(RuoYi)是一套基于 Spring Cloud 的微服务快速开发框架,通常包含以下核心组件:网关(Gateway)、认证中心(Auth)、系统模块(System)、业务模块(Business)、定时任务(Job)、代码生成(Gen)等。每个模块都是一个独立的 Spring Boot 应用,注册到 Nacos 或者 Eureka 做服务发现,配置统一放在配置中心。
迁移之前,我做的第一件事是把所有微服务的依赖关系画出来。哪些服务调用了哪些服务,哪些服务依赖了数据库、Redis、MQ,哪些服务有定时任务,哪些服务是无状态的。这张图直接决定了迁移的顺序和方式。
无状态服务(比如网关、认证中心)迁移最简单,直接在新环境部署,改一下注册中心地址就行。有状态服务(比如依赖本地文件、本地缓存的)需要额外处理数据迁移。定时任务服务需要特别注意,迁移过程中要避免新旧两个环境同时执行定时任务,否则会出现重复执行的问题。
3.2 数据库迁移:不丢数据的核心保障
数据不丢是迁移的底线。若依默认使用 MySQL,迁移方案我推荐用主从复制的方式:先在云上 ECS 部署一个新的 MySQL 实例,配置为旧库的从库,等数据同步追平后,再执行主从切换。
具体步骤是这样的:第一步,在旧库上开启 binlog,创建复制账号;第二步,在云上 MySQL 上执行 CHANGE MASTER TO 指向旧库,启动复制;第三步,观察 Seconds_Behind_Master 指标,等它降到 0 并且稳定一段时间;第四步,在业务低峰期,锁住旧库的写入(FLUSH TABLES WITH READ LOCK),确认从库数据完全同步后,把应用的数据库连接地址切到新库;第五步,解除旧库的锁,完成切换。
注意:切换过程中一定要先停掉所有写入操作,否则会出现数据不一致。如果你用的是云平台的数据库迁移服务(DTS),它可以帮你自动完成增量同步和切换,省去手动操作的麻烦,但核心原理是一样的。
3.3 Redis 和消息队列的迁移策略
Redis 的迁移相对简单,如果数据可以容忍短暂丢失,直接在新环境启动一个空的 Redis,让应用重新预热缓存就行。如果数据不能丢,可以用 Redis 的主从复制或者 RDB/AOF 文件迁移。
消息队列(比如 RabbitMQ 或者 RocketMQ)的迁移要复杂一些。核心问题是:迁移过程中生产者和消费者的连接会断开,消息可能丢失或者重复消费。我的做法是:先在新环境部署好 MQ,然后逐步把消费者切换到新 MQ,等消费者都切完之后,再切换生产者。切换期间,旧 MQ 里的积压消息需要手动处理或者等待消费完毕。
3.4 镜像与配置的准备工作
所有微服务的 Docker 镜像需要提前构建好并推送到云上的镜像仓库。如果你用的是腾讯云,那就是 TCR(Tencent Container Registry);如果用阿里云,就是 ACR。镜像的 tag 一定要规范,建议用 Git commit ID 或者版本号,不要用 latest,否则回滚的时候你会很痛苦。
配置文件方面,K8s 的 ConfigMap 和 Secret 需要根据云上环境重新调整。比如数据库地址、Redis 地址、Nacos 地址这些都要改成云上的内网地址。建议把配置项做成环境变量或者配置中心管理,不要硬编码在镜像里。
4. 实操迁移过程与关键环节实现
4.1 云上 K8s 环境的搭建
如果你选择自建 K8s,可以用 kubeadm 在 ECS 上初始化集群。操作系统建议用 Ubuntu 20.04 或者 CentOS 7.9,Docker 版本用 20.10.x,K8s 版本用 1.24.x(注意 1.24 之后不再默认支持 Docker 作为容器运行时,需要额外安装 cri-dockerd)。
初始化 Master 节点的命令大致如下:
kubeadm init \ --apiserver-advertise-address=10.0.1.10 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --kubernetes-version=v1.24.0初始化完成后,按照提示配置 kubectl 的 kubeconfig,然后安装网络插件(Calico 或者 Flannel)。接着把 Worker 节点 join 进来。如果你用的是云平台托管的 K8s,这一步可以跳过,直接在控制台创建集群即可。
4.2 微服务的部署与注册中心切换
所有微服务通过 Helm Chart 或者 kubectl apply 部署到 K8s 集群。部署顺序很重要:先部署 Nacos 或者 Eureka 作为注册中心,再部署网关和认证中心,最后部署业务模块。
每个微服务的 Deployment 里要配置好 readinessProbe 和 livenessProbe,这样 K8s 才能正确判断 Pod 是否就绪。readinessProbe 建议用 HTTP GET 请求健康检查接口,比如 /actuator/health,初始延迟设 30 秒,间隔 10 秒。
服务注册中心的切换是迁移的关键节点。旧环境的微服务注册在旧 Nacos 上,新环境的微服务注册在新 Nacos 上。在切换瞬间,需要把网关的路由配置指向新 Nacos 的服务列表。这个过程可以通过修改网关的配置中心来实现,不需要重启网关。
4.3 CLB 流量切换的具体操作
CLB 的流量切换是整个迁移过程中最需要谨慎操作的环节。我的做法是分三步走:
第一步,在新环境的 ECS 上部署好所有微服务,确保服务能正常启动并注册到 Nacos。此时 CLB 后端还没有挂任何新节点,用户流量仍然走旧环境。
第二步,把新环境的网关节点挂到 CLB 后端,权重设置为 1(旧节点权重 100)。观察一段时间,确认新环境能正常处理请求,没有报错。
第三步,逐步调高新节点的权重,同时降低旧节点的权重。比如新节点权重调到 10,旧节点降到 90;观察 5 分钟没问题,再调到 50/50;最后调到 100/0,完成流量切换。
实操心得:CLB 的健康检查一定要配置正确。如果健康检查路径返回 200 才算健康,那你的网关必须提供一个返回 200 的健康检查接口。健康检查间隔建议设 5 秒,不健康阈值设 3 次,健康阈值设 2 次。这样即使新节点有问题,CLB 也能快速把它摘掉,不影响用户。
4.4 数据一致性校验与回滚方案
流量切换完成后,不要急着把旧环境关掉。至少观察 24 小时,确认新环境运行稳定、数据写入正常、没有异常日志。
数据一致性校验的方法:对比新旧数据库的关键表记录数、最近更新时间、关键字段的汇总值。如果发现不一致,需要立即排查原因。常见的原因包括:切换瞬间有未同步的 binlog、应用连接池还连着旧库、定时任务在新旧环境同时执行等。
回滚方案必须提前准备好。如果新环境出现严重问题,需要能快速把流量切回旧环境。回滚的前提是旧环境还在运行,数据库还没有被新环境写入大量数据。所以切换后的观察期内,旧环境要保持待命状态,数据库要保留只读或者可写但能快速回滚的能力。
5. 压测验证:用 JMeter 验证云上环境的承载能力
5.1 压测脚本的设计与参数化
迁移完成后,由压测人员使用 JMeter 脚本做高并发测试,验证云上环境的承载能力。压测脚本的设计要贴近真实业务场景,不能只压一个接口。
我的做法是:先用抓包工具或者日志分析,统计出业务高峰期的主要接口调用比例。比如登录接口占 10%,查询接口占 60%,写入接口占 20%,其他接口占 10%。然后按照这个比例设计 JMeter 的线程组和请求分布。
参数化方面,用户账号、密码、查询条件这些都要从 CSV 文件读取,避免所有请求用同一个参数导致缓存命中率过高,压测结果失真。JMeter 的 CSV Data Set Config 组件可以很方便地实现这一点。
5.2 压测执行与监控指标
压测执行时,需要同时监控以下指标:
| 监控维度 | 具体指标 | 工具 |
|---|---|---|
| 应用层 | QPS、响应时间、错误率 | JMeter Aggregate Report |
| 容器层 | CPU、内存、网络 IO | kubectl top pods |
| 节点层 | CPU、内存、磁盘 IO | 云监控控制台 |
| 数据库 | 连接数、慢查询、QPS | MySQL 慢日志、云数据库监控 |
| 中间件 | Redis 命中率、MQ 积压量 | Redis INFO、MQ 控制台 |
压测从低并发开始,逐步增加线程数,观察各项指标的变化。如果响应时间突然飙升或者错误率上升,说明达到了瓶颈,需要停下来分析原因。
5.3 瓶颈分析与调优
压测过程中常见的瓶颈和调优方法:
- CPU 瓶颈:微服务的 JVM 参数需要调整,比如堆内存大小、GC 策略。建议用 G1 GC,堆内存设为容器内存限制的 70% 左右。
- 数据库瓶颈:慢查询需要优化 SQL 和索引;连接数不够需要调整最大连接数和连接池配置。
- 网络瓶颈:CLB 的带宽和连接数需要根据压测结果调整规格。
- Redis 瓶颈:如果 QPS 过高,可以考虑读写分离或者集群模式。
注意:压测环境的数据量和生产环境要尽量一致,否则压测结果没有参考价值。如果生产环境有 100 万条数据,压测环境只有 1 万条,那数据库的查询性能会差很多。
6. 常见问题与排查技巧实录
6.1 迁移后服务注册不上 Nacos
这是最常见的问题之一。排查思路:先看 Pod 的日志,确认 Nacos 地址配置是否正确;再检查网络连通性,在 Pod 里 ping 或者 telnet Nacos 的地址和端口;最后检查 Nacos 的命名空间和分组配置是否一致。
如果 Pod 能访问 Nacos 但注册不上,可能是 Nacos 的鉴权配置问题。若依默认可能没开鉴权,但云上的 Nacos 可能开了,需要在微服务配置里加上用户名和密码。
6.2 CLB 健康检查失败
健康检查失败的原因通常有几种:健康检查路径配置错误、后端服务没有正常启动、安全组没有放行健康检查的源 IP 段。云平台的 CLB 健康检查源 IP 段是固定的,需要在 ECS 的安全组里放行。
另外,如果后端服务返回的是 302 跳转而不是 200,健康检查也会失败。需要确认健康检查接口不经过认证拦截器。
6.3 数据库主从切换后数据不一致
主从切换后如果发现数据不一致,首先检查 binlog 的格式。如果旧库用的是 STATEMENT 格式,某些非确定性函数(比如 NOW()、RAND())可能导致主从不一致。建议用 ROW 格式。
其次检查是否有应用还在连旧库。切换后要确认所有应用的数据库连接地址都已经更新,连接池已经刷新。可以通过在旧库上执行 SHOW PROCESSLIST 来查看还有哪些连接。
6.4 压测时 QPS 上不去
压测时 QPS 上不去,但 CPU 和内存都没跑满,这种情况通常是 JMeter 本身的瓶颈。JMeter 是 Java 应用,单机并发能力有限。可以改用分布式压测,用多台压测机同时施压。
另外,JMeter 的默认堆内存可能不够,需要调整 jmeter.bat 或者 jmeter.sh 里的 HEAP 参数。还有,JMeter 的监听器会消耗大量资源,压测时建议只保留 Aggregate Report,关掉 View Results Tree。
6.5 迁移后定时任务重复执行
这个问题很隐蔽,但影响很大。如果新旧环境同时运行,定时任务会执行两次,可能导致数据重复写入或者业务逻辑错误。
解决方法:在迁移期间,先把旧环境的定时任务停掉,等新环境稳定后再在新环境启用。或者用分布式锁(比如 Redis 锁)来保证同一时间只有一个环境执行定时任务。
6.6 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 服务注册不上 | Nacos 地址错误、网络不通、鉴权失败 | 看日志、telnet 测试 | 修正配置、放行安全组 |
| 健康检查失败 | 路径错误、服务未启动、返回非 200 | 手动 curl 健康检查接口 | 修正路径、调整拦截器 |
| 数据不一致 | binlog 格式、应用连旧库 | 对比数据、SHOW PROCESSLIST | 改 ROW 格式、刷新连接池 |
| QPS 上不去 | JMeter 瓶颈、应用瓶颈 | 看 JMeter 和应用的 CPU | 分布式压测、调优应用 |
| 定时任务重复 | 新旧环境同时运行 | 检查两边日志 | 停旧环境任务、加分布式锁 |
7. 迁移后的收尾与个人体会
迁移完成后,旧环境的资源不要马上释放,至少保留一周。这一周内,新环境可能会暴露出一些在压测中没发现的问题,比如某些边缘业务的接口超时、某些定时任务执行失败、某些报表查询变慢等。保留旧环境可以让你在紧急情况下快速回滚。
监控和告警要配置到位。云监控可以配置 CPU、内存、磁盘、网络等基础指标的告警,应用层可以配置接口响应时间和错误率的告警。告警阈值不要设得太敏感,否则会被大量误报淹没;也不要设得太宽松,否则真出问题了收不到通知。
我在实际操作中的体会是:迁移这件事,技术方案只占三成,剩下七成是沟通和协调。你需要和业务方确认迁移窗口,和压测人员确认测试计划,和运维确认监控配置,和开发确认代码兼容性。任何一个环节沟通不到位,都可能在迁移当晚出问题。
最后分享一个小技巧:迁移前一定要做一次完整的演练。在测试环境把整个迁移流程走一遍,记录每一步的耗时和遇到的问题。演练中暴露的问题越多,正式迁移时就越顺利。我那次迁移演练发现了三个配置错误和一个脚本 bug,正式迁移时一次成功,切换窗口只用了 15 秒。