中移(苏州)软件技术有限公司研发经理王东旭:Ceph实践经验分享
大家好,自我介绍一下,我来自于中国移动苏州研发中心,也就是中移(苏州)软件技术有限公司。中国移动在Ceph领域里做了很多尝试,现在在线上运行的Ceph节点超过4000个,在Ceph的使用过程当中遇到了很多问题,这些问题我们做了一些梳理总结,完善了监控告警机制,也做了很多代码开发,希望和大家一起分享一下。
我的演讲分为四个小部分,第一是介绍一下大云的存储产品,第二是块存储方面的改进,主要涉及到TCMU和Cinder方面,第三是存储热升级方案,第四介绍一下我们在海量多数据中心方案的改进。
中移(苏州)软件技术有限公司研发经理王东旭
首先介绍一下我们这个产品,NanoStore的目标是做统一存储,最终提供对象、块和文件接口。目前cephfs不是那么稳定,所以基于Ceph我们提供的是块存储和对象存储两种接口。块存储有两种实现方式,一个是用户态的librbd,一个是内核态的krbd,krbd不是特别稳定,而且krbd有一个非常大的缺点,需要在高版本的Linux内核下运行。用户态的rbd主要是运行在KVM环境下,使用librbd来访问存储集群。目前企业里有很多环境,比如VMware、EXSI、Windows 等还不能直接使用rbd。我们既然要提供统一存储的服务,就必须要实现iSCSI协议来提供对这些环境的支持。
我们基于社区主推的方案TCMU来进行iSCSI方面的开发。TCMU我们是在2016年底开始介入,现在社区仍在快速的演进过程当中。既然是快速演进过程中,一定会有很多问题需要解决,我们在社区里也做了很多方面的工作。其中第一项工作是缓存的动态伸缩。Ring Buffer是TCMU非常重要的一个数据结构,数据读写都是经过Ring Buffer,这是一个环型的缓存区,每个设备都需要分配这么一块内存。之前的缓存区的大小是固定的,固定缓冲区在高IO压力下,比如高IOPS或者高带宽,都会导致性能不会那么好;另外如果设备特别多,消耗的内存就会特别大。我们对这个ring buffer的大小做了一个根据压力,动态的调整,如果是在IO压力特别大的情况下,给它分配更多的内存,这样IO刷得会更快,如果设备比较多,IO压力不大,就会分配比较小的内存,这样在相同的物理内存可用条件下,可以支持更多的设备。
第二个改进是开发了一套非阻塞日志系统,现在很多应用依然是采用syslog接口输出日志,但是syslog有些问题,日志如果过多,syslog可能会阻塞应用运行,甚至会影响到操作系统。前面介绍了Ring Buffer,我们也把Ring Buffer引入到了日志系统去。采用Ring Buffer动态分配内存机制,我们可以让刷日志的过程更流畅。平时日志级别比较高,日志量不多,Ring Buffer占用内存也会非常小。改进后的日志输出方案也支持了syslog和标准输出和日志文件。这套代码整个不到一千行,非常简单,容易维护。
第三个改进,为了方便存储软件更新配置,对上层的业务影响感知尽可能少,我们开发了一项功能,来对配置文件动态加载,其实这个实现非常简单,新加了一个线程,来监听配置文件,如果发现配置文件有改动,我们会重新reload到进程里。TCMU进程不需要重启,从而对应用的没有影响。
既然是块存储,在云环境下离不开OpenStack,Cinder是其中管理块存储的组件。我们开发了一个Cinder的驱动,这个驱动实现上有两种方式,第一种方式是通过TCMU Target命令行,通过TCMU暴露的用户接口,一个虚拟的文件系统进行Target命令操作。但是这个方案有弊端,发给文件系统的操作并不能被整个集群感知,所以我们采用了第二种方案,用gateway命令。一个Gateway得到这个命令之后,它会自动同步到Gateway集群里面去,从Gateway实现对target的操作。
Cinder里有cinder-backup来对块存储进行备份,大多数场景下会把块存储备份到对象存储。传统的备份方法是有些问题的,针对于这些备份的问题我们做了改进。举例来说,,第一个图,用户有100G的数据,他会全部读取100G的数据,做后端去重,读取的过程当中要读全量的,100G数据量非常庞大。改进后,首先是从存储集群里得到备份的增量,Ceph里有接口,rbd-differ可以得到这个差异,差异的数据量可能非常小,可能仅有1个G,我们备份就仍然是备份1个G。它的好处是,增量的读取会很大程度上减少读取的数据量。极端情况下将会有上百倍的性能提升。第二个改进是数据恢复的流程,传统的是从前往后进行恢复,比如下面黄色的,第一个备份是20G,第二个备份也是20G,第三个备份还是20G,我要恢复这个备份3,需要把读取60G的数据,我们改进的是从后往前恢复,因为备份3是最新的,优先把备份3的数据读出来。而备份3的数据下面的块的数据是新的,备份2打叉的地方就没用了,只读取它的10个G数据,备份1就不需要再读取数据了,所以从后往前恢复数据只需要读取30G数据。两个方案对比,一个是30G,一个是60G,数据量的压力减少一半。第三个改进,中间备份可删除,现在的客户基本上都是要求增量的备份恢复,以节省用户对象存储宝贵的空间。我们的实现是:假如说左边那个备份N-1被删除,绿色的这部分数据就会合并到差异数据里, 这时对象存储里并不需要真正的倒腾数据,数据还是在那,只更改元数据就可以了。但是传统的,中间的备份都是不可以删除的,要删除只能从尾部进行删除。
永不停服是存储的重要目标,为了实现这个目标,Ceph其实已经做得很好了,绝大多数情况下可以滚动升级来满足需求。但是有些用户的需求更严苛。存储软件的进程挂了之后,或多或少会对虚机、对应用产生一定影响,为了改善用户体验,我们开发了存储热升级技术,其中一个是进程的热升级,一个是动态库的热升级。进程的热升级原理是代码修改之前进行编译生成机器代码;代码修改后重新进行编译,生成一个修改后的机器代码,对比两个机器代码,输出的这个差异文件对我们来说就是热升级补丁。接下来是开始真正做热升级的过程,热升级的过程是先把修改后的热补丁差异文件映射到正在运行的进程的地址空间去。右边是一个代理函数,这个代理函数做了一个转发,转发到一个修改后的函数,在替换过程中有一个问题需要注意,就是保持线程之间的同步,升级过程当中怎么样保证对这个函数的调用安全呢?替换过程开始的时候,会让所有的线程去获取一个信号量,在替换过程当中让所有的线程阻塞。替换过程非常快,替换完之后通知工作线程继续运行了,对进程来说就是一个地址的转换。进程热升级有一个缺点,不能使用全局变量,只能进行函数的修改,任何使用全局变量的场景都是不能做进程热升级的。
KVM是使用用户态rbd驱动,会调用librbd这个动态库,如果发现librbd有问题,需要做替换,因为虚机的进程已经启动了,需要把虚机停掉才能升级librbd,然后才能重启虚机,业务才能正常进行。但是用户可能不希望重启虚机,所以理想情况是在虚机运行过程当中把librbd替换掉,整体思路和进程的热升级是比较像的,过程分为三个过程,一个是把新的动态库加载到进程的地址空间,第二是找到全部需要替换的符号到GOT表项地址,第三是用新地址替换旧地址。动态库在升级过程当中起一个进程,这个进程充当原进程的父进程。通过桩函数将热升级补丁注入到目标进程里去,然后目标进程就可以正常调用新的动态库。
我们线上还跑着一些对象存储,对象存储我们最大的一个集群是有接近500个节点,涉及到两个数据中心。这个图是传统的一个多数据中心的方案,中间烟台DC是作为一个主zone,如果对北京进行读写,需要同步元数据和数据到烟台这个数据中心,然后这个主zone再同步到上海DC。这个过程其实是有些问题的,如果我对北京DC和上海DC同时进行读写,这个数据很可能会产生冲突,不管是数据也好,元数据也好,同步是需要时间的。基于此我们设计了一个新的方案,这个方案不涉及代码,只涉及部署方案,这个图有两个DC,一个是北京,一个是烟台,北京里面设置一个主zone,就是左边紫色这个框,烟台那边也设一个主zone,下面这个紫色的框。上、下共有两个zonegroup。上边的读写只是北京zone1进行,下面的读写只是对右边紫色烟台进行,两个zonegroup之间只同步元数据,这样就解决了读写可能会冲突的问题。
上面是对中国移动苏州研发中心对Ceph方面的一些改进和总结,这些总结绝大多数都是我们线上遇到的一些问题或者需求,比如TCMU是我们通用性上的需求,比如Cinder驱动,是我们发现用户备份时间漫长做的改进优化;比如多数据中心,对象的池子大了,需要各个层面做优化。其实Ceph是一个非常好的开源的分布式存储的方案,但也不妨碍在线上遇到一些棘手的运维问题,比如说在一个二层网络里,出现过两个存储节点之间单通的问题,A和B通,B和A不通。这种情况下,Ceph也好,其他分布式存储软件也好,更多的可能会依赖于监控告警,借助于人工的或者自动化的运维手段去处理,分布式存储不是万能的,也需要网络和操作系统的一些其他技术去配合。
中国移动苏州研发中心承担着中国移动内的很多资源池的建设,我们也希望有合作意向的同仁可以和我们开展技术交流和产业合作,谢谢大家!