日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
OpenStack虛擬機(jī)掛載數(shù)據(jù)卷過程分析

OpenStack虛擬機(jī)掛載數(shù)據(jù)卷過程分析

作者:付廣平 2017-09-14 10:11:24

企業(yè)動(dòng)態(tài)

OpenStack 目前已經(jīng)成為了最流行的一種開源云解決方案。其中提供計(jì)算服務(wù)組件Nova、網(wǎng)絡(luò)服務(wù)組件Neutron以及塊存儲服務(wù)組件Cinder是OpenStack的最為核心的組件。這里我們重點(diǎn)關(guān)注Nova和Cinder組件,Neutron組件將在下一篇文章中詳細(xì)介紹。

成都創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括蔚縣網(wǎng)站建設(shè)、蔚縣網(wǎng)站制作、蔚縣網(wǎng)頁制作以及蔚縣網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,蔚縣網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到蔚縣省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

1 關(guān)于OpenStackOpenStack

是一個(gè)IaaS開源項(xiàng)目,實(shí)現(xiàn)公有云和私有云的部署及管理,目前已經(jīng)成為了最流行的一種開源云解決方案。其中提供計(jì)算服務(wù)組件Nova、網(wǎng)絡(luò)服務(wù)組件Neutron以及塊存儲服務(wù)組件Cinder是OpenStack的最為核心的組件。這里我們重點(diǎn)關(guān)注Nova和Cinder組件,Neutron組件將在下一篇文章中詳細(xì)介紹。

1.1 計(jì)算服務(wù)NovaNova

組件為OpenStack提供計(jì)算服務(wù)(Compute as Service),類似AWS的EC2服務(wù)。Nova管理的主要對象為云主機(jī)(server),用戶可通過Nova API申請?jiān)浦鳈C(jī)(server)資源。云主機(jī)通常對應(yīng)一個(gè)虛擬機(jī),但不是絕對,也有可能是一個(gè)容器(docker driver)或者裸機(jī)(對接ironic driver)。

Nova創(chuàng)建一臺云主機(jī)的三個(gè)必要參數(shù)為:

  • image: 即云主機(jī)啟動(dòng)時(shí)的鏡像,這個(gè)鏡像source可能是從Glance中下載,也有可能是Cinder中的一個(gè)volume卷(boot from volume)。
  • flavor: flavor包含申請的資源數(shù)量,比如CPU核數(shù)、內(nèi)存大小以及根磁盤大小、swap大小等。除了資源數(shù)量,flavor還包含一些特性配置,稱為extra specs,可以實(shí)現(xiàn)設(shè)置io限速、cpu拓?fù)涞裙δ堋?/li>
  • network: 云主機(jī)的租戶網(wǎng)絡(luò)。

創(chuàng)建一臺云主機(jī)的CLI為:

  
 
 
 
  1. nova boot --image ${IMAGE_ID} --flavor m1.small --nic net-id=${NETWORK_ID} int32bit-test-1 

使用nova list可以查看租戶的所有云主機(jī)列表。

1.2 塊存儲服務(wù)Cinder

Cinder組件為OpenStack提供塊存儲服務(wù)(Block Storage as Service),類似AWS的EBS服務(wù)。Cinder管理的主要對象為數(shù)據(jù)卷(volume),用戶通過Cinder API可以對volume執(zhí)行創(chuàng)建、刪除、擴(kuò)容、快照、備份等操作。

創(chuàng)建一個(gè)volume有兩個(gè)必要參數(shù):

  • volume_type: volume_type關(guān)聯(lián)了后端存儲信息,比如存儲后端、QoS信息等。
  • size: 創(chuàng)建volume的大小。

創(chuàng)建一個(gè)20G的volume:

  
 
 
 
  1. cinder create --volume-type ssd --name int32bit-test-volume 20 

Cinder目前最典型的應(yīng)用場景就是為Nova云主機(jī)提供云硬盤功能,用戶可以把一個(gè)volume卷掛載到Nova的云主機(jī)中,當(dāng)作云主機(jī)的一個(gè)虛擬塊設(shè)備使用。

掛載volume是在Nova端完成的:

nova volume-attach ${server_id} ${volume_id}

Cinder除了能夠?yàn)镹ova云主機(jī)提供云硬盤功能,還能為裸機(jī)、容器等提供數(shù)據(jù)卷功能。john griffith寫了一篇博客介紹如何使用Cinder為Docker提供volume功能:Cinder providing block storage for more than just Nova。

本文接下來將重點(diǎn)介紹OpenStack如何將volume掛載到虛擬機(jī)中,分析Nova和Cinder之間的交互過程。

2 存儲基礎(chǔ)

2.1 什么是iSCSI

