相信很多做存儲的同學(xué)都聽說過SPDK,它是Intel開發(fā)的一套開源存儲棧。SPDK的全稱為存儲高性能開發(fā)包(Storage Performance Development Kit),從名稱可以看出SPDK其實(shí)就是一個(gè)第三方的程序庫。但是這個(gè)程序庫卻是非常強(qiáng)大的,下圖是SPDK的軟件模塊圖,從該圖可以看出,幾乎囊括了整個(gè)存儲棧。
其實(shí)SPDK最早開發(fā)出來得時(shí)候功能還是比較單一的,它是作為一個(gè)用戶態(tài)的NVMe驅(qū)動(dòng)來發(fā)布的。這一點(diǎn)可以通過閱讀SPDK的發(fā)布日志得到。但是到目前,SPDPK包含三層數(shù)十個(gè)功能組件。其底層是驅(qū)動(dòng)層,包括NVMe驅(qū)動(dòng)和經(jīng)過網(wǎng)絡(luò)的NVMe-oF驅(qū)動(dòng),這也是SPDK最開始發(fā)布的內(nèi)容。

中間層則是SPDK開發(fā)的一個(gè)存儲服務(wù)層,這里最核心的是提供了一個(gè)塊設(shè)備抽象層。同時(shí),在該層還實(shí)現(xiàn)了塊存儲服務(wù)常見的功能,比如邏輯卷、快照和克隆等等。
最上層則是存儲協(xié)議層,包括常見的如iSCSI協(xié)議,Linux網(wǎng)絡(luò)塊存儲nbd,以及NVMe相關(guān)的NVMe-oF等。通過該層的功能,主機(jī)端可以通過存儲協(xié)議以遠(yuǎn)程的方式訪問NVMe提供的存儲功能。
我們從架構(gòu)回到具體實(shí)現(xiàn),如下圖是SPDK的目錄結(jié)構(gòu)。在該目錄樹中,核心的三個(gè)目錄分別是app、lib和module,這里面包含著SPDK中比較核心的代碼。從目錄名稱上我們也大概能猜出其中代碼的大致功能。
其中app目錄包含著一些應(yīng)用程序的源代碼,其中包含iscsi_tgt、nvmf_tgt和spdk_dd等子目錄,每個(gè)子目錄可以編譯成一個(gè)可以獨(dú)立執(zhí)行的應(yīng)用程序。例如iscsi_tgt,這個(gè)其實(shí)是一個(gè)iscsi的Target端,它本身也是一個(gè)開源項(xiàng)目。SPDK借鑒了該該開源項(xiàng)目,并且在其上做了大量的優(yōu)化。由于這些應(yīng)用都是可以獨(dú)立運(yùn)行的,因此我們也可以以這些應(yīng)用的代碼作為切入點(diǎn)來學(xué)習(xí)SPDK的代碼實(shí)現(xiàn)。
另外一個(gè)目錄是lib目錄,從名稱上來看可以知道該目錄包含一個(gè)功能集合,這是SPDK最核心的代碼。SPDK的代碼組織還是比較清晰的,很多核心功能都以庫的形式提供。比如SPDK中比較核心的事件(event)和線程模型(thread)等。

