服务上云那些事

背景和挑战

我们的服务是一款海外短视频产品,日活 1500 万,印度市场排名第二,是 ** 公司日活最高、数据最好的出圈产品。当时因为业务计划出售,需要最高优先级将后端服务迁移到 AWS 公有云。整个迁移工作面临以下几个挑战:

  • 迁移周期短

  • 迁移和业务增长并行

  • 数据量大、并发高

  • 跨团队、跨部门、跨公司协作
    由于用户规模大、业务场景复杂、数据量大、请求并发高,要在短时间内完成迁移工作,保证服务迁移和业务增长并行,同时保证服务的稳定性,是一个很大的挑战。

迁移方案

原服务架构

上图是原服务架构。承接了 1500 万日活,接入层服务峰值 QPS 136k,首页 Feed 流峰值 QPS 3.8k,使用 10 个MySQL 集群,约 5.7T 数据,5 个 Redis 集群,约 750G 数据,2 个 MongoDB 集群,约 100G 数据,4 个 ES 索引,约 280G 数据,1 个 Hbase 集群,约 180G 数据。

迁移技术评估

迁移涉及到的组件技术选型和难度评估如下表所示。

自建云平台 AWS 迁移技术要点 迁移技术难度
云主机 Amazon EC2 平滑迁移
负载均衡器 Amazon ELB 平滑迁移
MySQL Amazon Aurora 平滑迁移
MongoDB Amazon DocumentDB
MongoDB on EC2
平滑迁移,修改部分代码
平滑迁移,修改部分代码,维护工作大

Redis Amazon ElastiCache for Redis
Redis on EC2
平滑迁移,修改部分代码
平滑迁移,修改部分代码,维护工作大

RocketMQ Amazon SQS 重新技术选型,修改较多代码
Talos AWS Kinesis/Amazon MSK 重新技术选型,修改较多代码
EMQ Amazon SQS 重新技术选型,修改较多代码
Elasticsearch Amazon OpenSearch 平滑迁移,修改部分代码
FDS Amazon S3 重新技术选型,修改较多代码
HBase Amazon EMR HBase on S3
DynamoDB
Hbase on EC2
平滑迁移,修改部分代码
重新技术选型,修改较多代码
平滑迁移,修改部分代码,维护工作大


短信服务 Amazon Pinpoint 重新技术选型,修改部分代码

迁移大原则

1)保稳定

  • 业务低峰期迁移

  • 尽可能使用托管服务,尽量少改代码

  • 迁移前必须通过功能测试和性能测试

  • 多批次小数据量迁移,将每次迁移影响降到最小

2)可回滚

  • 原有环境可以回滚,如果不能则需要有数据实时同步和定时数据备份

  • 提前测试回滚,如果不能则测试备份恢复操作可以成功

3)有预案

  • 迁移前预想到可能导致迁移失败的情况

  • 针对可能导致失败的情况,提前准备好脚本和命令

  • 每个组件正式迁移前,提前演练

总体方案

首先迁移 MySQL、Redis、MongoDB、ES 等数据库,然后改造其它中间件和基础组件,最后使用同一套代码在 AWS 侧部署新服务,通过流量接入层将读写流量逐渐迁移到新服务。

对比行业最佳实践方案

根据行业中最佳实践,通常会采用以下流程所示方案:

如果我们的服务迁移应用这种方案,存在以下问题:
  • 大量三方调用,不是都能支持复制线上流量进行测试

  • 线上问题在最后阶段暴露,排期不可控

  • 业务需求与迁移并行,要维护两个分支

  • 提前部署,将会对基础服务的依赖提前

相较而言,上一种方案没有以上的问题,而且更容易保证数据的一致性。

迁移过程

数据库迁移基本方案

方案 描述 优点 缺点
双写 通过双写保证数据的一致,平滑迁移 可以保证数据一致性;
不停服,用户无感知
开发工作量大,周期长
停服迁移 原数据库停写,数据完全同步后,切换到 AWS 数据库 可以保证数据一致性 停写期间服务可用性受损
直接切换 服务直接切换到 AWS 服务 操作简单 容易导致数据不一致,出现主键冲突等错误,进而导致一些不可预料的问题,风险高

数据库迁移有如上三种方案,其中直接切换无法保证数据的一致性,风险高,通常情况下不会选择。

双写方案预期效果最好,但是开发工作量最大。我们业务使用了 MySQL、MongoDB、Redis、ES、Hbase 等多种数据库,部署项目数量 15+,涉及写数据的逻辑极多,想要实现数据双写,代码改动的工作量非常庞大,代码改动多也意味着潜在风险多、开发周期长。综合考虑开发周期与迁移风险,我们也放弃了这种方案。