iSCSI是一種通過TCP/IP共享塊設(shè)備的協(xié)議,通過該協(xié)議,一臺服務(wù)器能夠把本地的塊設(shè)備共享給其它服務(wù)器。換句話說,這種協(xié)議實(shí)現(xiàn)了通過internet向設(shè)備發(fā)送SCSI指令。

iSCSI server端稱為Target,client端稱為Initiator,一臺服務(wù)器可以同時(shí)運(yùn)行多個(gè)Target,一個(gè)Target可以認(rèn)為是一個(gè)物理存儲池,它可以包含多個(gè)backstores,backstore就是實(shí)際要共享出去的設(shè)備,實(shí)際應(yīng)用主要有兩種類型:

  • block。即一個(gè)塊設(shè)備,可以是本地的一個(gè)硬盤,如/dev/sda,也可以是一個(gè)LVM卷。
  • fileio。把本地的一個(gè)文件當(dāng)作一個(gè)塊設(shè)備,如一個(gè)raw格式的虛擬硬盤。

除了以上兩類,還有pscsi、ramdisk等。

backstore需要添加到指定的target中,target會(huì)把這些物理設(shè)備映射成邏輯設(shè)備,并分配一個(gè)id,稱為LUN(邏輯單元號)。

為了更好的理解iSCSI,我們下節(jié)將一步步手動(dòng)實(shí)踐下如何使用iSCSI。

2.2 iSCSI實(shí)踐

首先我們準(zhǔn)備一臺iscsi server服務(wù)器作為target,這里以CentOS 7為例,安裝并啟動(dòng)iscsi服務(wù):

  
 
 
 
  1. yum install targetcli -y 
  2. systemctl enable target 
  3. systemctl start target 

運(yùn)行targetcli檢查是否安裝成功:

  
 
 
 
  1. int32bit $ targetcli 
  2. targetcli shell version 2.1.fb41 
  3. Copyright 2011-2013 by Datera, Inc and others. 
  4. For help on commands, type 'help'. 
  5.  
  6. /> ls 
  7. o- / .................................... [...] 
  8.   o- backstores ......................... [...] 
  9.   | o- block ............. [Storage Objects: 0] 
  10.   | o- fileio ............ [Storage Objects: 0] 
  11.   | o- pscsi ............. [Storage Objects: 0] 
  12.   | o- ramdisk ........... [Storage Objects: 0] 
  13.   o- iscsi ....................... [Targets: 0] 
  14.   o- loopback .................... [Targets: 0] 

如果正常的話會(huì)進(jìn)入targetcli shell,在根目錄下運(yùn)行l(wèi)s命令可以查看所有的backstores和iscsi target。

具體的targetcli命令可以查看官方文檔,這里需要說明的是,targetcli shell是有context session(上下文),簡單理解就是類似Linux的文件系統(tǒng)目錄,你處于哪個(gè)目錄位置,對應(yīng)不同的功能,比如你在/backstores目錄則可以對backstores進(jìn)行管理,你在/iscsi目錄,則可以管理所有的iscsi target。你可以使用pwd查看當(dāng)前工作目錄,cd切換工作目錄,help查看當(dāng)前工作環(huán)境的幫助信息,ls查看子目錄結(jié)構(gòu)等,你可以使用tab鍵補(bǔ)全命令,和我們Linux shell操作非常相似,因此使用起來還是比較順手的。

為了簡單起見,我們創(chuàng)建一個(gè)fileio類型的backstore,首先我們cd到/backstores/fileio目錄:

  
 
 
 
  1. /> cd /backstores/fileio 
  2. /backstores/fileio> create test_fileio /tmp/test_fileio.raw 2G write_back=false 
  3. Created fileio test_fileio with size 2147483648 

我們創(chuàng)建了一個(gè)名為test_fileio的fileio類型backstore,文件路徑為/tmp/test_fileio.raw,大小為2G,如果文件不存在會(huì)自動(dòng)創(chuàng)建。

創(chuàng)建了backstore后,我們創(chuàng)建一個(gè)target,cd到/iscsi目錄:

  
 
 
 
  1. /iscsi> create iqn.2017-09.me.int32bit:int32bit 
  2. Created target iqn.2017-09.me.int32bit:int32bit. 
  3. Created TPG 1. 
  4. Default portal not created, TPGs within a target cannot share ip:port. 
  5. /iscsi> 

以上我們創(chuàng)建了一個(gè)名為int32bit的target,前面的iqn.2017-09.me.int32bit是iSCSI Qualified Name (IQN),具體含義參考wikipedia-ISCSI,這里簡單理解為一個(gè)獨(dú)一無二的namespace就好。使用ls命令我們發(fā)現(xiàn)創(chuàng)建一個(gè)目錄iqn.2017-09.me.int32bit:int32bit(注意:實(shí)際上并不是目錄,我們暫且這么理解)。

