SPDK对接Ceph性能优化

关键词:SPDK、NVMeOF、Ceph、CPU负载均衡

SPDK是intel公司主导开发的一套存储高性能开发套件,提供了一组工具和库,用于编写高性能、可扩展和用户态存储应用。它通过使用一些关键技术实现了高性能: 1.将所有必需的驱动程序移到用户空间,以避免系统调用并且支持零拷贝访问 2.IO的完成通过轮询硬件而不是依赖中断,以降低时延 3.使用消息传递,以避免IO路径中使用锁 SPDK是一个框架而不是分布式系统,它的基石是用户态(user space)、轮询(polled-mode)、异步(asynchronous)和无锁的NVMe驱动,其提供了零拷贝、高并发直接用户态访问SSD的特性。SPDK的最初目的是为了优化块存储落盘性能,但伴随持续的演进,已经被用于优化各种存储协议栈。SPDK架构分为协议层、服务层和驱动层,协议层包含NVMeOF Target、vhost-nvme Target、iscsi Target、vhost-scsi Target以及vhost-blk Target等,服务层包含LV、Raid、AIO、malloc以及Ceph RBD等,driver层主要是NVMeOF initiator、NVMe PCIe、virtio以及其他用于持久化内存的driver等。 image

SPDK架构 Ceph是目前应用比较广泛的一种分布式存储,它提供了块、对象和文件等存储服务,SPDK很早就支持连接Ceph RBD作为块存储服务,我们在使用SPDK测试RBD做性能测试时发现性能到达一定规格后无法继续提升,影响产品的综合性能,经过多种定位方法并结合现场与代码分析,最终定位问题原因并解决,过程如下。 1.测试方法:启动SPDK nvmf_tgt并绑定0~7号核,./build/bin/nvmf_tgt -m 0xff,创建8个rbd bdev,8个nvmf subsystem,每个rbd bdev作为namespace attach到nvmf subsystem上,开启监听,initiator端通过nvme over rdma连接每一个subsystem,生成8个nvme bdev,使用fio对8个nvme bdev同时进行性能测试。 2.问题:我们搭建了一个48 OSD的Ceph全闪集群,集群性能大约40w IOPS,我们发现最高跑到20w IOPS就上不去了,无论增加盘数或调节其他参数均不奏效。 3.分析定位:使用spdk_top显示0号核相对其他核更加忙碌,继续加压,0号核忙碌程度增加而其他核则增加不明显。 image

查看poller显示rbd只有一个poller bdev_rbd_group_poll,与nvmf_tgt_poll_group_0都运行在id为2的thread上,而nvmf_tgt_poll_group_0是运行在0号核上的,故bdev_rbd_group_poll也运行在0号核。 [root@test]# spdk_rpc.py thread_get_pollers {   "tick_rate": 2300000000,   "threads": [     {       "timed_pollers": [         {           "period_ticks": 23000000,           "run_count": 77622,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_tgt_accept"         },         {           "period_ticks": 9200000,           "run_count": 194034,           "busy_count": 194034,           "state": "waiting",           "name": "rpc_subsystem_poll"         }       ],       "active_pollers": [],       "paused_pollers": [],       "id": 1,       "name": "app_thread"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5919074761,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         },         {           "run_count": 40969661,           "busy_count": 0,           "state": "waiting",           "name": "bdev_rbd_group_poll"         }       ],       "paused_pollers": [],       "id": 2,       "name": "nvmf_tgt_poll_group_0"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5937329587,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 3,       "name": "nvmf_tgt_poll_group_1"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5927158562,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 4,       "name": "nvmf_tgt_poll_group_2"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5971529095,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 5,       "name": "nvmf_tgt_poll_group_3"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5923260338,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 6,       "name": "nvmf_tgt_poll_group_4"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5968032945,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 7,       "name": "nvmf_tgt_poll_group_5"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5931553507,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 8,       "name": "nvmf_tgt_poll_group_6"     },     {       "timed_pollers": [],       "active_pollers": [         {           "run_count": 5058745767,           "busy_count": 0,           "state": "waiting",           "name": "nvmf_poll_group_poll"         }       ],       "paused_pollers": [],       "id": 9,       "name": "nvmf_tgt_poll_group_7"     }   ] } 再结合代码分析,rbd模块加载时会将创建io_channel的接口bdev_rbd_create_cb向外注册,rbd bdev在创建rbd bdev时默认做bdev_examine,这个流程会创建一次io_channel,然后销毁。在将rbd bdev attach到nvmf subsystem时,会调用创建io_channel接口,因为nvmf_tgt有8个线程,所以会调用8次创建io_channel接口,但disk->main_td总是第一次调用者的线程,即nvmf_tgt_poll_group_0,而每个IO到达rbd模块后bdev_rbd_submit_request接口会将IO上下文调度到disk->main_td,所以每个rbd的线程都运行在0号核上。 image image

