Docker使用OpenStack Cinder持久化volume原理分析及實踐
1 背景知識
1.1 OpenStack Cinder簡介
OpenStack Cinder為OpenStack提供塊存儲服務(wù),其功能類似AWS的EBS服務(wù),目前使用最多的是為OpenStack Nova虛擬機提供虛擬硬盤功能,即把volume掛載到虛擬機中,作為附加彈性硬盤使用,關(guān)于OpenStack Cinder volume掛載到虛擬機的過程分析可以參考之前寫的博客OpenStack虛擬機掛載數(shù)據(jù)卷過程分析,這篇博客也是了解本文內(nèi)容的基礎(chǔ)。
但是,OpenStack Cinder不僅僅是為Nova虛擬機提供云硬盤功能,事實上,Cinder并不關(guān)心是誰在消費它的volume,除了虛擬機,還有可能是物理機和容器。Cinder volume掛載到物理機前面已經(jīng)介紹過,可以參考OpenStack中那些很少見但很有用的操作。Cinder volume掛載到虛擬機以及物理機都介紹過了,剩下最后一個內(nèi)容,Cinder volume如何掛載到Docker容器中呢,本文接下來將詳細(xì)介紹并通過兩個driver實例實踐。
1.2 Docker volume簡介
我們知道Docker容器本身是無狀態(tài)的,意味著容器退出后不會保存任何數(shù)據(jù)。但實際使用場景,肯定是需要保存業(yè)務(wù)數(shù)據(jù)的,Docker通過volume實現(xiàn)數(shù)據(jù)的持久化存儲以及共享。
默認(rèn)情況下,Docker會使用本地目錄作為容器的volume掛載到容器實例指定的路徑。用戶可以指定已經(jīng)存在的路徑作為Docker volume,如下:
- mkdir data
- docker run -t -i --rm -v `pwd`/data:/data busybox
以上把本地data目錄掛載到容器/data路徑中,注意源目錄路徑必須使用絕對路徑,否則Docker會當(dāng)作volume name。
你也可以不指定本地路徑,此時Docker會自動創(chuàng)建一個新的空目錄作為Docker volume:
- docker run -t -i --rm -v /data busybox
可以使用docker volume ls查看創(chuàng)建的volume:
- $ docker volume ls
- DRIVER VOLUME NAME
- local 0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788
通過inspect子命令查看源路徑path:
- $ docker volume inspect 0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788
- [
- {
- "CreatedAt": "2017-09-30T17:21:56+08:00",
- "Driver": "local",
- "Labels": null,
- "Mountpoint": "/var/lib/docker/volumes/0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788/_data",
- "Name": "0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788",
- "Options": {},
- "Scope": "local"
- }
- ]
從以上輸出的結(jié)果可看出本地源目錄為/var/lib/docker/volumes/0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788/_data,這個目錄是Docker自動創(chuàng)建的。
由此我們也得出結(jié)論,Docker創(chuàng)建的volume只能用于當(dāng)前宿主機的容器使用,不能掛載到其它宿主機的容器中,這種情況下只能運行些無狀態(tài)服務(wù),對于需要滿足HA的有狀態(tài)服務(wù),則需要使用分布式共享volume持久化數(shù)據(jù),保證宿主機掛了后,容器能夠遷移到另一臺宿主機中。而Docker本身并沒有提供分布式共享存儲方案,而是通過插件(plugin)機制實現(xiàn)與第三方存儲系統(tǒng)對接集成,下節(jié)我們詳細(xì)介紹。
1.3 Docker volume plugin介紹
前面提到Docker本身并沒有提供分布式共享volume方案實現(xiàn),而是提供了一種靈活的插件機制,通過插件能夠集成第三方的分布式共享系統(tǒng),用戶只需要實現(xiàn)plugin driver的接口就可以對接自己的任何存儲系統(tǒng)。如當(dāng)前非常流行的開源分布式存儲系統(tǒng)Ceph、AWS EBS、OpenStack Cinder等,這些外部存儲系統(tǒng)我們稱為Provider。
值得一提的是,官方在volume plugin協(xié)議文檔中強調(diào):
- If a plugin registers itself as a VolumeDriver when activated, it must provide the Docker Daemon with writeable paths on the host filesystem.
這句話的理解就是說,Docker不能直接讀寫外部存儲系統(tǒng),而必須把存儲系統(tǒng)掛載到宿主機的本地文件系統(tǒng)中,Docker當(dāng)作本地目錄掛載到容器中,換句話說,只要外部存儲設(shè)備能夠掛載到本地文件系統(tǒng)就可以作為Docker的volume。比如對于ceph rbd,需要先map到本地,并掛載到宿主機指定的路徑中,這個路徑稱為path。這里和虛擬機不一樣,rbd掛載到虛擬機,QEMU能夠直接通過rbd協(xié)議讀寫,不需要map到本地。
我們要想了解Docker掛載分布式存儲系統(tǒng)的原理,首先需要了解下官方定義的plugin協(xié)議接口:
- create: 創(chuàng)建一個volume。
- remove: 刪除一個volume。
- mount: 掛載一個volume到容器中。
- umount: 從容器中卸載一個volume。
- get/list: 獲取volume信息。
以上create和remove都比較簡單,最為核心的兩個接口為mount和umount,不同的存儲系統(tǒng),接口實現(xiàn)不一樣,我們這里只關(guān)心Cinder接口的實現(xiàn)。在此之前我沒有過多研究,不妨我們就用前面了解的知識大膽猜想下Docker使用Cinder volume的實現(xiàn)原理。
1.4 Docker使用Cinder volume原理猜想
前面我們介紹了Docker plugin接口,現(xiàn)在假設(shè)我們需要對接OpenStack Cinder,Cinder存儲后端(backend)使用LVM,猜測Docker plugin接口實現(xiàn)如下:
- create: 直接調(diào)用Cinder API創(chuàng)建一個volume。
- remote: 直接調(diào)用Cinder API刪除一個volume。
- get/list: 直接調(diào)用Cinder API獲取volume列表。
- mount: 前面提到Docker volume必須先掛載到本地,而這不正是恰好對應(yīng)Cinder的local-attach么,具體內(nèi)容可以參考OpenStack中那些很少見但很有用的操作。local attach到本地設(shè)備后,如果塊設(shè)備沒有安裝文件系統(tǒng),則mount操作還需要執(zhí)行文件系統(tǒng)格式化。創(chuàng)建完文件系統(tǒng)后,只需要mount到宿主機文件系統(tǒng)就可以了,Docker并不關(guān)心底層到底是什么存儲系統(tǒng),它只是把它當(dāng)作宿主機的一個目錄,剩下的工作就和Docker掛載本地目錄一樣了。
- umount: 不需要解釋,已經(jīng)非常明了,只需要從本地文件系統(tǒng)umount,然后從本地設(shè)備detach。
目前Docker掛載Cinder volume的方案還挺多的,如:
- docker cinder driver: Docker Volume Plugin to enable consumption of OpenStack-Cinder Block Storage with containers.
- fuxi: Enable Docker container to use Cinder volume and Manila share.
- REX-Ray:storage management solution designed to support container runtimes such as Docker and Mesos.
- Flocker: Flocker is an open-source container data volume orchestrator for your Dockerized applications.
以上原理只是我們的猜想,猜想是不是成立,我們接下來通過以上方案研究實踐下即可驗證。
2 docker cinder driver實踐
2.1 docker cinder driver簡介
docker-cinder-driver是由john griffith開發(fā)的,實現(xiàn)了Docker掛載Cinder卷Driver。作者還寫了篇專門的博客介紹Cinder - Block Storage for things other than Nova,也可以參考作者于OpenStack Days East 2016的分享pptslides: Consuming Cinder from Docker以及2016年奧斯汀分享視頻cinder and docker like peanut butter and chocolate。
2.2 環(huán)境準(zhǔn)備
實驗之前本人已經(jīng)使用DevStack工具部署了一個allinone OpenStack測試環(huán)境,代碼基于最新的master分支,對應(yīng)Cinder commit為2b58f2bb04c229c738b5cc806575ed3503fd1bfe。 Cinder使用LVM后端存儲(backend),配置如下:
- [lvmdriver-1]
- image_volume_cache_enabled = True
- volume_clear = zero
- lvm_type = auto
- iscsi_helper = tgtadm
- volume_group = stack-volumes-lvmdriver-1
- volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
- volume_backend_name = lvmdriver-1
后續(xù)操作都是在這個DevStack環(huán)境中進行,不再強調(diào)。
docker cinder driver文檔中說可以直接通過install.sh腳本下載:
- curl -sSl https://raw.githubusercontent.com/j-griffith/cinder-docker-driver/master/install.sh | sh
但這樣下載的可能不是最新代碼編譯的(親測有坑),為了使用最新的版本,我們只能手動編譯,首先需要安裝go開發(fā)環(huán)境,關(guān)于go語言開發(fā)環(huán)境可以參考官方安裝文檔。
ubuntu可以直接使用apt-get安裝:
- sudo apt-get install golang-go
下載cinder-docker-driver源碼到本地:
- git clone https://github.com/j-griffith/cinder-docker-driver
使用go build直接編譯:
- cd cinder-docker-driver
- mkdir -p vendor/src
- ln -s `pwd`/vendor/golang.org/ vendor/src
- ln -s `pwd`/vendor/github.com vendor/src
- export GOPATH=`pwd`/vendor
- go build
創(chuàng)建配置文件,主要包含Cinder的認(rèn)證信息:
- mkdir -p /var/lib/cinder/dockerdriver
- cat >/var/lib/cinder/dockerdriver/config.json <<EOF
- {
- "Endpoint": "http://10.0.2.15/identity/v3",
- "Username": "admin",
- "Password": "nomoresecret",
- "TenantID": "ae21d957967d4df0865411f0389ed7e8",
- "DomainName": "Default",
- "Region": "RegionOne"
- }
- EOF
其中Endpoint為認(rèn)證URL,注意包含版本/v3,且必須包含DomainName配置項。
配置完成后就可以直接運行cinder-docker-driver服務(wù)了:
- nohup ./cinder-docker-driver &
- tailf ./nohup
2.3 功能驗證
使用docker創(chuàng)建一個volume,如下:
- root@devstack:~# docker volume create -d cinder --name int32bit-test-1 -o size=2
- int32bit-test-1
- root@devstack:~# docker volume ls
- DRIVER VOLUME NAME
- cinder int32bit-test-1
啟動一個容器并掛載int32bit-test-1:
- root@devstack:~# docker run -t -i --rm -v int32bit-test-1:/int32bit-test-1 busybox
- / # cd /int32bit-test-1/
- /int32bit-test-1 # ls
- lost+found
- /int32bit-test-1 # echo "HelloWorld" >hello.txt
- /int32bit-test-1 # ls
- hello.txt lost+found
- /int32bit-test-1 #
以上我們掛載剛剛創(chuàng)建的volume到/int32bit-test-1中,并寫了HelloWorld到hello.txt文件中。
啟動容器時cinder-docker-driver日志如下:
- time="2017-09-29T21:29:44+08:00" level=debug msg="Found Volume ID: 58837c2b-af79-4f89-97ea-40e2622d2c52"
- time="2017-09-29T21:29:44+08:00" level=debug msg="Gather up initiator IQNs..."
- time="2017-09-29T21:29:44+08:00" level=debug msg="Found the following iqns: [iqn.1993-08.org.debian:01:19a8a9ca754f]"
- time="2017-09-29T21:29:44+08:00" level=debug msg="Value of IPs is=[127.0.0.1/8 10.0.2.15/24 192.168.99.101/24 192.168.122.1/24 172.17.0.1/16 ::1/128 fe80::a00:27ff:fe94:2f20/64 fe80::a00:27ff:fe69:2326/64 fe80::42:bcff:fee4:89ac/64]\n"
- time="2017-09-29T21:29:44+08:00" level=debug msg="Issue InitializeConnection..."
- time="2017-09-29T21:29:47+08:00" level=debug msg="Create the node entry using args: [-m node -T iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52 -p 10.0.2.15:3260]"
- time="2017-09-29T21:29:47+08:00" level=debug msg="Update username to: 36eDQkERhAAKXGi8CMFC"
- time="2017-09-29T21:29:47+08:00" level=debug msg="Update password to: GLFkwC6eV8abbtk8"
- time="2017-09-29T21:29:48+08:00" level=info msg="Logged into iSCSI target without error: [-m node -T iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52 -p 10.0.2.15:3260 --login]"
- time="2017-09-29T21:29:48+08:00" level=info msg="Waiting for path"
- time="2017-09-29T21:29:49+08:00" level=debug msg="path found: /dev/disk/by-path/ip-10.0.2.15:3260-iscsi-iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52-lun-1"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Begin utils.getDeviceFileFromIscsiPath: /dev/disk/by-path/ip-10.0.2.15:3260-iscsi-iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52-lun-1"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Found device: [lrwxrwxrwx 1 root root 9 Sep 29 21:29 /dev/disk/by-path/ip-10.0.2.15:3260-iscsi-iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52-lun-1 -> sdd\n]"
- time="2017-09-29T21:29:49+08:00" level=debug msg="using base of: /dev/sdd"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Attached volume at (path, devfile): /dev/disk/by-path/ip-10.0.2.15:3260-iscsi-iqn.2010-10.org.openstack:volume-58837c2b-af79-4f89-97ea-40e2622d2c52-lun-1, /dev/sdd"
- time="2017-09-29T21:29:49+08:00" level=debug msg="iSCSI connection done"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Begin utils.GetFSType: /dev/sdd"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Formatting device"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Begin utils.FormatVolume: /dev/sdd, ext4"
- time="2017-09-29T21:29:49+08:00" level=debug msg="Perform mkfs.ext4 on device: /dev/sdd"
- time="2017-09-29T21:29:50+08:00" level=debug msg="Result of mkfs cmd: mke2fs 1.42.13 (17-May-2015)\nCreating filesystem with 524288 4k blocks and 131072 inodes\nFilesystem UUID: 02318688-7448-4d25-98dd-0527a2bd9733
- Superblock backups stored on blocks:
- 32768, 98304, 163840, 229376, 294912
- Allocating group tables: 0/16 done
- Writing inode tables: 0/16 done
- Creating journal (16384 blocks): done
- Writing superblocks and filesystem accounting information: 0/16 done"
- time="2017-09-29T21:29:50+08:00" level=debug msg="Begin utils.Mount device: /dev/sdd on: /var/lib/cinder/mount/int32bit-test-1"
- time="2017-09-29T21:29:50+08:00" level=debug msg="Response from mount /dev/sdd at /var/lib/cinder/mount/int32bit-test-1: "
- time="2017-09-29T21:29:50+08:00" level=debug msg="Call gophercloud Attach..."
- time="2017-09-29T21:29:50+08:00" level=debug msg="Attach results: {ErrResult:{Result:{Body:<nil> Header:map[] Err:<nil>}}}"
從日志中可以看出掛載volume本質(zhì)就是通過iscsi把volume attach到本地(local attach),格式化為ext4文件系統(tǒng),然后掛載到宿主機/var/lib/cinder/mount目錄中,與我們猜想過程基本一致。
可以通過lsblk確認(rèn):
- root@devstack:~/cinder-docker-driver# lsblk -s | grep int32bit-test
- sdd 8:48 0 2G 0 disk /var/lib/cinder/mount/int32bit-test-1
從docker容器實例中退出,此時會自動把volume從本地detach。
我們使用cinder把創(chuàng)建的卷手動attach到本地并掛載,關(guān)于Cinder的local attach,可參考OpenStack中那些很少見但很有用的操作。
- root@devstack:~# cinder list
- +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+
- | ID | Status | Name | Size | Volume Type | Bootable | Attached to |
- +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+
- | 58837c2b-af79-4f89-97ea-40e2622d2c52 | available | int32bit-test-1 | 2 | lvmdriver-1 | false | |
- +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+
- root@devstack:~# cinder local-attach 58837c2b-af79-4f89-97ea-40e2622d2c52
- +----------+-----------------------------------+
- | Property | Value |
- +----------+-----------------------------------+
- | path | /dev/sdd |
- | scsi_wwn | 360000000000000000e00000000010001 |
- | type | block |
- +----------+-----------------------------------+
- root@devstack:~# mount /dev/sdd /mnt
查看前面我們寫的文件:
- root@devstack:~# cat /mnt/hello.txt
- HelloWorld
可見輸出了我們通過容器寫的HelloWorld。
通過docker cinder driver基本驗證了我們之前的猜想是正確的。
3 fuxi
3.1 fuxi項目簡介
OpenStack fuxi是一個比較新的項目,最初是從magnum項目分離出來,于2016年2月26號被OpenStack社區(qū)接受成為社區(qū)項目,目前主要由華為主導(dǎo)開發(fā),其目標(biāo)是使Docker容器可以使用Cinder volume和Manila share作為持久化存儲卷。
3.2 環(huán)境準(zhǔn)備
OpenStack環(huán)境仍然使用之前的DevStack環(huán)境,fuxi安裝過程如下。
首先安裝依賴的包,這些包其實DevStack基本都已經(jīng)安裝完成了。
- sudo apt-get update
- sudo apt-get install python-dev git libffi-dev libssl-dev gcc
- sudo apt-get install open-iscsi
- sudo apt-get install sysfsutils
下載fuxi源碼并安裝:
- git clone https://github.com/openstack/fuxi.git
- cd fuxi
- sudo pip install -r requirements.txt
- sudo python setup.py install
- ln -s /lib/udev/scsi_id /usr/local/bin # for root
使用generate_config_file_samples.sh生成配置文件模板,并拷貝到/etc/fuxi目錄下。
- ./tools/generate_config_file_samples.sh
- sudo cp etc/fuxi.conf.sample /etc/fuxi/fuxi.conf
修復(fù)配置文件,最終配置文件如下:
- root@devstack:~# cat /etc/fuxi/fuxi.conf | grep -v '^#' | grep -v '^$'
- [DEFAULT]
- my_ip = 10.0.2.15
- volume_providers = cinder
- [cinder]
- region_name = RegionOne
- volume_connector = osbrick
- fstype = ext4
- auth_url = http://10.0.2.15/identity/v3
- project_name = admin
- project_domain_name = Default
- username = admin
- user_domain_name = Default
- password = nomoresecret
- [keystone]
- [manila]
- volume_connector = osbrick
- auth_type = password
- [nova]
注意auth_url必須包含版本,如/v3。
啟動服務(wù):
- fuxi-server --config-file /etc/fuxi/fuxi.conf
3.3 功能驗證
使用Docker創(chuàng)建一個volume:
- $ docker volume create -d fuxi --name int32bit-test-fuxi
- int32bit-test-fuxi
- $ docker volume ls | grep int32bit-test-fuxi
- fuxi int32bit-test-fuxi
掛載volume到Docker容器中:
- $ docker run -ti --rm -v int32bit-test-fuxi:/int32bit-test-fuxi busybox
- / # cd /int32bit-test-fuxi/
- /int32bit-test-fuxi # ls
- a b c lost+found
我們可以驗證volume其實是映射到本地路徑的:
- $ lsblk -Sf
- NAME HCTL TYPE VENDOR MODEL REV TRAN NAME FSTYPE LABEL UUID MOUNTPOINT
- sda 2:0:0:0 disk ATA VBOX HARDDISK 1.0 sata sda
- sdb 11:0:0:1 disk IET VIRTUAL-DISK 0001 iscsi sdb ext4 d04b16a1-3392-41df-999f-e6c36b5d0cd6 /fuxi/data/cinder/int32bit-test-fuxi
- sr0 1:0:0:0 rom VBOX CD-ROM 1.0 ata sr0
由此可見,fuxi首先把Volume attach到本地,并mount到指定路徑中,然后mount到Docker容器中,又和我們的猜想一致,接下來我們從源碼角度分析。
3.4 Docker使用fuxi掛載volume源碼分析
fuxi掛載是通過fuxi/volumeprovider/cinder.py模塊的Cinder類實現(xiàn)的,該類實現(xiàn)了provider.Provider接口,而該接口就是對應(yīng)前面介紹的Docker volume plugin接口。我們主要研究其mount方法:
- t(self, docker_volume_name):
- cinder_volume, state = self._get_docker_volume(docker_volume_name)
- LOG.info("Get docker volume %(d_v)s %(vol)s with state %(st)s",
- {'d_v': docker_volume_name, 'vol': cinder_volume,
- 'st': state})
- connector = self._get_connector()
- if state == NOT_ATTACH:
- connector.connect_volume(cinder_volume)
- elif state == ATTACH_TO_OTHER:
- if cinder_volume.multiattach:
- connector.connect_volume(cinder_volume)
- else:
- msg = _("Volume {0} {1} is not shareable").format(
- docker_volume_name, cinder_volume)
- raise exceptions.FuxiException(msg)
- elif state != ATTACH_TO_THIS:
- msg = _("Volume %(vol_name)s %(c_vol)s is not in correct state, "
- "current state is %(state)s")
- LOG.error(msg, {'vol_name': docker_volume_name,
- 'c_vol': cinder_volume,
- 'state': state})
- raise exceptions.NotMatchedState()
以上主要通過Cinder API獲取volume信息,檢查其attach情況:
- 如果volume沒有attach,則直接attach。
- 如果volume已經(jīng)attach(in-use)到其它主機,則檢查其是否支持multiattach,如果支持多掛載,則直接掛載,否則拋出異常,掛載失敗。
- 如果volume已經(jīng)attach到當(dāng)前主機,則說明已經(jīng)掛載到本地了,但這不是我們所期望的,因此直接拋出異常。
假設(shè)前面都沒有問題,順利把volume attach到本地,則我們可以獲取映射到本地的虛擬設(shè)備名,接下來的代碼就是檢查該路徑是否就緒:
- link_path = connector.get_device_path(cinder_volume)
- if not os.path.exists(link_path):
- LOG.warning("Could not find device link file, "
- "so rebuild it")
- connector.disconnect_volume(cinder_volume)
- connector.connect_volume(cinder_volume)
- devpath = os.path.realpath(link_path)
- if not devpath or not os.path.exists(devpath):
- msg = _("Can't find volume device path")
- LOG.error(msg)
- raise exceptions.FuxiException(msg)
如果前面順利獲取的volume到設(shè)備名,比如/dev/sdd,則最后的工作就是mount到本地文件系統(tǒng)了:
- mountpoint = self._get_mountpoint(docker_volume_name)
- self._create_mountpoint(mountpoint)
- fstype = cinder_volume.metadata.get('fstype', cinder_conf.fstype)
- mount.do_mount(devpath, mountpoint, fstype)
- return mountpoint
其中mountpoint是掛載的目標(biāo)目錄,其路徑為volume_dir + volume_type + volume_name,其中volume_dir通過配置文件配置,默認(rèn)為/fuxi/data,volume_type這里為cinder,假設(shè)volume name為int32bit-test-volume,則掛載路徑為/fuxi/data/cinder/int32bit-test-volume。
create_mountpoint就是創(chuàng)建掛載目錄:
- (self, mountpoint):
- """Create mount point directory for Docker volume.
- :param mountpoint: The path of Docker volume.
- """
- try:
- if not os.path.exists(mountpoint) or not os.path.isdir(mountpoint):
- utils.execute('mkdir', '-p', '-m=755', mountpoint,
- run_as_root=True)
- LOG.info("Create mountpoint %s successfully", mountpoint)
- except processutils.ProcessExecutionError as e:
- LOG.error("Error happened when create volume "
- "directory. Error: %s", e)
最后調(diào)用mount.do_mount,mount是fuxi實現(xiàn)的一個通用的mount庫,代碼位于fuxi/common/mount.py。
- (devpath, mountpoint, fstype):
- """Execute device mount operation.
- :param devpath: The path of mount device.
- :param mountpoint: The path of mount point.
- :param fstype: The file system type.
- """
- try:
- if check_already_mounted(devpath, mountpoint):
- return
- mounter = Mounter()
- mounter.mount(devpath, mountpoint, fstype)
- except exceptions.MountException:
- try:
- mounter.make_filesystem(devpath, fstype)
- mounter.mount(devpath, mountpoint, fstype)
- except exceptions.FuxiException as e:
- with excutils.save_and_reraise_exception():
- LOG.error(str(e))
該方法直接調(diào)用Mounter的mount方法,如果mount失敗,則重新創(chuàng)建格式化文件系統(tǒng)后再次掛載(第一次掛載時沒有安裝文件系統(tǒng),因此mount必然失敗)。mount方法如下:
- (self, devpath, mountpoint, fstype=None):
- try:
- if fstype:
- utils.execute('mount', '-t', fstype, devpath, mountpoint,
- run_as_root=True)
- else:
- utils.execute('mount', devpath, mountpoint,
- run_as_root=True)
- except processutils.ProcessExecutionError as e:
- msg = _("Unexpected error while mount block device. "
- "Devpath: {0}, "
- "Mountpoint: {1} "
- "Error: {2}").format(devpath, mountpoint, e)
- raise exceptions.MountException(msg)
由此我們通過研究源碼,再次驗證了我們之前的猜想是正確的。
4 REX-Ray
REX-Ray是一個EMC團隊領(lǐng)導(dǎo)的開源項目,為Docker、Mesos及其他容器運行環(huán)境提供持續(xù)的存儲訪問。其設(shè)計旨在囊括通用存儲、虛擬化和云平臺,提供高級的存儲功能。換句話說,REX-Ray進一步封裝,提供一個統(tǒng)一的為Docker提供volume的工具,整合了各種不同的provide,如Ceph、Cinder、EBS等。但遺憾的是,目前Docker掛載Cinder卷,Docker必須安裝在Nova虛擬機中,虛擬機還必須能夠和OpenStack管理網(wǎng)打通,參考Cinder: failed to attach volume while using cinder driver,因此實際使用場景有限,本文不再詳細(xì)介紹。
【本文是51CTO專欄作者“付廣平”的原創(chuàng)文章,如需轉(zhuǎn)載請通過51CTO獲得聯(lián)系】