創(chuàng)建完target后,我們還需要把這個(gè)target export出去,即進(jìn)入監(jiān)聽狀態(tài),我們稱為portal,創(chuàng)建portal也很簡單:

  
 
 
 
  1. /iscsi> cd iqn.2017-09.me.int32bit:int32bit/tpg1/portals/ 
  2. /iscsi/iqn.20.../tpg1/portals> create 10.0.0.4 
  3. Using default IP port 3260 
  4. Created network portal 10.0.0.4:3260. 

以上10.0.0.4是server的ip,不指定端口的話就會(huì)使用默認(rèn)的端口3260。

target創(chuàng)建完畢,此時(shí)我們可以把我們之前創(chuàng)建的backstore加到這個(gè)target中:

  
 
 
 
  1. /iscsi/iqn.20.../tpg1/portals> cd ../luns 
  2. /iscsi/iqn.20...bit/tpg1/luns> create /backstores/fileio/test_fileio 
  3. Created LUN 0. 

此時(shí)我們的target包含有一個(gè)lun設(shè)備了:

  
 
 
 
  1. /iscsi/iqn.20...bit/tpg1/luns> ls /iscsi/iqn.2017-09.me.int32bit:int32bit/ 
  2. o- iqn.2017-09.me.int32bit:int32bit ...................................................................................... [TPGs: 1] 
  3.   o- tpg1 ................................................................................................... [no-gen-acls, no-auth] 
  4.     o- acls .............................................................................................................. [ACLs: 0] 
  5.     o- luns .............................................................................................................. [LUNs: 1] 
  6.     | o- lun0 .......................................................................... [fileio/test_fileio (/tmp/test_fileio.raw)] 
  7.     o- portals ........................................................................................................ [Portals: 0] 

接下來我們配置client端,即iSCSI Initiator:

  
 
 
 
  1. yum install iscsi-initiator-utils -y 
  2. systemctl enable iscsid iscsi 
  3. systemctl start iscsid iscsi 

拿到本機(jī)的initiator name:

  
 
 
 
  1. int32bit $ cat /etc/iscsi/initiatorname.iscsi 
  2. InitiatorName=iqn.1994-05.com.redhat:e0db637c5ce 

client需要連接server target,還需要ACL認(rèn)證,我們在server端增加client的訪問權(quán)限,在server端運(yùn)行:

  
 
 
 
  1. int32bit $ targetcli 
  2. targetcli shell version 2.1.fb41 
  3. Copyright 2011-2013 by Datera, Inc and others. 
  4. For help on commands, type 'help'. 
  5.  
  6. /> cd /iscsi/iqn.2017-09.me.int32bit:int32bit/tpg1/acls 
  7. /iscsi/iqn.20...bit/tpg1/acls> create iqn.1994-05.com.redhat:e0db637c5ce 
  8. Created Node ACL for iqn.1994-05.com.redhat:e0db637c5ce 
  9. Created mapped LUN 0. 

注意:以上我們沒有設(shè)置賬戶和密碼,client直接就能登錄。

一切準(zhǔn)備就緒,接下來讓我們在client端連接我們的target吧。

首先我們使用iscsiadm命令自動(dòng)發(fā)現(xiàn)本地可見的target列表:

  
 
 
 
  1. int32bit $ iscsiadm --mode discovery --type sendtargets --portal 10.0.0.4 | grep int32bit 
  2. 10.0.0.4:3260,1 iqn.2017-09.me.int32bit:int32bit 

發(fā)現(xiàn)target后,我們登錄驗(yàn)證后才能使用:

  
 
 
 
  1. int32bit $ iscsiadm -m node -T iqn.2017-09.me.int32bit:int32bit -l 
  2. Logging in to [iface: default, target: iqn.2017-09.me.int32bit:int32bit, portal: 10.0.0.4,3260] (multiple) 
  3. Login to [iface: default, target: iqn.2017-09.me.int32bit:int32bit, portal: 10.0.0.4,3260] successful. 

我們可以查看所有已經(jīng)登錄的target:

  
 
 
 
  1. int32bit $ iscsiadm -m session 
  2. tcp: [173] 10.0.0.4:3260,1 iqn.2010-10.org.openstack:volume-1e062767-f0bc-40fb-9a03-7b0df61b5671 (non-flash) 
  3. tcp: [198] 10.0.0.4:3260,1 iqn.2010-10.org.openstack:volume-060fe764-c17b-45da-af6d-868c1f5e19df (non-flash) 
  4. tcp: [199] 10.0.0.4:3260,1 iqn.2010-10.org.openstack:volume-757f6281-8c71-430e-9f7c-5df2e3008b46 (non-flash) 
  5. tcp: [203] 10.0.0.4:3260,1 iqn.2010-10.org.openstack:volume-2ed1b04c-b34f-437d-9aa3-3feeb683d063 (non-flash) 
  6. tcp: [205] 10.0.0.4:3260,1 iqn.2017-09.me.int32bit:int32bit (non-flash) 

