从Ceph Object Storage到Swift的数据迁移
原文链接 https://lingxiankong.github.io/2016-12-09-migrate-to-swift.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
前段时间一直在忙team的项目,在我们基于OpenStack的公有云(Catalyst Cloud)上部署Swift服务。由于历史原因,我们的公有云之前提供的对象存储是基于Ceph Object Gateway(RGW),所以我们已经有大量的用户在Ceph上存储的数据,并且每天都会有更新,总容量有十几个T。我在这个项目中的任务就是完成数据迁移。在Ceph集群之外,我们部署了独立的Swift集群,所以准确的说,我们需要做的是数据拷贝,而并非数据迁移。
这个工作看似简单,就是把数据从RGW获取到,再上传到Swift,因为RGW同时支持S3和Swift API,所以我选择了在迁移代码中全程使用swiftclient。根据swiftclient的开发者文档,同时提供一个low-level和一个high-level api,分别叫swift connection和swift service。最初我使用connect,但后面转而使用service client。
这篇文章不是为了详细介绍我的迁移代码如何实现,而是简单记录在这个过程中一些值得注意的点。
关于存储的迁移/复制,有个项目叫Rclone可以参考,但它无法满足我们的需求,而且它是go语言写的,临时修改的话有学习成本。
如何提升迁移速度?
最初代码完成时,整个过程比较直观,遍历每一个租户的每一个container中每一个object。这个过程在小规模测试时没有问题,但在生产环节就变的不可接受,因为速度太慢。有一些租户,在一个container中有几十万个objects,就算平均处理一个object需要1秒,光这个租户的一个container迁移完就需要将近10天,并且后续的同步过程也会持续很长时间。我们对服务宕机的时间是有SLA要求的,所以我开始对代码进行调优。
首先想到的是使用多进程,在代码中开启多个process处理不同的tenant。这样确实会加快复制速度,但生产环境中,有的租户数据较多,有的就几个很小的object,所以最开始迁移的时候,根据对象的大小将租户分组,尽可能使每个进程都处于忙碌状态。而迁移的后期,随着租户的数据逐步复制到Swift,已经不太合适继续根据对象大小进行租户分组了,而是应该根据对象个数。因为每个进程不再忙着做IO操作,而是遍历每个object进行API查询。
但还是不够完美。我在上面提到过,我们云上有某些租户在一个container中有好几十万个object,就算一个进程处理一个这样的租户,也会花很多时间。看来根据租户分组还是粒度不够,要细分到container中的object。于是,我在一个父进程中再根据对象个数,动态创建多个子进程,每个子进程仅处理一部分object。
还能提高速度么?当然可以。
无论是前期的数据复制(IO密集),还是后期的check(计算密集),当然是希望程序离RGW的api节点和Swift proxy节点越“近”越好,因为这样可以有效较少网络开销。所以,经过我们ops同意,最终选择在Swift proxy节点运行迁移程序。
日志打印
把日志打印拿出来单说,是因为前期迁移时,因为日志做的不够好,导致有时候程序可能是在迁移比较大的对象,而我误认为是某些原因导致程序卡死,于是经常强行把程序kill掉,然后重新启动,耽误了很多时间。
对于一个运行过程耗时比较长的程序,打印必要的、有用的日志很有讲究。像这种数据迁移的程序,前期本来就是IO密集,如果再打印过多(不必要)的日志,并且还要写日志文件,就会导致你看到的日志滞后,那就失去了打印日志的作用,因为我想实时了解迁移的进度。
所以,首先我会在screen中运行程序,这样不会因为session中断而无法看到日志;其次,我将日志打印到屏幕上,仅把错误信息保存在内存并在最后才写入文件以供后续分析。在日志中会呈现当前的时间、正在迁移的租户、container、object、该租户还剩余多少object以及大小。
RGW的坑
RGW有很多坑,估计跟我有类似迁移需求的人不多,再次我仅仅记录一下,不详细展开了。
- 使用boto multipart upload上传文件到RGW时,在RGW中最终存储的object的hash值是文件分片后最后一片的hash,这样就导致在后期检查object是否修改时,这个hash没有参考意义。
- 当使用boto multipart upload上传文件到RGW时,object大小可能会超过Swift规定object的最大值,此时需要对object进行分片再上传。这也是我使用high-level swiftclient的原因,它会根据参数自动处理大文件。
- 在RGW上使用DLO时,如果对象发生变更时,DLO的manifest object的hash并不发生变更。需要根据大小和时间戳来判断对象是否变化。
- 有时某些object的hash中包含
\x00
,需要屏蔽。
swiftclient的坑
目前为止,仅发现一个。
- service client不支持object名字第一个字符是
\
,而connection client支持。
总结
Swift项目是我入职Catalyst以来参与的第一个比较大的项目,本来老外做事就慢,再加上前期购买硬件设备的等待时间,这个项目前前后后持续了有小半年……跟在华为做项目迭代一样,这里也需要一个大概的时间点确定task的状态,分配task,定义task之间的依赖,只是没有项目压力而已。语言仍然是参与项目的一个比较大的障碍,有时还是听不懂他们在聊啥,还得厚着脸皮不停的sorry。
写这个博客并不是为了讲什么技术点,而是作为工作历程的一个记录,希望自己不是行尸走肉……