最终我们选择停写迁移的方案。停写后,确保增量数据完全同步成功,数据一致性校验通过后,切换为新数据源。同时我们采取了如下措施来减少停写造成的影响:

  • 在业务低峰期进行迁移;

  • 增量数据同步尽可能使用自动化工具,或提前准备好数据迁移和数据校验脚本,缩短停写时间;

  • 提前缩减服务节点,缩短切换数据源时服务部署需要的时间。

我们评估了各个数据库组件低峰期停写对业务造成的影响,确定了可以接受的最大停写时间,经过模拟演练,验证了停写方案的可行性。

MySQL 迁移

迁移方案

通过binlog机制实现原 MySQL 到 Aurora 的数据迁移,RTO:10分钟,RPO:1分钟。具体步骤如下:

  • 正向同步:

1)开启 Aurora binlog 功能,将 Aurora 的写节点设置为原 MySQL 的从节点,通过 binlog 将存量数据同步过来;

2)将只读流量通过 Proxy 切换到 Aurora 集群只读节点;

  • 反向同步:

3)将 MySQL 设置为只读,将 Aurora 写节点设置为主节点,原 MySQL 主节点设置为从节点,实现数据的反向同步,并将写流量通过 Proxy 分发给 Aurora 主节点;

4)将业务代码的写流量指向 Aurora 的主节点 Endpoint,读流量指向 Aurora 的读Endpoint,和 Proxy 剥离;

5)开启Aurora Backtrack功能。

MongoDB 迁移

通过 MongoShake 和 oplog 实现 MongoDB 的数据迁移,RTO:10 分钟,RPO:10 分钟。具体步骤如下:

1)通过 MongoShake 将 MongoDB 的存量数据同步到目标环境;

2)将源 MongoDB 设置为只读权限;

3)将业务流量切换至目标环境的 MongoDB;

4)关闭数据同步;

5)每10分钟做一次备份。

开源版本的 MongoDB 没有开启 PID 功能,如果要实现反向同步必须保证是空数据库同步,故改为离线备份方式。

Redis 迁移

通过 RedisShake 实现 Redis 到 ElastiCache Redis 的数据迁移,通过 ElastiCache 的 Backup 实现小时级的数据反向同步,RTO:10分钟,RPO:10分钟。具体步骤如下:

  • 正向同步

1)通过 RedisShake 将原 Redis 的存量数据同步到目标环境的 ElastiCache Redis 上;

2)将业务流量切换到目标环境的 ElastiCache Redis 上;

3)写脚本实现 ElastiCache Redis 的定期备份,并拷贝到 S3 上( ElastiCache Redis 默认不开启 psync,可以向后台申请开启,但开启后不能保障 SLA,故放弃 psync 反向同步);

  • 反向拷贝

4)原集群及时同步 ElastiCache Redis 的备份,当 ElastiCache Redis 出现问题时可以及时回滚到原集群。

Redis 迁移特殊的一点是没有采用类似 MySQL、MongoDB 等组件采用的停写迁移方案。主要原因是 Redis 停写操作难度较高,需要业务上进行代码改造来实现,代码改动的工作量非常庞大。

直接切换的方案在迁移过程中可能出现老数据覆盖新数据的问题,但考虑到迁移窗口在业务低峰期,Redis 写 QPS 很低,而且 Redis 中大部分数据属于缓存数据,造成的影响有限。所以我们最终选择了直接迁移的方案。

ES 迁移

通过 ElasticSearch 的 Snapshot 功能实现 ElasticSearch 数据迁移。RTO:10 分钟,RPO:20 分钟。具体步骤如下:

1)将原 ElasticSearch 索引的第一次(全量数据)Snapshot 导出到 S3(注册 S3 为 Snapshot 的仓库),并恢复到目标环境的 OpenSearch 上;

2)将原 ElasticSearch 账号设置为只读,将原 ElasticSearch 索引的第二次(增量数据)的 Snapshot 导出到 S3,并恢复到目标环境的 OpenSearch 上;

3)切换写读写流量到目标环境的 OpenSearch上;

4)写 cronjob 实现 20 分钟一次的 snapshot 备份。

注:ElasticSearch 7.7(OpenSearch)版本的 snapshot 无法在 ElasticSearch7.6.2(原集群版本)上恢复,所以采用离线 snapshot 的备份方式,如果 AWS ES 集群出现问题,则在 AWS 新建 另一个 ES 集群恢复数据,这里的风险在于无法会滚到原集群,不过考虑到 ES 数据非核心依赖,业务评估后可以接受这种方案。

Hbase 迁移

通过 HBase 的 snapshot 功能实现 HBase 数据迁移。RTO:30分钟,RPO:10分钟。具体步骤如下:

1)业务侧停写,将原 HBase 表数据通过 snapshot 导出到 HDFS 上,再 distcp 到目标环境 S3 上;

2)将 S3 上的 snapshot 恢复到 EMR HBase 上;

3)将业务流量切换至目标环境的 EMR HBase 上;

4)开启 EMR HBase 集群之间的 replication 功能,基于 WAL 日志实现同步;

