ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单节点K8s若依微服务准不停服迁移阿里云ECS实战

单节点K8s若依微服务准不停服迁移阿里云ECS实战 最近刚完成一次比较有代表性的迁移把一套跑在单节点 k8s 上的若依微服务整套环境准不停服、不丢数据地迁到了阿里云 ECS。迁移完以后压测同事用配套的 jmeter 脚本做了高并发测试验证云上环境的承载能力。整个过程走下来等于把灰度发布、蓝绿部署、双活架构、数据同步、数据迁移这几个词完整实战了一遍。说实话这几个概念在架构文章里经常出现但真正落到“怎么把正在跑的业务搬到另一个环境用户还没什么感知”这种场景时每一环都会冒出一堆细节问题。这篇就把我从方案设计到最终压测通过的完整思路、操作步骤和踩坑记录整理出来给正准备做云迁移或者想系统梳理发布策略与数据同步方案的读者一个参考样本。1. 这次迁移的整体思路与架构选型1.1 先摸家底单节点 k8s 上的若依微服务都有什么若依RuoYi微服务版是一套很典型的 Spring Cloud Alibaba 脚手架项目里常见模块包括 Nacos注册与配置中心、Gateway 网关、System 系统模块、Auth 认证模块、Job 定时任务模块前端是 Vue后端用 Spring Boot。这种架构在生产环境里跑起来以后依赖的东西其实相当多MySQL 存业务数据Redis 存 Token 和缓存MinIO 或 OSS 存文件Nacos 里还有一堆配置和命名空间。难点不在应用本身而在“单节点 k8s”这几个字。这意味着整套环境所有组件都挤在一台机器上的 k8s 集群里没有第二个节点可以做滚动也没有现成的外部负载均衡来帮你分流。你想在新环境把服务拉起来源端还不能停因为一旦停掉整个集群用户就全部感知到了。这种环境在中小团队里非常常见。很多人觉得测试或预发环境迁到云上很简单但真实生产环境里还有定时任务、消息队列、文件存储这些“隐藏依赖”它们不像 HTTP 接口那样容易在压测里体现却恰恰是数据一致性最容易出问题的地方。所以迁移的第一步一定是把家底盘清楚把所有依赖列出来而不是上来就开搞。1.2 为什么把发布策略和数据策略放在一起设计很多人会把灰度发布、蓝绿部署、双活架构归到“发布策略”把数据同步、备份、迁移归到“数据运维”两者分开看。但迁移场景里这两件事是强绑定的。发布策略解决的是“流量往哪走”旧环境先继续服务新环境拉起来以后把流量按比例切过去出了问题再切回来。数据策略解决的是“数据怎么保持一致”流量可以随时切但数据库里的数据不能分两半否则新旧环境看到的数据都不一样后续根本没法回滚。所以整体设计思路可以串成一条线先备份再同步后切换再压测。备份是为了兜底同步是为切换铺路切换是灰度发布思路在迁移场景里的体现压测则是验证新环境是不是真的能接住流量。当时我们选型的时候也讨论过要不要做真正的双活后来认定在迁移场景里不需要做完整双活只需要把新旧环境临时组成一个“准双活”状态两边同时在线但数据层是主从关系流量只从旧环境逐步往新环境切。这样既享受了双活架构里“随时可切换”的好处又不用承担双写的复杂度。这个判断后来证明是正确的因为数据层双写一旦做不好坑比收益大得多。2. 灰度发布与蓝绿部署应用层如何做到准不停服2.1 两者的本质区别与选型逻辑先聊清楚这两个概念因为迁移切换时一定会用到其中一个。蓝绿部署是准备两套完整的环境一套叫 Blue当前服务一套叫 Green新版本。发布时把所有流量一次性从 Blue 切到 Green如果出问题再一次性切回来。它的优点是回滚极快切换就是一个负载均衡或路由层面的动作缺点是资源占用翻倍而且 Green 环境在切换前必须和 Blue 环境在数据层保持一致否则一切流量用户状态就丢了。灰度发布则是在同一套环境里同时跑新旧两个版本通过负载均衡或网关把一小部分流量导到新版本上逐步验证没问题后再扩大比例直到全部切换。它不要求环境成对存在资源占用更小但回滚相对复杂因为旧版本和新版本会同时在线一段时间必须保证数据层两边兼容而且接口行为不能有破坏性差异。维度蓝绿部署灰度发布环境数量两套完整环境一套环境内多版本共存切换粒度整个环境整体切按比例/按用户特征切回滚速度极快路由层切回即可相对慢需调权重资源成本高双份资源低额外副本即可数据一致性要求切换前数据层强一致新旧版本共享数据层典型场景迁移、大版本发布功能验证、A/B 试验在迁移场景里我推荐“蓝绿打底 灰度放量”的组合思路。新环境整个拉起来当作 Green 环境这符合蓝绿的思想但对用户流量的切换不要一把梭而是按 10%、30%、50%、100% 的节奏逐步放量这就是灰度的手段。两者结合兼顾了切换的稳定性和回滚的灵活性实测下来比单纯的“停机拷贝再启动”靠谱得多。2.2 在单节点 k8s 上的流量切换实现既然源端是单节点 k8s目标端大概率也是单节点 k8s 或者简化版的 ECS k8s 环境。这种环境下 ingress-nginx 是最常用的流量入口它原生支持 canary 发布做灰度切流非常方便。我当时的做法是在目标环境用同一个应用名部署两个 Deployment分别对应 v1旧版本和 v2新版本它们的 Pod 标签有所不同然后创建一个 Service 指向 v2 版本。通过 ingress-nginx 的 canary 注解控制流量权重从 0 开始逐步增大apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ruoyi-gateway-canary namespace: ruoyi annotations: nginx.ingress.kubernetes.io/canary: true nginx.ingress.kubernetes.io/canary-weight: 10 spec: rules: - host: ruoyi.example.com http: paths: - path: / pathType: Prefix backend: service: name: ruoyi-gateway-v2 port: number: 8080canary-weight 的值就是新版本拿到的流量百分比。把它从 10 改成 30、50、100k8s 会自动重新加载 ingress 配置不需要重启任何服务。如果想更精细一点还可以用 canary-by-header 或 canary-by-cookie 做基于请求头的灰度比如让测试人员的请求带上固定 header 直接落到 v2其他用户保持 v1这样既能验证功能又不影响真实用户。有一点需要注意单节点 k8s 上如果没用云厂商负载均衡ingress-nginx 的访问入口通常是 NodePort。不同节点、不同网络环境下 NodePort 的转发链路可能不太一样切换前先在新环境里用 curl 带 Host 头访问一下 NodePort 地址确认 ingress 规则生效再开始切生产流量能省掉很多排查时间。2.3 注意事项别只顾着切流量流量能切过去不代表业务就能正常跑。我在这次实践中踩了几个坑值得单独列出来。第一新旧版本的代码必须兼容旧的数据表结构。灰度期间新旧 Pod 同时在线如果新版本代码里有数据库 DDL 修改比如新增字段或改名旧版本还在跑的老代码可能直接就报错了。建议所有表结构变更都做成向前兼容的先加字段再发代码最后再清理废弃字段而不是大版本一次性改到位。第二定时任务防双跑。若依自带 Quartz 定时任务模块你要是把整套环境复制到新环境新旧两边的 Job 同时启动就会出现重复执行的问题比如重复发短信、重复跑批。要么在配置中心里加一个“是否启用任务”的总开关切换前关掉旧环境的任务要么用分布式锁保证同一时刻只有一个节点执行。第三Token 和 Session 要共享。若依微服务的登录态一般存在 Redis 里如果新旧环境各用一套 Redis用户流量切过去以后 Token 根本查不到表现就是“莫名其妙被踢下线”。迁移期间最好让新旧环境连同一个 Redis或者把 Redis 也做成主从同步保证登录态两边都能识别。第四链路跟踪要提前做。新旧环境流量并存的时候如果没在日志里加 traceId出问题很难定位是旧链路还是新链路出的问题。我建议在网关层统一生成 traceId 并在日志里输出这样灰度阶段随便拿一个请求 ID 就能查完整链路。3. 双活架构与数据同步/备份/迁移数据层怎么保证不丢3.1 双活架构没你想得那么复杂双活架构这个词听起来很高大上但核心思想其实很朴素两套环境都对外提供服务任何一套挂了另一套能直接接管流量用户无感知。真正让双活变复杂的从来不是应用层而是数据层。应用层做双活很容易只要服务无状态前面挂个负载均衡多部署几个副本就行。难的是数据层两边的数据库必须实时同步而且不能丢数据、不能产生冲突。这也是为什么很多双活架构最终做成了“同城主备”或者“读写分离”而不是严格意义上的双写。在迁移场景里我们不需要长期维持双活只需要在切换窗口前后实现一个“准双活状态”旧环境继续服务老流量新环境的数据通过同步通道保持最新两边差距控制在秒级以内。这样就算用户流量切到新环境数据也是完整的不会出现旧环境有这条记录、新环境没有的情况。我比较推荐用“无状态应用 有状态数据主从”的方式来理解这个阶段。应用层随便怎么部署都行数据层必须有一条清晰的主从关系源端 MySQL 是主目标端 MySQL 是从同步追平以后再考虑切换。3.2 备份、同步、迁移别混为一谈很多初学者容易把数据备份、数据同步、数据迁移混在一起实际上这三件事目的完全不同用的工具也不同。数据备份是为了“万一出问题能恢复”它强调的是完整性和时间点的一致性。典型工具是 mysqldump、xtrabackup、云厂商的快照。备份是迁移的兜底迁移前必须做一次全量备份但备份本身不能当同步用因为它是静态的恢复只能恢复到某个时间点。数据同步是为了“两个环境的数据保持一致”它强调的是实时性和持续性。典型工具是 MySQL 主从复制、binlog 订阅、云厂商的 DRS/DTS 这类数据同步通道服务。同步可以是持续性的任务源端有变化目标端马上跟进这是迁移不丢数据的关键。数据迁移是为了“把数据从 A 环境搬到 B 环境”它强调的是最终结果也就是迁完之后 B 环境能接替 A 环境。迁移过程往往是先做一次全量搬迁再通过增量同步追赶业务产生的数据最后在业务低峰期做切换。结构迁移可能还会用到 UGO 这类偏数据库评估和 SQL 转换的工具尤其当源库和目标库类型不一样的时候对象的语法转换比想象中麻烦得多。类型目的典型工具关键指标数据备份灾难恢复、兜底mysqldump、xtrabackup、快照完整性、RPO/RTO数据同步保持两端数据一致MySQL 主从、binlog 订阅、DRS/DTS实时性、延迟数据迁移环境替换DRS/DTS、UGO、mysqldump同步一致性、停机时间3.3 不丢数据的核心原理全量加增量位点续传想做到准不停服、不丢数据核心方法就是“全量 增量”。用搬家来类比你要从旧房子搬到新房子不可能先把所有家具运过去然后人停在那里一直不动。更合理的做法是先把大件家具搬过去全量然后每天新增的东西、临时买的小物件通过一辆持续往返的货车不断送到新房子增量直到搬家当天人直接走过去再把门锁换掉。数据库迁移里的“全量”就是把源库当前所有数据导到目标库“增量”则是通过订阅源库的 binlog 变化把全量之后新增的、修改的、删除的数据继续同步到目标库。binlog 会记录数据库的所有变更并且每个变更都有一个位点信息通常是 binlog 文件名加 position 偏移量。同步工具记下这个位点之后就能做到“从某个位置继续同步”这就是增量同步能持续进行的原理。实际操作中增量同步追平以后目标库和源库还是会有一个非常小的延迟因为 binlog 传输和 SQL 回放需要时间。切换窗口要做的就是在业务低峰期等这个延迟归零然后立刻把写流量切到新环境。严格来说这个切换瞬间可能还会有一点残留的同步数据在途所以需要在源端保持一段时间只读状态来收尾确认同步完全追平后再关闭同步通道。这也是为什么我说“准不停服”而不是“完全不停服”——通常只需要在最终确认那一刻对数据库加个只读锁业务层面用户基本无感。4. 单节点 k8s 若依微服务迁移到阿里云 ECS 实操记录4.1 迁移前准备把家底盘清楚迁移准备工作越细后面切换越顺。我整理了一份清单按这份清单挨个核对基本不会漏容器镜像源端 k8s 上所有 Deployment 使用的镜像版本先列出来确认哪些是新环境要用的MySQL 数据业务库、配置库、若依自带的 quartz 库全都要迁移Redis 数据登录 Token、缓存、验证码等虽然不是持久化核心数据但不迁用户会被踢下线文件存储MinIO 的 bucket 或者对象存储里的上传文件备份、附件、导出文件都在这类存储里Nacos 配置命名空间、配置列表、服务列表每个服务的配置都要核对定时任务若依的 Quartz Job 有哪些迁移后是否要调整周期域名和证书用户访问入口的域名解析、HTTPS 证书准备好切换动作的方案。我在源端先把每个命名空间下的资源导出一份清单用 kubectl 查看所有 Deployment 的镜像kubectl get deployment -n ruoyi -o jsonpath{range .items[]}{.metadata.name}{\t}{.spec.template.spec.containers[].image}{\n}{end}这样一次就能看到所有服务对应的镜像版本比一个个翻控制台高效得多。迁移前必须做一次完整备份这是底线。MySQL 用 mysqldump 导出一份全量 SQLRedis 用 BGSAVE 生成 RDB 文件MinIO 直接对目录做 rsync 同步到本地中转。备份的目的不是让你迁移时用它而是万一迁移过程中出现不可控的问题你有退路可以把数据恢复到迁移前状态。4.2 准不停服迁移的完整步骤下面是我这次迁移的完整步骤可以当成一个模板直接参考。第一步目标环境准备。在阿里云 ECS 上先装好 k8s确保版本和源端一致或者兼容。单节点集群没有高可用但作为迁移目标环境足够了。提前建好命名空间、PV/PVC数据库和 Redis 如果准备用云产品版本比如 RDS 和 Redis 云实例就先把实例创建出来从底层省掉自己维护组件的成本。第二步镜像传输。这步最耗时也最容易踩坑。在源端把所有需要的镜像 docker save 打包压缩后传到 ECS再 docker load 导入。如果镜像不多还好若依这种几十个服务的小型微服务镜像打包压缩后也有几个 GB传输时间要预留充分。如果你有私有镜像仓库直接把镜像推上去会更快新环境从仓库拉取即可。第三步数据迁移。这是整个迁移的核心。我建议优先用专业的迁移工具比如 DRS/DTS 这类通道服务它能帮你处理全量加增量整个生命周期先在源库和目标库之间建立迁移任务先做全量迁移再进入增量同步阶段界面上能看到同步延迟时间。如果不用云工具手动搭 MySQL 主从也一样只是 binlog 位点管理更麻烦要自己记录文件和 position。第四步配置同步。Nacos 里的配置经常被忽略。若依微服务里很多配置是写在 Nacos 上的包括数据库连接、Redis 地址、各种开关。直接在旧环境的 Nacos 控制台把配置导出再导入到新环境然后逐个命名空间核对。尤其要注意数据库地址这种配置新环境里的连接串必须指向新环境自己的数据库否则就会出现“应用起来了但连的还是旧库”的经典事故。第五步应用启动。在目标环境把整套应用拉起来确保服务都注册到新环境的 Nacos。此时源端继续服务老流量两端应用同时在线。我会在这个阶段做一轮功能冒烟测试登录、查询列表、上传文件、触发一个定时任务确认核心链路在新环境是通的。第六步流量切换。把域名解析或者 ingress 入口切到新环境按 10%、30%、50%、100% 逐步放量。切换期间密切观察新环境的日志和监控一旦出现异常立刻把流量切回源端。我这次实际切换耗时大约 20 分钟真正对用户有影响的时间几乎为零只有最后一步数据收尾时把源库设为只读那几十秒内写请求会有短暂排队。第七步校验收尾。确认同步延迟归零、数据校验通过后关闭增量同步通道源端恢复可写但不再接生产流量。观察新环境稳定运行一段时间后再逐步销毁旧环境资源。4.3 迁移后的 jmeter 高并发压测验证云上承载能力迁移完成只是第一步业务方真正关心的是新环境能不能扛住生产压力。我们这次是由压测人员peseman用配套的 jmeter 脚本做高并发测试我作为环境方负责配合、盯监控、处理压测过程中暴露的问题。jmeter 脚本里一般会包含几类典型的业务请求登录获取 Token、列表查询、详情查询、提交写入操作。压测参数上我建议先用低并发预热比如 50 个线程跑 2 分钟让 JIT 编译热点代码连接池和缓存也逐步热起来然后阶梯式加压到目标值比如 500、800、1000 并发。如果一上来就 1000 并发压很多问题会被掩盖系统直接被打懵反而看不出真实瓶颈。压测期间重点关注的指标包括TPS每秒事务数、平均响应时间和 TP99、错误率、应用容器的 CPU 和内存、JVM GC 频率、数据库连接数、慢 SQL 数和 Redis 命中率。压测发现 TPS 上不去先看是不是数据库连接池被打满再看应用线程池是否排队最后看 GC 是否频繁。之前就有一次压测一上来接口全部超时排查下来是数据库连接池配置太小大量线程在等连接根本不是服务没性能。压测数据要注意隔离最好用一个专门的测试账号和测试数据源不要直接在真实用户数据上做写入压测。压测完记得清理测试产生的数据不然会把生产数据搞得一团糟。5. 常见问题与避坑经验5.1 迁移和切换中的典型问题速查问题现象可能原因解决办法切换后新环境登录全部掉线Redis 没有同步或新旧环境各用一套 Redis迁移期共享 Redis 或做 Redis 主从同步增量同步延迟一直不归零大事务、DDL 操作阻塞 binlog 解析低峰期切窗口拆分大事务调高同步规格新环境部分服务起不来Nacos 配置里数据库或 Redis 地址还是旧 IP导出配置后逐个核对先启动依赖组件定时任务两边都在跑新老环境同时启用 Quartz加总开关或分布式锁切换后关闭旧环境任务压测 TPS 上不去接口大量超时数据库连接池太小、线程池排队调大连接池、合理配置线程池、观察 GC文件上传后新环境访问不到MinIO/OSS 里的文件没有迁移用 rsync 或 OSS 跨域复制把文件同步过去5.2 几条建议直接抄的避坑心得第一先切读流量再切写流量。如果业务允许可以把查询类的请求先切到新环境写入请求留在旧环境观察一段时间确认新环境读链路没问题再切换写流量。这样做风险最小因为读操作即使出问题也只是用户访问异常不会产生脏数据。第二数据校验不能只看行数。迁移完以后你以为数据不少但某张表可能只是行数一样内容却对不上。建议在切换前做一次关键表的校验比如用 checksum 或者对关键字段做聚合求和对比新旧环境的计算结果。尤其要注意那些经常更新、删除的表行数一致不代表数据一致。第三迁移窗口预留的时间要足够。很多人计划 2 小时搞定迁移结果全量传输就花了 1 个半小时增量同步又因为业务高峰一直追不平最后只能带着风险强切。我一般会预留计划时间的 1.5 倍宁可早做完早收工也不要赶时间在数据还没完全同步的时候强行切换。第四条件允许时先做一次预演迁移。找一个业务低峰期按完整流程走一遍把全量备份恢复、增量同步、应用启动、流量切换都试一次。预演的价值不只是验证方案可行更关键的是暴露那些文档里没写的隐性依赖比如某个服务启动时依赖某个文件路径或者某个配置项在旧环境是手写进容器里的。走完一次预演正式切换就会从容很多。第五旧环境至少保留一周别急着销毁。切换后即使一切正常还是可能会发现某些历史数据需要回源查证或者某个业务方来说“我们还有一批数据在旧环境里没导出”。一周内旧环境都是你的后悔药。等到业务方确认新环境稳定了再清理旧环境资源也不迟。第六压测之前要确认压测流量不会打到源端。如果域名解析切到了新环境压测脚本里用的还是老域名那所有压力都会落在旧环境上压了半天测的不是你要验证的目标。压测前先确认脚本里的服务地址、端口、协议都是新环境的并且用测试账号在压测环境先跑一个单线程脚本验证链路通。还有一个小细节迁移期间的新旧环境如果在同一个内网或者通过公网互联务必给数据库同步通道做好安全限制只放行必要的 IP 和端口。数据链路加密、账号权限最小化这些安全习惯在迁移这种高强度运维操作里尤其不能省。最后再分享一点个人体会整个过程走下来我最深的感受是灰度发布、蓝绿部署、双活架构这些词听起来都很高大上但在迁移场景里它们本质上是同一个问题的不同解法——就是尽量让用户感觉不到你在折腾。而数据同步、备份、迁移则是另一个层面的问题无论你怎么折腾数据都不能丢。应用层的流量切换做得再花哨数据层一旦有偏差所有的工作都会变成白费。这次迁移里最值的一步是在正式切换前先做了一次完整的预演。预演时我们提前把全量备份恢复到 ECS 上建立好增量同步走了一遍应用启动和冒烟测试。真到正式切换窗口时因为前面所有步骤都验证过一遍全量加增量的追赶时间非常短最后几十秒内就完成了最终切换。如果你近期也有类似的上云或迁机计划我强烈建议你也先做一次预演——这比临时看再多的排查文档都管用。
返回列表