AWS→GCP:双活架构下,存量用户App和长连接设备如何平滑迁移
引言承接《AWS→GCP我准备好了MQ双写却发现外网不通》上一篇我在网关前置转发服务配合设备在新环境上线时的 Redis 会话标记解决了跨环境的指令链路问题——用户 App 在 GCP设备在 AWSApp 能正常下发指令。链路用户 AppGCP→ 前置转发 → 读取 Redis 会话标记 → 设备归属判断 → AWS 网关 → 设备这一篇的目标把所有存量的用户 App 和长连接设备从 AWS 平稳地迁到 GCP。一、迁移策略核心思路先切用户 App再切设备分步推进。整个迁移过程按以下顺序推进自测验证链路用内部设备验证 AppGCP→ 设备GCP的完整链路确认流程没问题迁移部分用户 App 到 GCP选择小部分用户将 App 流量切到 GCP。此时设备仍全部留在 AWS依赖前置转发保障指令下发迁移所有用户 App 到 GCP确认部分用户切流无问题后将全部用户 App 流量切到 GCP迁移部分设备到 GCP选择少量设备切到 GCP 网关观察运行状态逐批迁移设备到 GCP按批次扩大范围逐步将设备从 AWS 迁到 GCP全量设备迁至 GCP所有设备完成迁移前置转发下线AWS 旧网关关停二、自测验证链路用户在新环境设备在旧环境没发现问题。新环境 APP 配网绑定设备用户和设备都在新环境没发现问题。用户在新环境设备在旧环境设备迁移时发现设备固化域名解析IP问题部分型号设备未按规范在每次重连时重新解析域名只在配网阶段解析一次之后直接将IP缓存到本地。这意味着迁移后即使DNS已指向新网关设备仍会连接旧网关。问题可控。六年前设计协议时我预留了一个重置连接地址的指令——原本是为设备更换服务器场景准备的迁移时正好用上通过下发新网关地址让设备重新发起连接并重新解析域名。本次迁移切流计划通过该协议分批完成逐步下发新地址避免大量设备同时切换导致新网关涌进过多重连请求。三、用户App 的平滑切换3.1 灰度切流通过Nginx负载均衡迁移部分用户通过修改 AWS 网关 Nginx 配置将 GCP 转发服务 IP 加入负载均衡实现部分用户 App 流量迁移到 GCP。该方式在发现问题后可以立刻回滚风险可控。3.2 观察期间发现的异常设备意外下线灰度过程中发现有设备意外下线。经排查是攻击者在利用设备的洲际上行链路进行攻击。攻击路径还原攻击者逆向协议根据设备 UID 生成规律批量推导出大量设备 UID攻击者在中国节点网关用这些 UID 模拟设备发起登录请求部分设备归属地不是中国中国网关通过本地调度服务将登录请求中转到数据中心数据中心通过跨境寻址将请求转发到设备归属地网关跨境链路走 HTTP3.3 为什么攻击能打到 GCP 环境配网时平台只会下发用户归属区域的网关地址给设备。理论上设备洲际链路是被封存的保留相关代码只是为了后续单区域多节点架构升级。但因为这一点GCP 环境的转发服务没有拦截洲际链路相关的接口导致 GCP 转发服务收到设备跨洲上行请求时直接放行本地处理。3.4 攻击的实际影响第一条链路攻击者模拟设备在中国登录攻击者用推导出的真实 UID在中国节点网关模拟设备发起登录请求但没有合法凭证。欧洲 GCP 本地设备服务校验失败后触发跨洲通知设备下线。由于真实设备在欧洲上线此次登录没有成功上线区域不会同步更新到数据中心。数据中心寻址把下线通知转发回欧洲网关欧洲网关因没有缓存 Netty 产生的对应唯一 session直接空处理——日志记录了但没执行任何操作。这条链路问题不大。第二条链路异常下线回调导致的误下线攻击者模拟设备连接中国网关登录超时异常下线后触发下线回调走洲际链路来到 GCP 转发服务。GCP 本地没有该设备的 session 缓存但老代码逻辑没有加空判断——正常单活架构下本地是有缓存的这个漏洞是因为没有考虑到攻击者会从跨洲链路打进来。由于这是白名单安全内部通道设备服务直接下线成功。这才是导致设备意外下线的根本原因。3.5 解决方式暂时采取怀柔手段GCP 转发服务把设备上行洲际链路的接口也加入拦截转发到 AWS 环境处理规避设备下线问题。这不是妥协是策略。如果贸然用封禁 IP 等激烈手段会激起攻击者变换攻击方式得不偿失。黑客的攻击并不高明。之所以没有特别关注是因为合作公司之前做的设备跑着跑着凭证会空导致的现象和攻击者造成的现象比较相似——误报和真实攻击混在一起很难区分。在想到妥善的处理方式之前初步打算短时间内通过关小黑屋的方式配合黑客演戏对于凭证错误的设备开辟一个独立的模块处理按之前的方式回应设备让攻击者以为链路仍然通畅但实际已被隔离。3.6 用户 App 切流成功灰度切流完成后用户 App 流量已全部指向 GCP。此前因攻击导致的设备意外下线现象在怀柔手段生效后已不再出现。切流期间未影响正常用户使用未收到相关投诉。至此用户 App 切流阶段完成。对攻防知识感兴趣的读者可以参考另一系列文章《7年打磨全球宠物IoT平台》专栏。四、存量长连接设备的逐步迁移4.1 部分设备迁移选择部分设备下发重置连接指令迁移到 GCP。迁移后观察设备在新环境正常上线但后续发现部分设备状态异常排查发现部分设备重连时没有正常关闭旧连接。AWS 网关 Netty 触发 read idle 后关闭回调到 AWS 设备服务处理经对比缓存中的 session 一致执行了下线操作把在线状态改为离线。另外发现有 2 个型号的设备下行链路跑不通。经排查合作公司未按协议规范处理——重置连接地址时端口没有使用平台下发的值而是沿用了合作的第一款设备的默认端口导致设备上线后被平台识别为另一款设备。影响下行链路App 控制指令无法正常下发到设备上行链路设备主动上报的数据仍能被正常处理与该合作公司各型号设备的上报逻辑差异不大影响较小。4.2 解决方式针对迁移过程中发现的两个问题分别做了对应处理1. 解决设备在线状态被误下线的问题修改 GCP 设备服务的会话处理逻辑采用 Redis 会话缓存双写策略。当设备在新环境上线时设备服务将缓存同时写入 GCP 和 AWS 两边的 Redis确保旧环境在触发 read idle 回调时能正确识别设备已经重新上线避免误下线。2. 解决下行指令无法到达特定型号设备的问题修改 GCP 设备服务的指令处理模块下发指令时根据设备上线时平台识别到的型号进行路由而不是依赖出厂时入库的型号识别。确保 App 控制指令能正确下发到对应型号的设备。4.3 设备逐步迁移完成设备服务修改完成并平滑发布到 GCP 环境后经验证手段已生效。继续逐批设备下发重置指令未再出现因攻击或会话缓存不一致导致的设备异常下线。五、迁移完成后的验证与清理迁移完成后AWS 环境日志中未产生用户和设备的新日志确认流量已全部切换至 GCP。通过 GCP 设备服务预留的关闭 Redis 双写接口停止双写操作。随后进行数据库迁移操作参考 《AWS→GCP迁移中最容易翻车的一步——数据库连接池切换》。AWS 数据清理完成后正式停机。六、总结采用前置转发的方式过渡用户和设备割裂问题十分考验团队对平台架构的熟悉程度和操作手法出问题时排查难度很大。如果采用上一篇讨论的 MQ 和 Redis 双写来过渡就不会出现洲际链路被攻击、AWS 网关 Netty 触发 read idle 回调下线的问题。但由于外网 MQ 不可达加上三周内完成欧美双节点迁移的硬性约束前置转发是当时唯一可行的选择。关于攻击应对的思考这次洲际链路攻击我没有选择封 IP 或直接拦截而是用怀柔手段配合“演戏”——因为协议和设备 UID 已经被逆向推导出来了存量设备无法在短时间内完成升级改变交互方式贸然反击只会让攻击者变换攻击方式得不偿失。之前验证码被盗刷时我犯过这个错误用简单的图像验证码拦截脚本上线不到 1 天就被攻破攻击者很快加上了自动识别能力。后来换了腾讯云的验证码服务才解决。在面对黑客的时候没有彻底解决的方案不要轻易下手。 先稳住争取时间找到根因再想办法收网。