5)同时定期对重要表进行 snapshot 备份。

AWS 的 HBase 是 EMR HBase on HDFS 模式,底层是 i3 的 EC2,而 EMR 默认是单 AZ 部署,所以将已有两个 HBase 集群分别部署到不同的 AZ 上,同时对重要的表做了 replication 操作。

服务改造

底层存储数据库迁移完成后,开始对服务中使用的基础组件进行改造,具体措施如下:

自建云平台侧技术组件 改造方式
RocketMQ 替换为 Amazon SQS,步骤如下:
1. 上线 SQS 生产者和消费者,新增数据写入 SQS;
2. 原 MQ 消费者继续消费历史数据;
3. 历史数据消费完成后,下线原 MQ
EMQ 同 RocketMQ
Talos 替换为 Amazon MSK ,步骤和 RocketMQ、EMQ 类似
FDS 替换为 Amazon S3,通过双写完成数据迁移
Thrift 无需改造
Zookeeper 在 Amazon EC2 上自建 Zookeeper
短信服务 替换为 Amazon Pinpoint,由于 Pinpoint 只是发送通道,需要自己实现验证码生成和验证逻辑
日志系统 通过 MSK 和 OpenSearch 自建日志收集系统
中台评论 自建服务,去中台依赖
中台审核 自建服务,去中台依赖

服务改造完成后,在 AWS 侧部署一套服务,通过 Nginx 逐步切换流量,最终完整个服务迁移。

其它技术问题

时区问题

原服务机器服务都是东八区,而 AWS 是 UTC 时区。

解决措施:

  • 整理哪些组件和服务依赖时区

  • EC2 可以设置时区

  • EMR 可以通过 Bootstrap 更改底层 EC2 的时区,Flink/Spark/Hive都默认调EC2的时区

  • Aurora 可以更改 MySQL引擎的时区

  • Redis/MSK/OpenSearch 默认是 UTC 时间,可以在写入数据时通过代码指定时区

binlog 问题

数据组需要用到业务组的 binlog,并写入到数仓中,原来的解决方案依赖自建云平台的中间件和数仓产品,迁移后不可用。

解决方案:

  • 在 MSK 托管的 connector 上引用 Debezium

  • 将 binlog 日志导入到 MSK 里的 Topic 里

  • 下游 EMR Flink 消费,通过 Flink Kudu 的 connector 写入到数仓中

CI/CD 问题

迁移前使用自建云平台的 CI/CD 平台,迁移后需要有另一套 CI/CD 平台方案,并与 Autoscaling Group 结合。

解决方案:使用Jenkins,AWS CodeDeploy,Autoscaling Group实现

具体步骤:

  • 使用 Jenkins 实现 Gitlab 对接

  • 基于 Jenkins worker 节点实现代码编译,不使用 CodeBuild 的原因是原服务有使用修改内核后的 CentOS 编译 C++ 代码的场景,而 CodeBuild 默认没有 CentOS,为了避免使用两套技术栈所以采用了自建 Jenkins worker 节点的方式编译代码

  • 基于 HTTP Plugin 实现和 S3 的交互,将编译好的包上传到 S3

  • 使用 Jenkins 的 Plugin 调 CodeDeploy 部署代码到相应的 Autoscaling Group EC2 上,当 S3 代码出现变化,自动触发 CD 流程

总结和收获

托管服务的优势

迁移前 迁移后
安全合规 需要内部法务和安全专家确定,时间周期长 无需考虑,托管服务有各种合规标准
运维工作 需要跨部门支持,耗费大量人工和时间 3 个 SRE 维护整个架构,如有 case AWS 会及时响应
系统可用性 自建云平台无准确评估 托管服务都有 SLA 和最佳实践
性能 基于开源自建 托管服务性能和开源一致,Aurora 性能高于开源
创新 需求需要层层传递,耗费大量人工和沟通时间,且需求较难实现 利用 AWS 托管服务可以随时验证想法,如可以利用托管 AI 服务 Transcribe 进行很多测试
成本 IDC 用法,需要预置资源,浪费较多 大部分托管服务都有弹性,可以随时调整

迁移的心得

历经4月,最终顺利完成了整个迁移工作,我们总结了迁移的几个关键点。
首先是技术能力方面:

  • 梳理当前架构/设计合理的架构:服务迁移的前提是完全了解服务架构和实现细节;

  • 迁移方案确认:根据实际情况制定合理方案,是服务迁移的关键;

  • 功能测试/性能测试:迁移过程中每个环节都要经过严格的测试和演练,是迁移的基础;

  • 最佳实践:充分了解各组件的最佳实践并合理应用,才能提升效率并保证迁移后服务的稳定性。

然后是项目管理方面:

  • 切分可以并行的业务模块,提升迁移效率;

  • 把控迁移节奏,及时发现风险点;

  • 集中时间和资源解决 block issue。