此時(shí)target已經(jīng)自動(dòng)映射到本地塊設(shè)備,我們可以使用lsblk查看:

  
 
 
 
  1. int32bit $ lsblk --scsi 
  2. NAME HCTL       TYPE VENDOR   MODEL             REV TRAN 
  3. sda  0:0:2:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  4. sdb  0:0:3:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  5. sdc  0:0:4:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  6. sdd  0:0:5:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  7. sde  0:0:6:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  8. sdf  0:0:7:0    disk ATA      INTEL SSDSC2BX40 DL2B 
  9. sdg  0:2:0:0    disk DELL     PERC H330 Mini   4.26 
  10. sdh  183:0:0:0  disk LIO-ORG  IBLOCK           4.0  iscsi 
  11. sdi  208:0:0:0  disk LIO-ORG  IBLOCK           4.0  iscsi 
  12. sdj  209:0:0:0  disk LIO-ORG  IBLOCK           4.0  iscsi 
  13. sdk  213:0:0:0  disk LIO-ORG  IBLOCK           4.0  iscsi 
  14. sdm  215:0:0:0  disk LIO-ORG  test_fileio      4.0  iscsi 

可見映射本地設(shè)備為/dev/shm。接下來就可以當(dāng)作本地硬盤一樣使用了。

以上我們是通過target服務(wù)器的一個(gè)本地文件以塊形式共享的,通常這只是用來測試,生產(chǎn)環(huán)境下一般都通過商業(yè)存儲提供真實(shí)的塊設(shè)備來共享。OpenStack Cinder如果使用的LVM driver,則是通過LVM卷共享的,這其實(shí)不難實(shí)現(xiàn),只需要把LVM對應(yīng)LV PATH加到block backstore即可,本文后面會(huì)重點(diǎn)介紹這種情況。

2.3 cinder-rtstool工具簡介

前面我們使用的targetcli是Datera公司開發(fā)的,不僅提供了這個(gè)CLI工具,Datera還提供了一個(gè)Python庫-rtslib,該項(xiàng)目地址為rtslib??赡苡捎谀承┰颍鐓^(qū)fork自rtslib項(xiàng)目,并單獨(dú)維護(hù)了一個(gè)分支,命名為“free branch”,即rtslib-fb項(xiàng)目,目前這兩個(gè)分支可能不兼容,因此確保targetcli、rtslib以及configshell是在同一個(gè)版本分支,要么全是fb,要么全是non-fb。

OpenStack社區(qū)基于rtstool封裝了一個(gè)CLI工具,即我們要介紹的cinder-rtstool工具。該工具使用起來非常簡單,我們查看它的help信息:

  
 
 
 
  1. $ cinder-rtstool --help 
  2. Usage: 
  3. cinder-rtstool create [device] [name] [userid] [password] [iser_enabled]  [-a] [-pPORT] 
  4. cinder-rtstool add-initiator [target_iqn] [userid] [password] [initiator_iqn] 
  5. cinder-rtstool delete-initiator [target_iqn] [initiator_iqn] 
  6. cinder-rtstool get-targets 
  7. cinder-rtstool delete [iqn] 
  8. cinder-rtstool verify 
  9. cinder-rtstool save [path_to_file] 

該工具主要運(yùn)行在target端,即cinder-volume所在節(jié)點(diǎn),其中create命令用于快速創(chuàng)建一個(gè)target,并把設(shè)備加到該target中,當(dāng)然也包括創(chuàng)建對應(yīng)的portal。add-initiator對應(yīng)就是創(chuàng)建acls,get-targets列出當(dāng)前服務(wù)器的創(chuàng)建的所有target。其它命令不過多介紹,基本都能大概猜出什么功能。

2.4 ceph rbd介紹

Ceph是開源分布式存儲系統(tǒng),具有高擴(kuò)展性、高性能、高可靠性等優(yōu)點(diǎn),同時(shí)提供塊存儲服務(wù)(rbd)、對象存儲服務(wù)(rgw)以及文件系統(tǒng)存儲服務(wù)(cephfs)。目前也是OpenStack的主流后端存儲,為OpenStack提供統(tǒng)一共享存儲服務(wù)。使用ceph作為OpenStack后端存儲,至少包含以下幾個(gè)優(yōu)點(diǎn):

  1. 所有的計(jì)算節(jié)點(diǎn)共享存儲,遷移時(shí)不需要拷貝塊設(shè)備,即使計(jì)算節(jié)點(diǎn)掛了,也能立即在另一個(gè)計(jì)算節(jié)點(diǎn)啟動(dòng)虛擬機(jī)(evacuate)。
  2. 利用COW特性,創(chuàng)建虛擬機(jī)時(shí),只需要基于鏡像clone即可,不需要下載整個(gè)鏡像,而clone操作基本是0開銷。
  3. ceph rbd支持thin provisioning,即按需分配空間,有點(diǎn)類似Linux文件系統(tǒng)的sparse稀疏文件。你開始創(chuàng)建一個(gè)20GB的虛擬硬盤時(shí),實(shí)際上不占用真正的物理存儲空間,只有當(dāng)寫入數(shù)據(jù)時(shí),才逐一分配空間,從而實(shí)現(xiàn)了磁盤的overload。

