服务上云那些事

背景和挑战

我们的服务是一款海外短视频产品,日活 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。