其中module目錄也是非常重要的目錄,其中包含的內(nèi)容與上述架構(gòu)圖中的存儲服務(wù)相對應(yīng)。從上圖我們可以看到諸如加密、邏輯卷、RAID、Linux AIO和Ceph RBD等。
除了上面介紹的三個(gè)目錄外,還有一個(gè)目錄中的代碼是值得關(guān)注的,這就是examples目錄。這個(gè)目錄中的代碼雖然不是SPDK的核心代碼,但是我們可以通過其中示例來學(xué)習(xí)SPDK的實(shí)現(xiàn)細(xì)節(jié)。
上面都是從理論層面來介紹SPDK的,感覺還是不太接地氣。從程序員的角度來說更喜歡實(shí)際動(dòng)手操作一下。接下來我們從實(shí)操層面介紹一下SPDK。首先我們可以安裝一個(gè)Ubuntu的虛擬機(jī),在虛擬機(jī)上進(jìn)行相關(guān)的學(xué)習(xí)。這里我們使用的版本是Ubuntu20.04,其他版本不確定釋放可以正常運(yùn)行。
首先我們需要下載一下SPDK的代碼,SPDK的代碼就放在github上,我們可以通過git命令clone一個(gè)本地庫。
git clone https://github.com/spdk/spdk --recursive
由于SPDK依賴很多第三方的軟件庫,所以我們需要安裝一下。大家不必?fù)?dān)心安裝這些軟件庫有多難,因?yàn)镾PDK已經(jīng)給我們提供了工具。我們可以通過下面命令自動(dòng)安裝依賴的軟件包:
sudo scripts/pkgdep.sh
上述命令可以安裝一個(gè)最小依賴包,也可以通過下面命令安裝所有的依賴軟件包:
sudo scripts/pkgdep.sh –all
安裝完成依賴包后就可以編譯SPDK的源代碼了,編譯過程也是非常簡單的,我們可以執(zhí)行如下兩條命令完成SPDK的編譯。
./configure
make
編譯完成后可以運(yùn)行一下SPDK自帶的單元測試用例庫,在運(yùn)行該用例庫的時(shí)候可能會看到不少錯(cuò)誤信息。不過沒有關(guān)系,只要我們在最后看到運(yùn)行成功的提示信息就可以了。
./test/unit/unittest.sh
如果一切沒有問題,說明SPDK已經(jīng)編譯成功了。在運(yùn)行SPDK示例之前,我們需要進(jìn)行一個(gè)基本的設(shè)置,具體命令如下:
sudo scripts/setup.sh
上述命令主要是釋放內(nèi)核對NVMe設(shè)備的管理,當(dāng)然也包含一些其他設(shè)置。完成上述設(shè)置后,我們就具備一個(gè)運(yùn)行SPDK應(yīng)用的基本環(huán)境了。接下來我們看一下如何運(yùn)行一個(gè)基于SPDK的基本應(yīng)用。

做為一個(gè)存儲工程師,IP-SAN是最常見的應(yīng)用場景了。我們就看一下如何通過SPDK構(gòu)建一個(gè)iSCSI目標(biāo)端應(yīng)用,并且通過iSCSI來進(jìn)行訪問。如上圖是IP-SAN的拓?fù)?,?jì)算節(jié)點(diǎn)用過以太網(wǎng)與存儲系統(tǒng)建立連接,訪問存儲系統(tǒng)上的存儲資源。在存儲端需要運(yùn)行一個(gè)稱為目標(biāo)器(Target)的程序。