ceph的更多知識可以參考官方文檔,這里我們僅僅簡單介紹下rbd。

前面我們介紹的iSCSI有個(gè)target的概念,存儲設(shè)備必須加到指定的target中,映射為lun。rbd中也有一個(gè)pool的概念,rbd創(chuàng)建的虛擬塊設(shè)備實(shí)例我們稱為image,所有的image必須包含在一個(gè)pool中。這里我們暫且不討論pool的作用,簡單理解是一個(gè)namespace即可。

我們可以通過rbd命令創(chuàng)建一個(gè)rbd image:

  
 
 
 
  1. $ rbd -p test2 create --size 1024 int32bit-test-rbd --new-format 
  2. $ rbd -p test2 ls 
  3. int32bit-test-rbd 
  4. centos7.raw 
  5. $ rbd -p test2 info int32bit-test-rbd 
  6. rbd image 'int32bit-test-rbd': 
  7.         size 1024 MB in 256 objects 
  8.         order 22 (4096 kB objects) 
  9.         block_name_prefix: rbd_data.9beee82ae8944a 
  10.         format: 2 
  11.         features: layering 
  12.         flags: 

以上我們通過create子命令創(chuàng)建了一個(gè)name為int32bit-test-rbd,大小為1G的 image,其中-p的參數(shù)值test2就是pool名稱。通過ls命令可以查看所有的image列表,info命令查看image的詳細(xì)信息。

iSCSI創(chuàng)建lun設(shè)備后,Initiator端通過login把設(shè)備映射到本地。rbd image則是通過map操作映射到本地的,在client端安裝ceph client包并配置好證書后,只需要通過rbd map即可映射到本地中:

  
 
 
 
  1. $ rbd -p test2 map int32bit-test-rbd 
  2. /dev/rbd0 

此時(shí)我們把創(chuàng)建的image映射到了/dev/rbd0中,作為本地的一個(gè)塊設(shè)備,現(xiàn)在可以對該設(shè)備像本地磁盤一樣使用。

2.5 如何把塊設(shè)備掛載到虛擬機(jī)

如何把一個(gè)塊設(shè)備提供給虛擬機(jī)使用,qemu-kvm只需要通過--drive參數(shù)指定即可。如果使用libvirt,以CLI virsh為例,可以通過attach-device子命令掛載設(shè)備給虛擬機(jī)使用,該命令包含兩個(gè)必要參數(shù),一個(gè)是domain,即虛擬機(jī)id,另一個(gè)是xml文件,文件包含設(shè)備的地址信息。

  
 
 
 
  1. $ virsh  help attach-device 
  2.   NAME 
  3.     attach-device - attach device from an XML file 
  4.  
  5.   SYNOPSIS 
  6.     attach-device   [--persistent] [--config] [--live] [--current] 
  7.  
  8.   DESCRIPTION 
  9.     Attach device from an XML 
  10.  
  11.   OPTIONS 
  12.     [--domain]   domain name, id or uuid 
  13.     [--file]   XML file 
  14.     --persistent     make live change persistent 
  15.     --config         affect next boot 
  16.     --live           affect running domain 
  17.     --current        affect current domain 

iSCSI設(shè)備需要先把lun設(shè)備映射到宿主機(jī)本地,然后當(dāng)做本地設(shè)備掛載即可。一個(gè)簡單的demo xml為:

  
 
 
 
  1.  
  2.        
  3.        
  4.        
  5.       2ed1b04c-b34f-437d-9aa3-3feeb683d063 
  6.        
  7.  

可見source就是lun設(shè)備映射到本地的路徑。

值得一提的是,libvirt支持直接掛載rbd image(宿主機(jī)需要包含rbd內(nèi)核模塊),通過rbd協(xié)議訪問image,而不需要先map到宿主機(jī)本地,一個(gè)demo xml文件為:

  
 
 
 
  1.  
  2.        
  3.        
  4.          
  5.        
  6.        
  7.          
  8.          
  9.          
  10.        
  11.        
  12.        
  13.  

所以我們Cinder如果使用LVM driver,則需要先把LV加到iSCSI target中,然后映射到計(jì)算節(jié)點(diǎn)的宿主機(jī),而如果使用rbd driver,不需要映射到計(jì)算節(jié)點(diǎn),直接掛載即可。

