零故障上云全过程再现,PB级数据迁移如何保障一致性?

靳洪兵

美图 技术专家

  • 美图用户产品研发部技术专家,主要负责美图秀秀 区服务研发,主导参与美图秀秀调度系统、内容中台等多个项目的研发与设计。在分布式系统、消息中间件等方面具有丰富经验;
  • 曾就职于新浪微博,负责平台研发部消息中间件、配置服务等系统的研发与设计。
  • 一、美图秀秀介绍

    我们来看一组数据,截止今年上半年,美图应用月活跃用户数超过3亿,独立设备数超过20亿,每个月美图秀秀要处理的图片以及视频数超过30亿。

    在这些数据背后还有一些看不见的数据,在全国各地我们有6大自建机房,数以百计的服务器,十几款软硬件产品,PB级的业务数据。在支撑这些数据的过程中,我们逐渐的发现了一些问题。

    首先是运维管理复杂,因为有这么多的服务器,随着一些服务器过保老化,故障率逐步上升。为了业务的稳定性,需要投入大量的研发人力去监控这些机器的监控状况。每隔一段时间,我们就会看到公司大群里,内购一些硬盘。

    美图秀秀的业务流量有极强的周期性,周末比平时峰值流量高,元旦春节峰值会是平时的很多倍。为了支撑每年元旦春节流量,业务部门首先会预估流量,然后采购部门都会提前按照预估峰值流量采购服务器,然而春节过后流量下来,一部分服务器资源会闲置,变成一种浪费。需要付出的成本较高。

    美图内部多个部门服务及组件重复建设的情况也比较普遍。但是由于有的服务组件与业务绑定的过于紧密,使得美图内部孵化的一些创新产品无法快速使用,导致创新产品的发布周期较长。

    我们看到了这么些问题。应该怎么解决?那换个角度思考,从这些问题,看看我们需要的是什么?

    其实我们需要的是运维简单的弹性计算和存储资源,开箱即用的服务和组件共同支撑起应用的快速开发。

    通过什么样的方式能够实现我们想要的这些能力?经过公司仔细调研,我们给出的答案是云服务。公司在19年下半年提出了上云的计划。

    二、上云的挑战与应对

    那对于这么庞大体量的产品要上云,应该怎么做呢?又会遇到些什么问题呢?接下来就给大家带来的是上云的什么挑战,以及我们应该怎么去应对呢?

    可能有人会说上云有什么难的?把服务部署上去不就好了。一开始我们接到上云任务的时候也是这么想的。然后在讨论这个事情的时候,组内的资深技术专家给大家提了这样的一个问题?

    把大象装进冰箱里,总共分几步?当然我们都知道答案。3步,第一步把冰箱门打开,第二步把大象装进去,第三步把冰箱门关上。上云就相当于把大象装进冰箱。简单来说上云也分3步:

  • 第一步在云上创建对应的服务和资源
  • 第二步把数据同步上去
  • 第三步把流量切换到云上。
  • 每一步听起来都很简单,对不对?但是具体每一步该怎么做,细节就是魔鬼?我们有3亿的月活,海外用户的占比也不小,我们需要为用户提供的是7X24小时的不间断服务,在上云的过程中也不能中断。另外是我们有PB级数据,用户数据是我们最核心的资产,不管在哪个环境下,我们都需要保证用户数据的完整性和一致性。

    简单来说就是用户需要完全无感知,这就要求我们整个上云的过程需要做到平滑和稳定。接下来主要围绕着如何做到平滑和稳定来跟大家分享上云的一些方案设计。

    去掉一些细枝末节,我们可以将业务系统的架构简化为3层,最上层的负载均衡,中间的web服务,下面是资源层,对应着3个层面的迁移,流量的切换,应用的迁移和资源的迁移。另外还有依赖的外部服务。如果外部依赖不迁移,跨IDC的访问,专线有1ms的延迟,公 有几毫秒的时延。

    我们将依赖服务按照跨IDC的时间敏感度分为强依赖服务和弱依赖服务。对于强依赖服务,需要在我们业务上云之前部署在云上,弱依赖服务可以按照服务提供方自身的上云顺序进行,但是我们仍然需要关注的是依赖服务跨IDC访问的一个带宽的情况。

    每一个迁移操作我们都会按照“准备”—>”执行”—>”验证”—>”观察”的方式来操作,确保每一个操作都没有问题才进行下一个操作。

    接下来我会按照大象装冰箱的一个步骤,以应用、资源和流量的顺序跟大家分享各部分内容具体是如何迁移的。

    1、把冰箱门打开:应用迁移

    首先是应用的迁移。

    在云下我们有自己的一套容器化平台,可以支撑业务的容器化需求。

    因此应用迁移,我们会将其划分为两种类型,一种是容器化应用,另一种是非容器化的应用。由于容器化平台的建设和改造提前上云,可以让业务在云上部署应用的时候采用跟IDC相同的方式,所以大大简化了应用上云的复杂性。

    对于另一类非容器化应用,因为我们的应用都是无状态的服务,所以上云主要考虑的是在云上部署的问题。那是部署在虚机上,还是各方面性能都更好的裸金属服务器?另外在选择组件的时候,我们是应该自建,还是使用云上的开源组件,或者云上自研的组件?其实这里涉及到一个云上服务及组件的选型问题。那我们遵循的一个选型原则是:最大限度的复用已有的流程和系统。

    因为我们的目的是服务上云,不是做架构或者应用优化。上云本身是一个复杂的工程,我们希望在上云的过程中尽量控制变量的发生。在上云的过程中不会刻意去改动架构和流程,因为这样会进一步的增加上云的复杂度和时间,导致故障的发生。当然也不是绝对的不变,为了解决上云中的一些问题,该变的还是要变。就是不要为了“顺便”做个架构优化而去变。

    2、把大象装进去:数据迁移

    说完了应用迁移,相当于打开了冰箱门,接下来是数据迁移,该装大象了。

    数据分为两部分,一个是业务数据,一个是媒体数据。业务数据也包括了很多,比如MySQL的数据,缓存Memcache,Redis,Elasticsearch等等。今天业务数据我们只跟大家介绍MySQL和Memcached。

    在下面的分享中,大家可以看到MySQL中的持久化数据,Memcache中的缓存和媒体文件作为3种常见的数据,分别采取了不同的迁移方案。

    要迁移PB级的数据量,仅仅依靠公 带宽做数据传输是远远不够的。那应该怎么办?很多人会想到搭专线,没错。我们也是使用专线来进行数据的传输。但其实公有云还提供了一种方式,就是数据快递。使用方式是,你预约一个数据快递服务,然后云厂商会给你邮寄一个几十或者几百T容量的硬件设备,真的是邮寄,你把这个设备插到服务器上开始同步数据,同步完成后寄给云厂商,云厂商会把这些数据在云上用对应的存储恢复出来。

    我们有PB级的数据,好几百个数据快递在路上跑,听起来总有一种不靠谱的感觉。当然我们也没有采用这种方案。说回专线。

    我们在各个机房之间提早搭建了专门用于数据传输的专线。

    业务跨IDC访问流量走业务专线,业务数据传输通过数据传输专线,然后媒体文件走存储专线。

    在右边公有云上,针对不同的集群,我们会划分vpc来做 络隔离,vpc之间通过虚拟路由来连接。另外还会搭建一套云上的测试环境。主要用于服务组件的压测和迁移方案的演练。后面绝大多数方案中涉及到的一些问题都是通过在测试环境压测和演练发现的。

    在专线带宽搭建好之后,我们开始进入mysql的数据迁移。在数据迁移前,我们会在云上搭建一套跟IDC配置一样的mysql的主从集群。因为本身业务做了读写分离,所以主从节点都分配有各自的域名。一开始公有云上的读写域名都指向IDC。

    我们依靠云上提供的数据复制服务来做数据同步。将IDC的数据同步到云上。同步分为两个阶段,刚开始是存量数据同步,之后是增量数据同步。云上数据复制服务会告诉你什么时候存量数据同步完了,什么时候开始增量数据同步。

    那么MySQL数据迁移的完整性如何保证?我们从两方面来看,首先是数据复制服务的设计,我们可以将数据复制服务理解为也是MySQL的实例,使用的同步机制跟MySQL的主从同步一样,理论上来说就是相当于是挂载了一个MySQL的从库,由MySQL的主从机制来保证数据的完整性。

    另外我们还可以从两个维度来验证数据的完整性,一个是数据复制服务提供了按照表行数和表内容的对比功能,同时,从业务层面我们也提供了相应的接口可以抽样对比两个数据库中的数据。

    在数据同步完成以及校验完成之后,下一步就是读写流量的切换?切换顺序是先切读再切写。流量切换只需要变更对应域名的DNS配置。当域名的DNS配置变更完成后,并且经过验证变更已经生效后,mysql的流量应该切换到云上。

    但是在验证环境,我们通过流量监测脚本发现IDC的mysql集群仍然有来自云上的读写流量?通过ip对比,确认这些流量来自一部分服务,通过在这些服务的机器上去查看tcp连接,我们并没有发现来自云上服务的ip。

    那我们首先想到的是不是DNS的缓存?

    在对应的机器上执行nslookup域名解析,域名确实已经解析到云上了。机器上域名解析正常。

    再一个怀疑的是不是服务里面有dns缓存的机制?当我们在服务里面去做域名解析的操作发现依然是解析到云上。

    都不是,那会是什么问题?重启大法。

    我们重启一部分服务,发现服务起来之后连接全部到云上。没有IDC的连接。说明不是域名解析的问题。

    然后我们在另外的没有重启的服务上发现了一个现象,就是新建的连接都来自云上,老的一些连接来自IDC。从这里发现我们不同服务使用的数据库组件,有的有dns感知,有的没有dns感知功能。当域名变化时,没有dns感知功能的服务不会主动释放老的连接创建新连接。

    因为本身服务使用的组件都比较老,变更版本带来了很多依赖兼容性问题,所以我们放弃在服务本身上变更。转而在方案上做文章。

    在计算机系统设计领域有这样一句经典语录:”计算机系统设计里面的任何问题都可以通过增加一个中间层来解决”。

    我们通过增加一个VIP来解决域名变更后长连接不断开的问题。

    切完写之后,流量全部切换到云上,此时IDC没有写入流量,这时候IDC的MySQL主从集群已经不能用了。整个切换操作就完成了。如果方案到这里就结束了,那这只是一个正常的切换操作。

    那再问一个问题,如果这个时候云上服务出现故障,需要回滚怎么办?已经回滚不回去了,数据已经不一致了。

    那么我们该怎么解决切写之后的回滚问题呢?

    想一想这里回滚不了是因为什么?因为IDC没有完整的数据了。那现在完整的数据在哪里在云上,那如果我把云上的数据同步到IDC一份不就又是一份完整的数据了吗?但是同步的时机肯定不是出问题之后,而是应该在出问题之前。

    顺着这个思路,在一开始的时候,我们就在IDC重新准备了一个备库集群。将公有云上的数据又同步一份到备库集群。这样可以保证在切写之后,IDC还是存在一份完整的数据,可以保证流量的回滚。在回滚后,将数据复制服务的流向变更为从最右边的集群到共有云。然后重复前面的过程。

    针对MySQL的数据迁移,这里有些小Tips跟大家分享。

    第一点,是数据迁移需要做数据对比,对比从两方面入手,一个是迁移工具的验证,一个是业务层面的验证。另外要注意迁移源库的内存使用率,因为在做数据对比的时候,源库的内存使用率会上升,如果源库本身内存容量不足,有可能造成源库oom。在实际迁移过程中,我们就遇到了源库oom的情况。

    第二点,针对一些按年,按月建表的业务,不要忘记了数据库表创建的脚本要同步到云上。

    第三点是数据库复制服务的限速,一个是复制的过程中不要给源库造成太大的压力,另外迁移过程中有多个应用多种数据迁移,需要协调专线的使用率。

    第四点是 在做切换操作时,需要将切换操作脚本话,一键切换,只需要执行一条命令。可以减少出错的几率。

    介绍完MySQL的数据迁移,下面介绍Memcached。

    看下作为缓存的memcache是如何迁移的。美图秀秀重度依赖mc作为缓存。mc的同步采用的是双写+就近读的方案。云上和IDC会互相写数据,但是读流量只会读本数据中心的mc。另外在流量切换之前需要有一个预热的过程,预热既可以在业务层面做,也可以通过灰度的流量自动预热,利用灰度预热需要我们实现预估好穿透的量,避免给mysql造成太大的压力。

    在测试环境使用mc的过程中发现了不少的问题。

  • 在稳定运行期间,mc容量写满之后性能下降比较明显。而且数据写得越多,性能下降越厉害。
  • 通过云上监控发现,容量满后,对应虚机swap开始上涨。发现云上mc实例默认开启了swap。

    我们使用mc的姿势就是申请一定容量的mc实例,然后一直写数据,写满后依赖mc自身内存管理机制来淘汰数据。有一些业务方跟我们使用姿势不一样,会开启mc的swap。开启swap,当访问到磁盘上的数据时性能就下降的比较厉害。

  • mc平稳运行一段时间后会突然挂掉?
  • 这个问题跟前面关闭swap有一定关系,因为mc创建在虚机上。比如我们申请的mc实例是8G,那虚机内存8G,因为操作系统会占用一定的内存,所以mc实际使用的内存可能不会超过6.4G,当我们不断写入数据之后,由于关闭了swap没法做数据交换,在内存分配和销毁的过程中不断产生内存碎片。当整个mc的内存+操作系统内存超过8G后,虚机会重启,导致mc挂掉。这里需要的是设置mc的max_memory的值。

  • mc的穿透率不平稳,时高时低?
  • 穿透率与mc的淘汰策略有关系,虽然业务也会设置mc的过期时间,但是在业务设置的mc过期之前,就存在穿透率不平稳的问题。然后发现云上mc的淘汰策略设置的是随机淘汰。互联 应用大多是读多写少的模式。因为我们首页请求量很大,需要缓存来抗读的量,越热的数据越需要有被缓存住,如果是随机淘汰可能会因为淘汰掉热数据带来一定的穿透量。

    由于我们重度依赖mc作为缓存,mc的查询性能直接影响到上云后系统的稳定性。因此在上云前会在压测平台上拷贝线上真实环境的流量对云上的mc进行了压测。但是压测的结果显示,mc的读性能跟idc相比有一定的差异,差异大得有点令人诧异。具体现象是压测流量到达某个点后,mc会出现大量的数据淘汰,同时伴随着有大量的新连接创建,CPU利用率升高,ops降低,然后5-10分钟后又自动恢复的现象。

    跟云上工程是沟通发现,云上mc不是原生的mc实现,是使用redis+mc协议实现的。当然云上有自己的考虑,使用redis主要是为了数据持久化以及HA。但是我们知道, redis内存管理基于Jemalloc内存分配器,Jemalloc频繁分配和释放内存会导致内存碎片。而原生的mc使用slab的内存管理,内存满后,淘汰同等大小的slab,直接覆盖数据,不会造成内存碎片。

    当碎片率到达一定程度会触发内存的主动淘汰,由于主动淘汰时间过长,影响了mc的读写性能。

    另外一个我们业务上使用的mc客户端,当发现mc时延超过一定阈值后,会重新创建连接。由于服务数过多,导致单个mc上会看到大量的新建连接过来。每一个连接又会占用一定的mc内存,从而进一步恶化整个mc的性能。

    针对这个问题,云上进行了mc的性能优化攻关,最终将mc的性能提升到跟idc的一个量级。

    在mc的性能这点,其实前面的参数都不重要,重要的是我们对于云上服务和组件应该保持的一个态度。引用一句当年美苏中程核武器谈判时,美国总统里根对戈尔巴乔夫说的一句话俄语谚语:“Trust,But Verify”。信任,但要核实。

    有点儿奇怪的一个场景,一个中国人演讲里面引用的是美国人对俄国人说的俄语谚语。

    媒体文件的迁移。

    特点数据量大,迁移时间长,缓存数据双写同步按天记,数据库同步按周,媒体要按月。数据迁移依然使用云上的obs迁移工具,支持断点续传。同时还需要我们在业务层面进一步做数据对比。全量数据做etag对比,确保数据完整迁移到云上。

    切读依赖CDN更改域名。域名指向变更后读流量就逐步迁移到云上。

    切写流量依赖上传SDK组件变更上传域名。我们可以下发需要上传的域名给到sdk组件。但是客户端会有一段时间的缓存,因此始终都会存在一部分客户端将数据上传到IDC,一部分上传到公有云。

    因此,对于媒体

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2021年3月15日
    下一篇 2021年3月15日

    相关推荐