综合环境现象与代码分析,最终定位该问题的原因是:由于spdk rbd模块在创建盘时bdev_rbd_create_cb接口会将每个盘的主线程disk->main_td分配到0号核上,故导致多盘测试时CPU负载不均衡,性能无法持续提高。 4.解决方案:既然问题的原因是CPU负载不均衡导致,那么我们的思路就是让CPU更加均衡的负责盘的性能,使得每个盘分配一个核且尽可能均衡。具体到代码层面,我们需要给每个盘的disk->main_td分配一个线程,且这个线程均匀分布在07号核上。我们知道bdev_rbd_create_cb是在每次需要创建io_channel时被调用,第一次bdev_examine的调用线程是spdk主线程app_thread,之后的调用均是在调用者线程上执行,比如当rbd bdev attach到nvmf subsystem时,调用者所在线程为nvmf_tgt_poll_group_#,因为这些线程本身就是均匀的创建在07号核上,故我们可以复用这些线程给rbd模块继续使用,将这些线程保存到一个global thread list,使用round-robin的方式依次分配给每个盘使用,该方案代码已推送到SPDK社区:bdev/rbd: Loadshare IOs for rbd bdevs between CPU cores (I9acf218c) · Gerrit Code Review (spdk.io)。打上该patch后,可以观察到CPU负载变得均衡,性能突破20w,达到集群40w能力。 5.思考回溯:该问题是如何出现或引入的?我们分析rbd模块的合入记录,发现在bdev/rbd: open image on only one spdk_thread · spdk/spdk@e1e7344 (github.com)和bdev/rbd: Always save the submit_td while submitting the request · spdk/spdk@70e2e5d (github.com)对rbd的结构做了较大的变化,主要原因是rbd image有一把独占锁exclusive_lock,通过rbd info volumes/rbd0可查看,这把锁的作用是防止多客户端同时对一个image写操作时并发操作,当一个客户端持有这把锁后,其他客户端需要等待该锁释放后才可写入,所以多客户端同时写导致抢锁性能非常低,为此这两个patch做了两个大的改动:1)对每个rbd bdev,无论有多少个io_channel,最后只调用一次rbd_open,即只有一个rbd客户端,参见接口bdev_rbd_handle的调用上下文;2)对每个盘而言,IO全部收敛到一个线程disk->main_td发送给librbd。

因为每个盘的disk->main_td均为第一个io_channel调用者的线程上下文,所以他们的线程都在同一个核上,导致IO从上游到达rbd模块后全部汇聚到一个核上,负载不均衡导致性能无法继续提高。 6.总结:在定位性能问题时,CPU利用率是一个重要的指标,spdk_top是一个很好的工具,它可以实时显示每个核的繁忙程度以及被哪些线程占用,通过观察CPU使用情况,结合走读代码流程,能够更快定位到问题原因。

点赞
收藏

评论区

加载中...

相关推荐

京东APP百亿级商品与车关系数据检索实践 | 京东云技术团队

本文主要讲解了京东百亿级商品车型适配数据存储结构设计以及怎样实现适配接口的高性能查询。通过京东百亿级数据缓存架构设计实践案例,简单剖析了jimdb的位图(bitmap)函数和lua脚本应用在高性能场景。希望通过本文,读者可以对缓存的内部结构知识有一定了解,并且能够以最小的内存使用代价将位图(bitmap)灵活应用到各个高性能实际场景。

TarsGo支持Protocol Buffer

Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(TotalApplicationFramework),目前支持C,Java,PHP,Nodejs,Golang语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。它集可扩展协议编解码、高性能RPC通信框架、名字路

DUBBO 详细介绍

摘要:主要核心部件:Remoting:网络通信框架,实现了syncoverasync和requestresponse消息机制.RPC:一个远程过程调用的抽象,支持负载均衡、容灾和集群功能Registry:服务目录框架用于服务的注册和服务事件发布和订阅Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能

CEPH总结

ceph简介Ceph是一个分布式存储系统,诞生于2004年,是最早致力于开发下一代高性能分布式文件系统的项目。随着云计算的发展,ceph乘上了OpenStack的春风,进而成为了开源社区受关注较高的项目之一。ceph基本结构!(http://uploadimages.jianshu.io/upload_images/4

TarsGo新版本发布,支持protobuf,zipkin和自定义插件

Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架,目前支持C,Java,PHP,Nodejs,Golang语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用

TarsGo支持Protocol Buffer

Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(TotalApplicationFramework),目前支持C,Java,PHP,Nodejs,Golang语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。它集可扩展协议编解码、高性能RPC通信框架、名字路