以上介紹了存儲的一些基礎(chǔ)知識,有了這些知識,再去理解OpenStack nova和cinder就非常簡單了,接下來我們開始進(jìn)入我們的正式主題,分析OpenStack虛擬機(jī)掛載數(shù)據(jù)卷的流程。

3 OpenStack虛擬機(jī)掛載volume源碼分析

這里我們先以Ciner使用LVM driver為例,iSCSI驅(qū)動(dòng)使用lioadm,backend配置如下:

  
 
 
 
  1. [lvm] 
  2. iscsi_helper=lioadm 
  3. volume_driver=cinder.volume.drivers.lvm.LVMVolumeDriver 
  4. volume_backend_name=lvm 
  5. volume_group = cinder-volumes 

OpenStack源碼閱讀方法可以參考如何閱讀OpenStack源碼,這里不過多介紹。這里需要說明的是,Nova中有一個(gè)數(shù)據(jù)庫表專門用戶存儲數(shù)據(jù)卷和虛擬機(jī)的映射關(guān)系的,這個(gè)表名為block_device_mapping,其字段如下:

  
 
 
 
  1. MariaDB [nova]> desc block_device_mapping; 
  2. +-----------------------+--------------+------+-----+---------+----------------+ 
  3. | Field                 | Type         | Null | Key | Default | Extra          | 
  4. +-----------------------+--------------+------+-----+---------+----------------+ 
  5. | created_at            | datetime     | YES  |     | NULL    |                | 
  6. | updated_at            | datetime     | YES  |     | NULL    |                | 
  7. | deleted_at            | datetime     | YES  |     | NULL    |                | 
  8. | id                    | int(11)      | NO   | PRI | NULL    | auto_increment | 
  9. | device_name           | varchar(255) | YES  |     | NULL    |                | 
  10. | delete_on_termination | tinyint(1)   | YES  |     | NULL    |                | 
  11. | snapshot_id           | varchar(36)  | YES  | MUL | NULL    |                | 
  12. | volume_id             | varchar(36)  | YES  | MUL | NULL    |                | 
  13. | volume_size           | int(11)      | YES  |     | NULL    |                | 
  14. | no_device             | tinyint(1)   | YES  |     | NULL    |                | 
  15. | connection_info       | mediumtext   | YES  |     | NULL    |                | 
  16. | instance_uuid         | varchar(36)  | YES  | MUL | NULL    |                | 
  17. | deleted               | int(11)      | YES  |     | NULL    |                | 
  18. | source_type           | varchar(255) | YES  |     | NULL    |                | 
  19. | destination_type      | varchar(255) | YES  |     | NULL    |                | 
  20. | guest_format          | varchar(255) | YES  |     | NULL    |                | 
  21. | device_type           | varchar(255) | YES  |     | NULL    |                | 
  22. | disk_bus              | varchar(255) | YES  |     | NULL    |                | 
  23. | boot_index            | int(11)      | YES  |     | NULL    |                | 
  24. | image_id              | varchar(36)  | YES  |     | NULL    |                | 
  25. +-----------------------+--------------+------+-----+---------+----------------+ 

Cinder中也有一個(gè)單獨(dú)的表volume_attachment用來記錄掛載情況:

  
 
 
 
  1. MariaDB [cinder]> desc volume_attachment; 
  2. +---------------+--------------+------+-----+---------+-------+ 
  3. | Field         | Type         | Null | Key | Default | Extra | 
  4. +---------------+--------------+------+-----+---------+-------+ 
  5. | created_at    | datetime     | YES  |     | NULL    |       | 
  6. | updated_at    | datetime     | YES  |     | NULL    |       | 
  7. | deleted_at    | datetime     | YES  |     | NULL    |       | 
  8. | deleted       | tinyint(1)   | YES  |     | NULL    |       | 
  9. | id            | varchar(36)  | NO   | PRI | NULL    |       | 
  10. | volume_id     | varchar(36)  | NO   | MUL | NULL    |       | 
  11. | attached_host | varchar(255) | YES  |     | NULL    |       | 
  12. | instance_uuid | varchar(36)  | YES  |     | NULL    |       | 
  13. | mountpoint    | varchar(255) | YES  |     | NULL    |       | 
  14. | attach_time   | datetime     | YES  |     | NULL    |       | 
  15. | detach_time   | datetime     | YES  |     | NULL    |       | 
  16. | attach_mode   | varchar(36)  | YES  |     | NULL    |       | 
  17. | attach_status | varchar(255) | YES  |     | NULL    |       | 
  18. +---------------+--------------+------+-----+---------+-------+ 
  19. 13 rows in set (0.00 sec) 

接下來我們從nova-api開始一步步跟蹤其過程。