對于SPDK來說,整體拓?fù)涫且恢碌?,但略有差異。目?biāo)器服務(wù)的存儲資源LUN對應(yīng)的后端為一個(gè)bdev設(shè)備,本文以Malloc設(shè)備為例,具體如上圖所示。另外,在SPDK中已經(jīng)具備一個(gè)iSCSI目標(biāo)器程序,也就是iscsi_tgt。我們可以利用該程序入門SPDK,導(dǎo)出bdev設(shè)備給計(jì)算節(jié)點(diǎn)作為塊設(shè)備。首先我們運(yùn)行該程序,具體需要執(zhí)行如下命令:
./build/bin/iscsi_tgt
該程序運(yùn)行后相當(dāng)于啟動(dòng)了一個(gè)目標(biāo)器服務(wù),同時(shí)該命令不會退出,因此我們需要另外啟動(dòng)一個(gè)窗口進(jìn)行后續(xù)的配置。
首先需要有基本的存儲資源,我們這里通過創(chuàng)建一個(gè)Malloc類型的bdev設(shè)備,而不是使用NVMe設(shè)備。這里創(chuàng)建一個(gè)塊大小為512字節(jié),64MB存儲空間的bdev設(shè)備,具體命令如下所示:
sudo ./scripts/rpc.py bdev_malloc_create -b Malloc0 64 512
然后是在目標(biāo)器服務(wù)中創(chuàng)建一個(gè)端口組(port group),這里端口組的ID為1,地址為虛擬機(jī)的IP地址。
sudo ./scripts/rpc.py iscsi_create_portal_group 1 192.168.2.173:3260
再然后是創(chuàng)建一個(gè)啟動(dòng)器組,其ID為2,后面的地址表示允許接入該目標(biāo)器服務(wù)的IP地址。本文選用的是跟目標(biāo)器相同的虛擬機(jī),所以是相同的地址。當(dāng)然也可以使用另外一個(gè)新的虛擬機(jī),這個(gè)時(shí)候就需要更改該IP地址了。
sudo ./scripts/rpc.py iscsi_create_initiator_group 2 ANY 192.168.2.173
最后是在目標(biāo)器服務(wù)中創(chuàng)建一個(gè)目標(biāo)器,其名稱是disk1,同時(shí)可以有一個(gè)別名“Data Disk1”。在該目標(biāo)器中同時(shí)創(chuàng)建一個(gè)LUN0,其所使用的bdev設(shè)備是前面創(chuàng)建的Malloc0。同時(shí)建立該目標(biāo)器與端口組1和啟動(dòng)器組2的關(guān)聯(lián)。也就是說,在上述配置的IP地址上的啟動(dòng)器可以通過端口組訪問該目標(biāo)器了。
sudo ./scripts/rpc.py iscsi_create_target_node disk1 "Data Disk1" "Malloc0:0" 1:2 64 -d
萬事俱備,接下來我們就可以通過iSCSI協(xié)議來訪問配置的資源了。在虛擬機(jī)中執(zhí)行如下命令發(fā)現(xiàn)我們新創(chuàng)建的目標(biāo)器。這個(gè)命令就是一條普通的iSCSI命令,與SPDK無關(guān)。
sudo iscsiadm -m discovery -t sendtargets -p 192.168.2.173
執(zhí)行上述命令可以得到如下結(jié)果,可以看到這里的disk1正式我們前面創(chuàng)建的目標(biāo)器的名稱。
192.168.2.173:3260,1 iqn.2016-06.io.spdk:disk1
發(fā)現(xiàn)目標(biāo)器后我們就可以登錄目標(biāo)器了。執(zhí)行如下命令,我們就可以登錄目標(biāo)器,也就可以訪問存儲資源了。
sudo iscsiadm -m node –login
登錄成功后會返回如下內(nèi)容。
Logging in to [iface: default, target: iqn.2016-06.io.spdk:disk1, portal: 192.168.2.173,3260]
Login to [iface: default, target: iqn.2016-06.io.spdk:disk1, portal: 192.168.2.173,3260] successful.
此時(shí)我們可以通過fdisk命令來查看一下本地的塊設(shè)備列表,可以看到這里多出一個(gè)名為sdb的塊設(shè)備,大小為64MB。

至此,我們對SPDK做了一個(gè)整體的介紹,從架構(gòu)到代碼結(jié)構(gòu),然后又實(shí)操了一把。相信大家對SPDK有了一個(gè)基本的認(rèn)識了。但回頭想想,Linux內(nèi)核其實(shí)已經(jīng)實(shí)現(xiàn)上述功能了,為什么要在用戶態(tài)實(shí)現(xiàn)這么一套系統(tǒng)呢?
Intel官方給出的解釋是內(nèi)核的處理太過笨重了,NVMe的性能并沒有得到充分的發(fā)揮。我們基于虛擬機(jī)的環(huán)境并不能測試性能,但是我們可以看一下Intel官方的測試數(shù)據(jù)。下面兩張圖分別是4KB隨機(jī)讀和4KB隨機(jī)寫的性能對比圖。通過可以看到通過SPDK的性能是Linux內(nèi)核的2倍左右??梢钥吹?,SPDK在提高性能方面確實(shí)有不錯(cuò)的效果。
