服务上云那些事
背景和挑战
我们的服务是一款海外短视频产品,日活 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 写 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。