S1 nova-apinova-api

掛載volume入口為nova/api/openstack/compute/volumes.py,controller為VolumeAttachmentController,create就是虛擬機(jī)掛載volume的方法。

該方法首先檢查該volume是不是已經(jīng)掛載到這個(gè)虛擬機(jī)了:

  
 
 
 
  1. bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( 
  2.                 context, instance.uuid) 
  3. for bdm in bdms: 
  4.     if bdm.volume_id == volume_id: 
  5.         _msg = _("Volume %(volume_id)s have been attaced to " 
  6.                  "instance %(server_id)s.") % { 
  7.                  'volume_id': volume_id, 
  8.                  'server_id': server_id} 
  9.         raise exc.HTTPConflict(explanation=_msg) 

然后調(diào)用nova/compute/api.py的attach_volume方法,該方法的工作內(nèi)容為:

(1) create_volume_bdm()

即在block_device_mapping表中創(chuàng)建對應(yīng)的記錄,由于API節(jié)點(diǎn)無法拿到目標(biāo)虛擬機(jī)掛載后的設(shè)備名,比如/dev/vdb,只有計(jì)算節(jié)點(diǎn)才知道自己虛擬機(jī)映射到哪個(gè)設(shè)備。因此bdm不是在API節(jié)點(diǎn)創(chuàng)建的,而是通過RPC請求到虛擬機(jī)所在的計(jì)算節(jié)點(diǎn)創(chuàng)建,請求方法為reserve_block_device_name,該方法首先調(diào)用libvirt分配一個(gè)設(shè)備名,比如/dev/vdb,然后創(chuàng)建對應(yīng)的bdm實(shí)例。

(2) check_attach_and_reserve_volume()

這里包含check_attach和reserve_volume兩個(gè)過程,check_attach就是檢查這個(gè)volume能不能掛載,比如status必須為avaliable,或者支持多掛載情況下狀態(tài)為in-use或者avaliable。該方法位置為nova/volume/cinder.py的check_attach方法。而reserve_volume是由Cinder完成的,nova-api會(huì)調(diào)用cinder API。該方法其實(shí)不做什么工作,僅僅是把volume的status置為attaching。該方法流程:nova-api -> cinder-api -> reserver_volume,該方法位于cinder/volume/api.py:

  
 
 
 
  1. @wrap_check_policy 
  2. def reserve_volume(self, context, volume): 
  3.     expected = {'multiattach': volume.multiattach, 
  4.                 'status': (('available', 'in-use') if volume.multiattach 
  5.                            else 'available')} 
  6.  
  7.     result = volume.conditional_update({'status': 'attaching'}, expected) 
  8.  
  9.     if not result: 
  10.         expected_status = utils.build_or_str(expected['status']) 
  11.         msg = _('Volume status must be %s to reserve.') % expected_status 
  12.         LOG.error(msg) 
  13.         raise exception.InvalidVolume(reason=msg) 
  14.  
  15.     LOG.info(_LI("Reserve volume completed successfully."), 
  16.              resource=volume) 

(3) RPC計(jì)算節(jié)點(diǎn)的attach_volume()

此時(shí)nova-api會(huì)向目標(biāo)計(jì)算節(jié)點(diǎn)發(fā)起RPC請求,由于rpcapi.py的attach_volume方法調(diào)用的是cast方法,因此該RPC是異步調(diào)用。由此,nova-api的工作結(jié)束,剩下的工作由虛擬機(jī)所在的計(jì)算節(jié)點(diǎn)完成。

S2 nova-computenova-compute

接收到RPC請求,callback函數(shù)入口為nova/compute/manager.py的attach_volume方法,該方法會(huì)根據(jù)之前創(chuàng)建的bdm實(shí)例參數(shù)轉(zhuǎn)化為driver_block_device,然后調(diào)用該類的attach方法,這就已經(jīng)到了具體的硬件層,它會(huì)根據(jù)volume的類型實(shí)例化不同的具體類,這里我們的類型是volume,因此對應(yīng)為DriverVolumeBlockDevice,位于nova/virt/block_device.py。

我們看其attach方法,該方法是虛擬機(jī)掛載卷的最重要方法,也是實(shí)現(xiàn)的核心。該方法分好幾個(gè)階段,我們一個(gè)一個(gè)階段看。

(1) get_volume_connector()

該方法首先調(diào)用的是virt_driver.get_volume_connector(instance),其中virt_driver這里就是libvirt,該方法位于nova/virt/libvirt/driver.py,其實(shí)就是調(diào)用os-brick的get_connector_properties:

  
 
 
 
  1. def get_volume_connector(self, instance): 
  2.    root_helper = utils.get_root_helper() 
  3.    return connector.get_connector_properties( 
  4.        root_helper, CONF.my_block_storage_ip, 
  5.        CONF.libvirt.iscsi_use_multipath, 
  6.        enforce_multipath=True, 
  7.        host=CONF.host) 

