大数据集群迁移这件事,不知道有多少同学做过。我说的不是把一个集群的数据备份到另一个集群上。我指的是整个数据平台与大数据相关的所有集群及业务的迁移工作,从一个机房到另一个机房。
具体范围可能包括:从离线计算集群到实时计算集群;从存储,计算组件,到作业调度,开发平台服务;从底层数据同步到上层业务迁移。要求:不能影响业务,几乎没有停服预算。。。
这事,这要求,但凡玩过大数据平台的同学,应该都不难想象,一定是个吃力不讨好,还要背故障的苦差事。不幸的是,这样的苦差事,出于各种各样的原因,最近两年来,我们一共干了三次。。。
所以,这篇文章,我打算和大家一起讨论分享一下那些不堪回首的岁月里,我们迁移过的集群。以备哪一天,万一,你也摊上事了,也好有所准备,看看有那些工作需要做的,尽量不要死得太过悲惨。
都有哪些麻烦事
要说这事有多苦,那得从解放前的那个晚上。。。哦,不,底层的环境开始向上说起。
集群和机房外部环境问题
历数三次搬迁工作,一次是同城机房搬迁,另外两次是异地机房搬迁。其中一次同城和一次异地,机房间有10-20Gb不等的专线带宽可供使用;另外一次异地搬迁,机房间只有大概1Gb的公网带宽。。。
需要搬迁的集群的整体规模,大概在200到400个节点之间,所涉及到的集群类型,包括,HDFS/HBase等存储集群,以及Yarn/Storm/Spark等计算集群
数据量呢,具备专线环境的这两次搬迁,基本上集群上的历史全量数据按单拷贝来算大概2-3个PB,占用HDFS集群容量6-7个P左右,而集群每日的数据增量规模大概在十几到几十个TB的样子。
可以粗略估算一下,如果全部历史数据走网络传输,纯粹的拷贝传输动作,平均跑满10Gb带宽的话,大概要花费二十几天的时间才能拷贝完成2P的数据,如果这期间数据拷贝出了问题。。。而做一次单日的增量拷贝动作,根据数据量变化大小的不同,大概也要花费3-8个小时的时间。
那么如何在可接受的时间和空间资源内,及时,正确的完成数据的同步工作呢?
平台自身组件和服务依赖问题
讲完集群外部环境问题,接下来看看开发平台自身的组件和服务依赖。
我司的大数据开发平台,自身组件众多,和外部系统也有着千丝万缕的关联。
以离线批处理业务流程为例:
首先,会有数据采集系统负责从各种外部数据源比如,日志,DB,消息队列中以全量或增量的方式将数据采集到集群中来,
其次会有调度系统将各种不同类型的作业分发到不同的Worker和集群上去执行,而作业的来源,包括周期性调度的作业,也包括从开发平台上发起的临时作业,还包括通过我们对外提供的服务接口,由外部业务系统通过程序自动触发的周期或一次性作业
然后,会有数据交换系统将数据导出到其它各个目标数据源中,比如报表DB,各种业务DB,HBase集群,ES集群等等。
此外,平台的报警监控服务,消息通知服务,元数据管理服务,权限管控服务,数据可视化服务,数据质量监控服务等等,往往也是互相依赖的。
实时业务流程和离线业务流程相比,整体链路长度可能会更短,但是和外部系统的关联倒并不见得更简单,而服务的容错性和对网络带宽延迟等方面的要求往往也会更高一些。
所以,如何保证迁移期间,服务的稳定可靠,尽可能减少服务下线或不可用的时间,如何保证各种依赖服务的平滑过渡衔接呢?
业务模式和流程配合问题
说完服务,接下来说说业务方使用我们服务的模式,又会给迁移工作带来哪些麻烦。
如果所有的业务都由平台完全掌控,事情会好办一些,但作为数据平台基础架构团队,我们的定位是提供平台服务,所以,绝大多数的业务都是由业务方透过我们的服务来自主运行和管理的。那就会存在服务用多用少,用好用坏的问题
比如,可能某个业务方的一个完整业务链路,一半的流程是和他们自身的业务系统紧耦合的,在它们自己的平台和机器上运行,另一半的流程则零散的透过我们的服务来运行,甚至由于各种各样的原因跳过我们的平台调度体系和服务,直接使用底层的应用接口和集群进行交互(比如,具体的业务逻辑和数据处理逻辑强关联,代码逻辑无法拆分,不能模块化或着不想重构改造成通过我们的服务接口来处理数据,直接读写HDFS,直接提交MR任务等等)
如果整个流程链路是自封闭的,自产自销也就罢了,坏就坏在这些业务保不定还有些上下游依赖,需要和其它业务方的作业相串联。更糟糕的是,有时候,这些上下游依赖方并没有意识到对方的存在,再加上互联网公司难免出现的业务变更,组织架构调整,工作交接,遇上这种情况,事情就更加严重了。
这种情况,往往很多业务就需要具体的业务方配合梳理或者改造才能顺利完成迁移,可是业务方对这类工作一般都是拒绝的,很容易理解,大家都忙,都希望麻烦越少越好不是,你们迁移,别拖我们下水啊 ;)好吧,这怎么办?也只能动之以情,晓之以公司大义了呗 ;)
总之,这种情况下,如何降低风险,确保业务在迁移过程中不会出现大的差错,的确是个大问题。
业务逻辑和数据正确性问题
最后,是业务逻辑和由此带来的数据正确性问题。迁移,不光迁,迁完以后,你得保证业务结果的正确性吧?
在海量数据的情况下,如何验证数据,这本身已经是个很棘手的问题了,更糟糕的是,如果业务方自己都搞不清楚数据是否正确怎么办? 甚至同一个作业,在同样的数据集上跑两次,结果都不一样,重跑作业也不幂等又怎么办?再糟糕一点,连数据集自身的状态,可能也和时间相关,随时可能变化怎么办?
所以,你如何验证迁移的结果是正确的,或者有哪些业务是正确的?有多大的概率是正确的?谁能替结果负责? 万一某个业务真的有问题,能不能发现,怎么发现,具体又会是数据,脚本,集群哪个环节的问题?
那该怎么办
总之,大数据平台搬迁工作并不是光“集群” 搬迁 这么简单,它是一个你享受过一次以后,就绝对不想再来一次的苦差事。然而,说这么多,并没有什么用。日子再苦,也得过不是。接下来,让我来针对上述具体问题,和大家一起探讨一下应对的方式和几次搬迁过程中的我们的经验教训。
总体目标和原则
日子怎么过,上述问题如何应对,取决于你的目标是什么。一旦决定了目标,为了顺利达到,也就会有一些原则是不能轻易打破,要时时刻刻留心遵守的。
所以,我们的目标是:
整体迁移工作,一到两个月的周期内完成
迁移期间,大数据平台的各种服务不能长时间下线(最多小时级别),不能对公司业务造成影响。
必须确保迁移完成后,核心业务的正确性,不能靠运气,要有足够可靠的验证手段和数据
对于和外部系统重度耦合的业务,需要给业务方足够的时间,正确的环境和过渡手段分批逐步迁移
迁移过程,尽可能做到对多数业务方透明,减少需要业务方需要配合的工作
那么,原则有哪些呢:
一切迁移工作和步骤,不以难易为标准,以不对线上业务造成影响为标准
凡是可能出错,不能一步做到位的环节,都必须要有事前验证测试的手段
只要能够双跑的环节那就双跑,宁可花费更多的精力准备并行方案,也不能寄希望于一切顺利
具体的双跑方案,要确保与最终完成迁移,停止双跑后的流程最大限度的保持一致,减少切换带来的变数。
不做一锤子买卖,直到完成集群切换,数据和业务正确性验证完毕,正式开始对用户提供服务之前,都要给自己留下后路,坚决不做任何不可逆的操作
过程和步骤,能自动化的自动化,不能自动化的,也要明确的文档化和标准化,不能依靠临场的随机应变。
好吧,你可能会说,这些,不是废话么,必需做到呀。。。是的,如果只是光站着说说的话,那的确如此。
但是当你真正面对这项棘手的工作的时候,你就随时都有可能就把这些目标原则抛在脑后。毕竟,没有人想要主动给自己找麻烦,所以,这个时候,你只有时刻告诫自己,如果不这么做,一旦出了问题,只会更加的麻烦 ;)
大致流程方案
要在预期的时间范围内,风险和代价可控的完成迁移的工作,光靠跨机房网络这点带宽进行同步肯定是不现实的。所以,我们的整体迁移流程大致如下
分离历史数据,在源机房内部搭建中转集群,先做一次历史大全量数据的拷贝工作,受数据量规模限制,只同步那些确定不经常变更的数据,然后下线中转集群,物理搬迁到目标机房,再次上线同步到目标集群中
在历史数据同步过程中,在目标机房搭建数据平台的全套集群和服务,逐个验证各个服务功能的正确性
完成初始的大全量数据拷贝工作后,开始通过网络实施若干轮阶段性小全量数据拷贝工作,目标是将数据同步时间逐步缩短到当天能同步完成截止前一天为止的数据(因为第一轮全量拷贝的同步周期会比较长,期间集群新增的数据无法在一天内透过网络完成拷贝)
使用实际的历史数据,验证集群服务和性能
开始集群每日增量数据同步工作,同时,同步各种数据平台服务自身的元数据信息和作业脚本信息,开启作业双跑流程
每日核心作业双跑完毕后,对比两边平台的产出结果,排查问题,存在问题,修复,并继续下一轮双跑工作,如此循环,直到结果验证满意为止。
正式切换各种对外服务的域名,接口,数据库等到新机房,完成主要链路的迁移工作。
切换完毕后,保留原平台整体业务按既有逻辑运转一段时间,给部分因为各种原因无法双跑或立刻切换的业务留下分批迁移的时间窗口。
上述方案,主要描述的是偏离线批处理业务的迁移流程,实时类业务,由于从业务逻辑的角度来看,往往无法在统一的时间点上整体切换,所以更加强调分批双跑的流程。具体的迁移工作,也往往需要业务方根据自己的业务情况参与配合,因此流程上有些环节需要具体业务具体讨论,这里就不再详细阐述。
一些具体问题的分析和实践
如何保证正确性?
你要问迁移工作中,哪部分工作最难? 我可以很负责任的告诉你,不是海量数据的同步,也不是服务的搭建,甚至也不是与各种关联业务方无止境的沟通工作。
实际上,上述工作,尽管工作量很大,但只要花时间,总是能做好的。而最难,也最容易被轻视的,是你如何确保迁移完毕后,作业运行结果的正确性?
你可能会想,这还不简单,前面不都说了两边机房同步进行作业的双跑么?那第二天比较作业运行的结果数据就好了啊,不对,查问题,查到对为止。。。然而,事情并没有那么美好,暂且放下怎么比较结果不提,先让我们来看看结果真的可以用来比较么?
具体难在哪里
首先,双跑结果可以比较验证的前提,是数据源是一致的,但数据源往往做不到一致。。。
先来看看数据平台是如何采集外部数据的?数据平台的上游数据源有很多,但主要的来源是DB和日志,这两种数据源根据业务场景不同,会有不同的采集方式
比如日志可能通过客户端Agent采集后,写入Kafka消息队列,然后再消费解析写入Hive。
而DB一方面可以通过Binlog采集进入消息队列,走日志类似的流程消费,另一方面也可以直接连接DB,定时按一定的业务逻辑扫描源表后写入数据平台。采用哪种方式取决于数据量的大小,业务的更新模式等等。
那么问题来了,这些数据源是会随着时间变化的,什么时候执行这些数据采集任务的作业逻辑呢?任务执行的时间不同,采集到的结果就不一样啊,而两边集群具体某个任务的实际运行时间,受集群资源,前序任务运行时间等随机因素的干扰,是无法精确控制的。
你会说,判断数据源里信息的时间不就好了? 这里面有三个问题:
有些数据源的采集逻辑里没有可以用来做精确更新判断时间的依据信息(不要问我为什么,DB设计,业务逻辑,历史遗留问题等等都有可能)
自定义的清洗脚本逻辑,或者自认为对具体时间信息不敏感,或者没有意识到会有问题,没有做时间筛选。
流程中做了数据的时间筛选判断,但客户端会有晚到的数据,会有时间错误的数据,前置链路会有延迟的情况等等。
其次,同样的数据源,双跑的结果一致,还有一个要求是,作业运行逻辑是幂等的。 所谓幂等,这里包含了两层意思:一是只要输入源一致,作业每次运行的结果都因该是一样的,二是重跑等情况对结果没有影响。
但实际情况也不是这样,不少作业逻辑并非幂等,运行两次,结果并不能保证一致,一个集群上跑如此,在两边集群分别运行,那就更加无法保证了。
为什么会出现非幂等作业的情况?比如有些作业逻辑对数据进行排序,然后Limit取部分值,而排序用的字段组合,并非唯一标识一行数据的,也就是说在分布式计算的场景下,排序的结果顺序可能是随机的。再有,比如脚本运行的逻辑是把上游的增量数据写入下游的全量表中,如果因为各种原因执行了两次,那就会写入两份数据等等。
虽然实际上这些差异可能对作业的业务逻辑结果的正确性不一定有很大影响(否则早就被发现并解决了),比如客户端晚到的PV数据算昨天的还是今天的?每天都有部分移位的数据的话,量级上还补偿上了呢。。。
但是这对双跑结果的验证,却带来了不小的麻烦,我们平台上每天跑上万个任务,产出几千张结果表,虽然对应存在幂等或随机问题的作业比例不会很高,但是经过作业依赖传导以后,可能会对下游的大批作业都造成影响。那么如果几千张结果表的数据全都不一样(虽然差异有大有小),你又如何判断平台迁移结果的正确性? 你显然不可能去挨个人工分析每张表数据不一致的原因。
这时候,你可以对自己有信仰,相信只要集群服务是正确的,就OK了,管它结果如何呢,一定都是上述原因造成的,无伤大雅。
说实话,这的确是一种解决方案。前提是,你的业务方认可,领导也认可。问题在于你如何说服他们相信集群服务是正确的呢?请拿数据说话啊,可是数据都不一样啊!你说差异是正常的,是由业务逻辑造成的,可是有差异的部分,业务方会替你背书么?我看悬,而你自己,说实话,也未必真的心里踏实。。。
可以采取的措施
所以,要降低风险,就必须尽可能的减少这两者的干扰。实际上,我们前期做的大量准备工作都是围绕着这个目标来的,具体方案上在几次迁移过程中,也采用过不同的措施。详细的细节不说,大致包括:
数据源部分不双跑,单边跑完,同步,再开始双跑(然而,一来违反了双跑和正式切换流程尽可能一致的原则,二来双跑流程会变得冗长,影响正常业务和实施验证的时间点,最近一次迁移,放弃了这种方案)
提前梳理非幂等脚本,逻辑上能够修复的进行修复
日志采集链路,采用双跑但是由源端单边判断和控制offset进度,确保两边数据的读取范围完全一致
DB链路上,适当调整运行时间,尽量规避由于链路延迟,业务更新等造成的数据晚到/变更的情况
通过这些手段,减少源头数据的差异和计算过程的随机性,最终我们能够做到主要链路三千五百多张表格,90%左右的表,验证结果我们认为完全一致,无需人工判断。99%的表,差异比例在0.1%以下,只需要重点人工检查极少量差异较大的表的结果逻辑,和部分核心关键表格的具体数值,加快了结果验证的效率和可靠性。
验证比较方式
最后再来说一下如何比较结果数据。
首先,结果数据分为两类,一类是在集群上的数据,主要以hive表为主。另一类是导出到外部数据源的数据,以DB为主,也有ES/HBASE等。
理论上你会说,那就一条一条的对比啊。但问题是,你如何确定用哪一条数据和哪一条数据进行对比?排序么?怎么排序?一方面,你不可能为每张表单独构建比较逻辑,另一方面,海量的数据你是否有足够的计算和存储资源进行比较?你是否可能承担相应的代价?
要确定哪条数据对比哪条数据,针对DB中的数据,我们的做法是拼合所有我们认为可能区别一行数据的字段,对两边的表进行Join,然后根据Join后的结果进行值的比对。这种做法并不完全精确,因为无法保证拼合用的字段就一定构成Unqiue key,能够唯一标识每一行数据,还是可能造成错位比较数据,将实际结果正确的表格误判为结果不匹配。
而对于集群上的海量hive数据来说,这种操作,无论计算和存储代价都是无法接受的。所以,集群上的hive表,我们只统计数据的条数和尺寸大小,你说这样做会不会风险太大?其实还好,理论上,只要比较最终导出到DB的业务数据就OK了,毕竟下游数据如果一致,上游数据也应该是一致的,所以Count Hive表中的数据量大小,主要是为了方便溯源查找问题,同时快速判断整体的差异情况。
最后,这些工作要执行的顺利,还需要尽可能的自动化,还要让比较结果便于人工解读,所以在实际操作中,我们还会自动将比较的结果格式化的导入到可视化平台的报表中,这样,业务方可以通过各种条件,过滤和筛选比较结果,便于快速定位问题。 总之,一切都是为了提高验证效率,加快验证速度。给问题修复,双跑迭代和正式切换工作留出更充裕的时间。
集群数据同步拷贝
平台搬迁,需要同步的数据源头很多,包括 HDFS/HBase/DB 这里面有业务数据,也有各种服务和系统自身需要的配置,任务,元数据,历史记录等信息。
这里主要讨论一下HDFS集群数据的拷贝同步,毕竟这是同步工作中,占比最大,也最麻烦的部分。
如何在两个集群间同步HDFS集群数据,显然,你不会去做硬盘拷贝的动作。因为集群上的数据是在持续变化的,而且,还有元数据映射关系要处理呢。
玩过一点HDFS集群的同学应该都了解,HDFS集群自身提供了一个distcp工具来做集群间的数据拷贝工作。但是,真正用这个工具实践过整个集群规模的数据拷贝工作的同学,估计就是凤毛麟角了。为什么这么说,因为这个工具有很大的局限性。无论谷歌,stackoverflow,还是邮件列表,你几乎看不到大规模搬迁的实际案例。
所以,distcp工具最大的问题是什么? 它最大的问题是慢! 不是拷贝文件速度慢,而是拷贝任务的启动速度和收尾速度慢!
至于为什么慢,就要来看看distcp的工作原理和流程了。
Distcp在执行拷贝工作前,会先根据指定的目录路径比较两边集群的文件状态,生成需要新增/修改/删除的文件列表内容,这个过程包括遍历目录树,比较文件元数据信息(比如时间戳,尺寸,CRC校验值等等)。生成的结果提交执行MR任务执行,然后,当数据全部拷贝完成以后,还要执行结果校验,元数据信息同步之类的工作。同步哪些元数据信息取决于你的执行distcp时指定的参数,比如文件owner,权限,时间戳,拷贝数等等。通常情况下,做集群迁移工作,这些信息都是要同步的。
在Distcp的执行流程中,开始和收尾的很多步骤,都是单机执行的。。。所以当集群的规模大到一定程度的时候(比如我们集群PB级别的容量,亿级别的文件对象),这两步动作就会变得异常缓慢。在我们的集群中,往往需要2-3个小时的启动和收尾时间。(这还是在一些步骤社区已经打过多线程补丁,配置过参数以后,否则会需要4-8小时的启动时间)
所以,用Distcp来做历史全量数据的同步,问题不大,但是要在数据增量同步阶段,进行快速同步迭代,就比较困难了。而我们的目标是做到最大限度不影响线上业务,那么同步流程就会希望做到尽可能快速的迭代,最后一轮增量同步动作加上各种DB元数据同步和准备工作,必须在一个小时内完成。
为了达到这个目标,我们参考distcp的代码,自己开发了数据同步拷贝的工具,主要针对distcp的问题,将一些单机执行的流程进行了调整,分散到Map任务中并行的执行,同时调整和简化了同步过程中的一些工作步骤(比如拷贝完成后的CRC校验,出错的概率非常非常低,万一拷贝出错了,下一轮同步的时候覆盖掉或者再同步一次就好了),这样准备和收尾时间可以做到只需要半小时就能完成。整体一轮增量同步所需时间,在最后一轮增量数据很少的情况下,可以满足一小时内完成的目标。
实际的同步工作,我们前期通过distcp完成了历史数据的同步,后续集群范围的增量数据同步通过自己开发的这个工具定时自动循环执行来完成。而如果有临时的小范围的数据拷贝动作,则还是通过distcp工具来完成(因为我们的同步工具,业务逻辑设计得比较固定)
当然,除了上面说的迭代速度的问题,数据同步工作中还有很多其它的问题要考虑,比如:
有些数据是不需要/不能同步的,那么需要过滤掉
数据拷贝过程中源头数据发生了变化怎么办?(实际上Distcp后期的版本还提供了基于集群Snapshot来拷贝和验证的机制,我们其中一次迁移使用过这个机制,也有很多具体问题需要解决)
出于各种原因,在一些场景下,我们需要获取集群文件比较的差异信息(汇总和明细),来做同步任务的决策。
总结来说,数据同步工作的难点在于及时,准确和文件状态的可控可比较。这三点做得好不好,对整体迁移流程的顺利进行和结果的验证,影响还是很大的。
各种无法双跑的业务场景梳理
要想处理无法双跑的业务,首先,你得找到哪些业务不能双跑不是。问业务方是不行的,因为业务方自己可能也未必清楚,多数情况下,要靠你的经验判断以及反复的沟通去推动梳理
举几个在我们的场景下无法/不宜 双跑的例子:
大量不受我们自己管辖的数据源,具体管辖的业务方没有时间精力或资源搭建双跑用数据源的(比如,没那么多机器,还有其它数据源写入,相关业务需要修改代码等等)
一些服务或作业双跑会干扰线上业务的。比如监控报警相关业务,如果按流程跑,双跑过程中无效的报警或双份的报警都不是业务方希望看到的。可以hack一些流程,但是代价就比较高了。
双跑会对一些系统会造成压力的,比如网络带宽,服务负载
业务整体流程,只有部分链路在我们的系统中,双跑这部分业务链路会造成整体业务逻辑错误的
此外还有一些业务场景可能需要修改以后才能支持双跑的,比如从消息队列读取数据,如果不修改消费组ID信息,双跑的业务就会在同一份数据中各自读取部分数据,造成两边结果都是错误的。
类似会出问题的地方可能还有很多,都是坑啊。。。
至于无法双跑的业务场景,如果发现了,那么具体如何处理,反倒可能没有那么难,总能找到临时解决方案,最最不济,在目标集群禁掉部分业务,不要双跑,依靠其它手段提前验证确保流程的正确性,集群切换完再把这部分业务单独切换过去就好了。
小结
小结一下:大数据平台/集群的搬迁工作,绝对不是集群数据拷贝这么简单(实际上,数据拷贝也不简单),作为一个开放的服务平台,拥有海量的数据,系统组件众多,上下游依赖关系错综复杂,业务逻辑不完全受你控制,外部系统的方案和决策也往往不受你左右,而你的业务环境又是在持续变化中,可能出错的环节太多太多了。
在这种情况下,请务必坚守文中我们所提到的原则:坚决不做任何不可逆转的操作;凡事另可麻烦一些,也要给自己留下退路;尽可能让所有的步骤,流程自动化,标准化;让系统状态透明化;同时做好犯错的准备,提前想好补救的手段。
生活总是如此艰辛,还是只有搬迁时才这样?总是如此!
祝好运,搬迁顺利!
常按扫描下面的二维码,关注“大数据务虚杂谈”,务虚,我是认真的 ;)