SPDK對接Ceph效能優化

天翼雲開發者社群發表於2022-06-02

作者:天翼雲 譚龍

關鍵詞: S PDK NVM e OF Ceph、 CPU 負載均衡

 

 

SPDK是intel公司主導開發的一套儲存高效能開發套件,提供了一組工具和庫,用於編寫高效能、可擴充套件和使用者態儲存應用。它通過使用一些關鍵技術實現了高效能:

1.  將所有必需的驅動程式移到使用者空間,以避免系統呼叫並且支援零拷貝訪問

2.  IO的完成通過輪詢硬體而不是依賴中斷,以降低時延

3.  使用訊息傳遞,以避免 IO路徑中使用鎖

SPDK是一個框架而不是分散式系統,它的基石是使用者態(user space)、輪詢(polled-mode)、非同步( asynchronous )和無鎖的 NVMe驅動,其提供了零拷貝、高併發直接使用者態訪問SSD的特性。SPDK的最初目的是為了優化塊儲存落盤效能,但伴隨持續的演進,已經被用於優化各種儲存協議棧。S PDK 架構分為協議層、服務層和驅動層,協議層包含 NVM e OF T arget、vhost-nvme  T arget、iscsi  T arget、vhost-scsi  T arget以及vhost-blk T arget等,服務層包含L V Raid、A IO malloc以及Ceph  RBD 等, driver層主要是 NVM e OF initiator、N VM e  PCI e、virtio以及其他用於持久化記憶體的driver等。

 

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.  問題 :我們搭建了一個 4 8 OSD Ceph全閃叢集,叢集效能大約4 0 w  IOPS ,我們發現最高跑到 2 0 w  IOPS 就上不去了,無論增加盤數或調節其他引數均不奏效。

3.  分析定位 :使用 spdk_top顯示0號核相對其他核更加忙碌,繼續加壓, 0 號核忙碌程度增加而其他核則增加不明顯。

 

檢視 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模組後b dev_rbd_submit_request 介面會將 IO上下文排程到disk->main_td,所以每個rbd的執行緒都執行在0號核上。

綜合環境現象與程式碼分析,最終定位該問題的原因是:由於 spdk   rbd模組在建立盤時bdev_rbd_create_cb介面會將每個盤的主執行緒disk->main_td分配到0號核上,故導致多盤測試時C PU 負載不均衡,效能無法持續提高。

4.  解決方案 :既然問題的原因是 C PU 負載不均衡導致,那麼我們的思路就是讓 CPU 更加均衡的負責盤的效能,使得每個盤分配一個核且儘可能均衡。具體到程式碼層面,我們需要給每個盤的 disk->main_td分配一個執行緒,且這個執行緒均勻分佈在0 ~7 號核上。我們知道 bdev_rbd_create_cb是在每次需要建立io_channel時被呼叫,第一次bdev_examine的呼叫執行緒是spdk主執行緒app_thread,之後的呼叫均是在呼叫者執行緒上執行,比如當rbd bdev attach到nvmf subsystem時,呼叫者所線上程為nvmf_tgt_poll_group_#,因為這些執行緒本身就是均勻的建立在0 ~7 號核上,故我們可以複用這些執行緒給 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 負載變得均衡,效能突破 2 0 w,達到叢集4 0 w能力。

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/ rbd 0 可檢視,這把鎖的作用是防止多客戶端同時對一個 image寫操作時併發操作,當一個客戶端持有這把鎖後,其他客戶端需要等待該鎖釋放後才可寫入,所以多客戶端同時寫導致搶鎖效能非常低,為此這兩個patch做了兩個大的改動:1)對每個rbd   bdev,無論有多少個io_channel,最後只呼叫一次 rbd_open ,即只有一個 rbd客戶端,參見介面 bdev_rbd_handle 的呼叫上下文; 2)對每個盤而言, IO 全部收斂到一個執行緒 disk->main_td傳送給librbd。

 

因為每個盤的 disk->main_td均為第一個io_channel呼叫者的執行緒上下文,所以他們的執行緒都在同一個核上,導致I O 從上游到達 rbd模組後全部匯聚到一個核上,負載不均衡導致效能無法繼續提高。

6.  總結 :在定位效能問題時, C PU 利用率是一個重要的指標, spdk_top是一個很好的工具,它可以實時顯示每個核的繁忙程度以及被哪些執行緒佔用,通過觀察C PU 使用情況,結合走讀程式碼流程,能夠更快定位到問題原因。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70014251/viewspace-2898700/,如需轉載,請註明出處,否則將追究法律責任。

相關文章