os-brick是從Cinder項(xiàng)目分離出來的,專門用于管理各種存儲系統(tǒng)卷的庫,代碼倉庫為os-brick。其中g(shù)et_connector_properties方法位于os_brick/initiator/connector.py:

  
 
 
 
  1. def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath, 
  2.                              host=None): 
  3.     iscsi = ISCSIConnector(root_helper=root_helper) 
  4.     fc = linuxfc.LinuxFibreChannel(root_helper=root_helper) 
  5.  
  6.     props = {} 
  7.     props['ip'] = my_ip 
  8.     props['host'] = host if host else socket.gethostname() 
  9.     initiator = iscsi.get_initiator() 
  10.     if initiator: 
  11.         props['initiator'] = initiator 
  12.     wwpns = fc.get_fc_wwpns() 
  13.     if wwpns: 
  14.         props['wwpns'] = wwpns 
  15.     wwnns = fc.get_fc_wwnns() 
  16.     if wwnns: 
  17.         props['wwnns'] = wwnns 
  18.     props['multipath'] = (multipath and 
  19.                           _check_multipathd_running(root_helper, 
  20.                                                     enforce_multipath)) 
  21.     props['platform'] = platform.machine() 
  22.     props['os_type'] = sys.platform 
  23.     return props 

該方法最重要的工作就是返回該計(jì)算節(jié)點(diǎn)的信息(如ip、操作系統(tǒng)類型等)以及initiator name(參考第2節(jié)內(nèi)容)。

(2) volume_api.initialize_connection()

終于輪到Cinder真正干點(diǎn)活了!該方法會(huì)調(diào)用Cinder API的initialize_connection方法,該方法又會(huì)RPC請求給volume所在的cinder-volume服務(wù)節(jié)點(diǎn)。我們略去cinder-api,直接到cinder-volume。

S3 cinder-volume

代碼位置為cinder/volume/manager.py,該方法也是分階段的。

(1) driver.validate_connector()

該方法不同的driver不一樣,對于LVM + iSCSI來說,就是檢查有沒有initiator字段,即nova-compute節(jié)點(diǎn)的initiator信息,代碼位于cinder/volume/targets/iscsi.py:

  
 
 
 
  1. def validate_connector(self, connector): 
  2.    # NOTE(jdg): api passes in connector which is initiator info 
  3.    if 'initiator' not in connector: 
  4.        err_msg = (_LE('The volume driver requires the iSCSI initiator ' 
  5.                       'name in the connector.')) 
  6.        LOG.error(err_msg) 
  7.        raise exception.InvalidConnectorException(missing='initiator') 
  8.    return True 

注意以上代碼跳轉(zhuǎn)過程:drivers/lvm.py -> targets/lio.py -> targets/iscsi.py。即我們的lvm driver會(huì)調(diào)用target相應(yīng)的方法,這里我們用的是lio,因此調(diào)到lio.py,而lio又繼承自iscsi,因此跳到iscsi.py。下面分析將省去這些細(xì)節(jié)直接跳轉(zhuǎn)。

(2) driver.create_export()

該方法位于cinder/volume/targets/iscsi.py:

  
 
 
 
  1. def create_export(self, context, volume, volume_path): 
  2.     # 'iscsi_name': 'iqn.2010-10.org.openstack:volume-00000001' 
  3.     iscsi_name = "%s%s" % (self.configuration.iscsi_target_prefix, 
  4.                            volume['name']) 
  5.     iscsi_target, lun = self._get_target_and_lun(context, volume) 
  6.     chap_auth = self._get_target_chap_auth(context, iscsi_name) 
  7.     if not chap_auth: 
  8.         chap_auth = (vutils.generate_username(), 
  9.                      vutils.generate_password()) 
  10.  
  11.     # Get portals ips and port 
  12.     portals_config = self._get_portals_config() 
  13.     tid = self.create_iscsi_target(iscsi_name, 
  14.                                    iscsi_target, 
  15.                                    lun, 
  16.                                    volume_path, 
  17.                                    chap_auth, 
  18.                                    **portals_config) 
  19.     data = {} 
  20.     data['location'] = self._iscsi_location( 
  21.         self.configuration.iscsi_ip_address, tid, iscsi_name, lun, 
  22.         self.configuration.iscsi_secondary_ip_addresses) 
  23.     LOG.debug('Set provider_location to: %s', data['location']) 
  24.     data['auth'] = self._iscsi_authentication( 
  25.         'CHAP', *chap_auth)  本文題目:OpenStack虛擬機(jī)掛載數(shù)據(jù)卷過程分析
    文章分享:http://www.dlmjj.cn/article/djdshgo.html