Docker使用OpenStackCinder持久化volume原理分析及实践

docker使用OpenStack Cinder持久化volume原理分析及实践

作者:付广平 2017-10-20 15:25:17

开发

开发工具

云计算

OpenStack Docker容器本身是无状态的,意味着容器退出后不会保存任何数据。但实际使用场景,肯定是需要保存业务数据的,Docker通过volume实现数据的持久化存储以及共享。

创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的马山网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

 1 背景知识

1.1 OpenStack Cinder简介

OpenStack Cinder为OpenStack提供块存储服务,其功能类似AWS的EBS服务,目前使用最多的是为OpenStack Nova虚拟机提供虚拟硬盘功能,即把volume挂载到虚拟机中,作为附加弹性硬盘使用,关于OpenStack Cinder volume挂载到虚拟机的过程分析可以参考之前写的博客OpenStack虚拟机挂载数据卷过程分析,这篇博客也是了解本文内容的基础。

但是,OpenStack Cinder不仅仅是为Nova虚拟机提供云硬盘功能,事实上,Cinder并不关心是谁在消费它的volume,除了虚拟机,还有可能是物理机和容器。Cinder volume挂载到物理机前面已经介绍过,可以参考OpenStack中那些很少见但很有用的操作。Cinder volume挂载到虚拟机以及物理机都介绍过了,剩下最后一个内容,Cinder volume如何挂载到Docker容器中呢,本文接下来将详细介绍并通过两个driver实例实践。

1.2 Docker volume简介

我们知道Docker容器本身是无状态的,意味着容器退出后不会保存任何数据。但实际使用场景,肯定是需要保存业务数据的,Docker通过volume实现数据的持久化存储以及共享。

默认情况下,Docker会使用本地目录作为容器的volume挂载到容器实例指定的路径。用户可以指定已经存在的路径作为Docker volume,如下:

  
 
 
  1. mkdir data 
  2. docker run -t -i --rm -v `pwd`/data:/data busybox 

以上把本地data目录挂载到容器/data路径中,注意源目录路径必须使用绝对路径,否则Docker会当作volume name。

你也可以不指定本地路径,此时Docker会自动创建一个新的空目录作为Docker volume:

  
 
 
  1. docker run -t -i --rm -v /data busybox 

可以使用docker volume ls查看创建的volume:

  
 
 
  1. $ docker volume ls 
  2. DRIVER              VOLUME NAME 
  3. local               0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788 

通过inspect子命令查看源路径path:

  
 
 
  1. $ docker volume inspect 0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788 
  2.     { 
  3.         "CreatedAt": "2017-09-30T17:21:56+08:00", 
  4.         "Driver": "local", 
  5.         "Labels": null, 
  6.         "Mountpoint": "/var/lib/docker/volumes/0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788/_data", 
  7.         "Name": "0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788", 
  8.         "Options": {}, 
  9.         "Scope": "local" 
  10.     } 

从以上输出的结果可看出本地源目录为/var/lib/docker/volumes/0e8d4d3936ec3b84c2ee4db388f45cbe5c84194d89d69be6b7a616fbdf1ea788/_data,这个目录是Docker自动创建的。

由此我们也得出结论,Docker创建的volume只能用于当前宿主机的容器使用,不能挂载到其它宿主机的容器中,这种情况下只能运行些无状态服务,对于需要满足HA的有状态服务,则需要使用分布式共享volume持久化数据,保证宿主机挂了后,容器能够迁移到另一台宿主机中。而Docker本身并没有提供分布式共享存储方案,而是通过插件(plugin)机制实现与第三方存储系统对接集成,下节我们详细介绍。

1.3 Docker volume plugin介绍

前面提到Docker本身并没有提供分布式共享volume方案实现,而是提供了一种灵活的插件机制,通过插件能够集成第三方的分布式共享系统,用户只需要实现plugin driver的接口就可以对接自己的任何存储系统。如当前非常流行的开源分布式存储系统Ceph、AWS EBS、OpenStack Cinder等,这些外部存储系统我们称为Provider。

值得一提的是,官方在volume plugin协议文档中强调:

  • If a plugin registers itself as a VolumeDriver when activated, it must provide the Docker Daemon with writeable paths on the host filesystem.

这句话的理解就是说,Docker不能直接读写外部存储系统,而必须把存储系统挂载到宿主机的本地文件系统中,Docker当作本地目录挂载到容器中,换句话说,只要外部存储设备能够挂载到本地文件系统就可以作为Docker的volume。比如对于ceph rbd,需要先map到本地,并挂载到宿主机指定的路径中,这个路径称为path。这里和虚拟机不一样,rbd挂载到虚拟机,QEMU能够直接通过rbd协议读写,不需要map到本地。

我们要想了解Docker挂载分布式存储系统的原理,首先需要了解下官方定义的plugin协议接口:

  • create: 创建一个volume。
  • remove: 删除一个volume。
  • mount: 挂载一个volume到容器中。
  • umount: 从容器中卸载一个volume。
  • get/list: 获取volume信息。

以上create和remove都比较简单,最为核心的两个接口为mount和umount,不同的存储系统,接口实现不一样,我们这里只关心Cinder接口的实现。在此之前我没有过多研究,不妨我们就用前面了解的知识大胆猜想下Docker使用Cinder volume的实现原理。

1.4 Docker使用Cinder volume原理猜想

前面我们介绍了Docker plugin接口,现在假设我们需要对接OpenStack Cinder,Cinder存储后端(backend)使用LVM,猜测Docker plugin接口实现如下:

  • create: 直接调用Cinder API创建一个volume。
  • remote: 直接调用Cinder API删除一个volume。
  • get/list: 直接调用Cinder API获取volume列表。
  • mount: 前面提到Docker volume必须先挂载到本地,而这不正是恰好对应Cinder的local-attach么,具体内容可以参考OpenStack中那些很少见但很有用的操作。local attach到本地设备后,如果块设备没有安装文件系统,则mount操作还需要执行文件系统格式化。创建完文件系统后,只需要mount到宿主机文件系统就可以了,Docker并不关心底层到底是什么存储系统,它只是把它当作宿主机的一个目录,剩下的工作就和Docker挂载本地目录一样了。
  • umount: 不需要解释,已经非常明了,只需要从本地文件系统umount,然后从本地设备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开发的,实现了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 环境准备

实验之前本人已经使用DevStack工具部署了一个allinone OpenStack测试环境,代码基于最新的master分支,对应Cinder commit为2b58f2bb04c229c738b5cc806575ed3503fd1bfe。 Cinder使用LVM后端存储(backend),配置如下:

  
 
 
  1. [lvmdriver-1] 
  2. image_volume_cache_enabled = True 
  3. volume_clear = zero 
  4. lvm_type = auto 
  5. iscsi_helper = tgtadm 
  6. volume_group = stack-volumes-lvmdriver-1 
  7. volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver 
  8. volume_backend_name = lvmdriver-1 

后续操作都是在这个DevStack环境中进行,不再强调。

docker cinder driver文档中说可以直接通过install.sh脚本下载:

  
 
 
  1. curl -sSl https://raw.githubusercontent.com/j-griffith/cinder-docker-driver/master/install.sh | sh 

但这样下载的可能不是最新代码编译的(亲测有坑),为了使用最新的版本,我们只能手动编译,首先需要安装go开发环境,关于go语言开发环境可以参考官方安装文档。

ubuntu可以直接使用apt-get安装:

  
 
 
  1. sudo apt-get install golang-go 

下载cinder-docker-driver源码到本地:

  
 
 
  1. git clone https://github.com/j-griffith/cinder-docker-driver 

使用go build直接编译:

  
 
 
  1. cd cinder-docker-driver 
  2. mkdir -p vendor/src 
  3. ln -s `pwd`/vendor/golang.org/ vendor/src 
  4. ln -s `pwd`/vendor/github.com vendor/src 
  5. export GOPATH=`pwd`/vendor 
  6. go build 

创建配置文件,主要包含Cinder的认证信息:

  
 
 
  1. mkdir -p /var/lib/cinder/dockerdriver 
  2. cat >/var/lib/cinder/dockerdriver/config.json <
  3.   "Endpoint": "http://10.0.2.15/identity/v3", 
  4.   "Username": "admin", 
  5.   "Password": "nomoresecret", 
  6.   "TenantID": "ae21d957967d4df0865411f0389ed7e8", 
  7.   "DomainName": "Default", 
  8.   "Region": "RegionOne" 
  9. EOF 

其中Endpoint为认证URL,注意包含版本/v3,且必须包含DomainName配置项。

配置完成后就可以直接运行cinder-docker-driver服务了:

  
 
 
  1. nohup ./cinder-docker-driver &  
  2. tailf ./nohup 

2.3 功能验证

使用docker创建一个volume,如下:

  
 
 
  1. root@devstack:~# docker volume create -d cinder --name int32bit-test-1 -o size=2 
  2. int32bit-test-1 
  3. root@devstack:~# docker volume ls 
  4. DRIVER              VOLUME NAME 
  5. cinder              int32bit-test-1 

启动一个容器并挂载int32bit-test-1:

  
 
 
  1. root@devstack:~# docker run -t -i --rm -v int32bit-test-1:/int32bit-test-1 busybox 
  2. / # cd /int32bit-test-1/ 
  3. /int32bit-test-1 # ls 
  4. lost+found 
  5. /int32bit-test-1 # echo "HelloWorld" >hello.txt 
  6. /int32bit-test-1 # ls 
  7. hello.txt   lost+found 
  8. /int32bit-test-1 # 

以上我们挂载刚刚创建的volume到/int32bit-test-1中,并写了HelloWorld到hello.txt文件中。

启动容器时cinder-docker-driver日志如下:

  
 
 
  1. time="2017-09-29T21:29:44+08:00" level=debug msg="Found Volume ID: 58837c2b-af79-4f89-97ea-40e2622d2c52" 
  2. time="2017-09-29T21:29:44+08:00" level=debug msg="Gather up initiator IQNs..." 
  3. time="2017-09-29T21:29:44+08:00" level=debug msg="Found the following iqns: [iqn.1993-08.org.debian:01:19a8a9ca754f]" 
  4. 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" 
  5. time="2017-09-29T21:29:44+08:00" level=debug msg="Issue InitializeConnection..." 
  6. 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]" 
  7. time="2017-09-29T21:29:47+08:00" level=debug msg="Update username to: 36eDQkERhAAKXGi8CMFC" 
  8. time="2017-09-29T21:29:47+08:00" level=debug msg="Update password to: GLFkwC6eV8abbtk8" 
  9. 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]" 
  10. time="2017-09-29T21:29:48+08:00" level=info msg="Waiting for path" 
  11. 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" 
  12. 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" 
  13. 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]" 
  14. time="2017-09-29T21:29:49+08:00" level=debug msg="using base of: /dev/sdd" 
  15. 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" 
  16. time="2017-09-29T21:29:49+08:00" level=debug msg="iSCSI connection done" 
  17. time="2017-09-29T21:29:49+08:00" level=debug msg="Begin utils.GetFSType: /dev/sdd" 
  18. time="2017-09-29T21:29:49+08:00" level=debug msg="Formatting device" 
  19. time="2017-09-29T21:29:49+08:00" level=debug msg="Begin utils.FormatVolume: /dev/sdd, ext4" 
  20. time="2017-09-29T21:29:49+08:00" level=debug msg="Perform mkfs.ext4 on device: /dev/sdd" 
  21. 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 
  22. Superblock backups stored on blocks:  
  23.     32768, 98304, 163840, 229376, 294912 
  24.     Allocating group tables: 0/16 done                             
  25.     Writing inode tables:  0/16 done                             
  26.     Creating journal (16384 blocks): done 
  27.     Writing superblocks and filesystem accounting information:  0/16 done" 
  28. 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" 
  29. time="2017-09-29T21:29:50+08:00" level=debug msg="Response from mount /dev/sdd at /var/lib/cinder/mount/int32bit-test-1: " 
  30. time="2017-09-29T21:29:50+08:00" level=debug msg="Call gophercloud Attach..." 
  31. time="2017-09-29T21:29:50+08:00" level=debug msg="Attach results: {ErrResult:{Result:{Body: Header:map[] Err:}}}" 

从日志中可以看出挂载volume本质就是通过iscsi把volume attach到本地(local attach),格式化为ext4文件系统,然后挂载到宿主机/var/lib/cinder/mount目录中,与我们猜想过程基本一致。

可以通过lsblk确认:

  
 
 
  1. root@devstack:~/cinder-docker-driver# lsblk -s | grep int32bit-test  
  2. sdd 8:48 0 2G 0 disk /var/lib/cinder/mount/int32bit-test-1 

从docker容器实例中退出,此时会自动把volume从本地detach。

我们使用cinder把创建的卷手动attach到本地并挂载,关于Cinder的local attach,可参考OpenStack中那些很少见但很有用的操作。

  
 
 
  1. root@devstack:~# cinder list 
  2. +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+ 
  3. | ID                                   | Status    | Name            | Size | Volume Type | Bootable | Attached to | 
  4. +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+ 
  5. | 58837c2b-af79-4f89-97ea-40e2622d2c52 | available | int32bit-test-1 | 2    | lvmdriver-1 | false    |             | 
  6. +--------------------------------------+-----------+-----------------+------+-------------+----------+-------------+ 
  7. root@devstack:~# cinder local-attach 58837c2b-af79-4f89-97ea-40e2622d2c52 
  8. +----------+-----------------------------------+ 
  9. | Property | Value                             | 
  10. +----------+-----------------------------------+ 
  11. | path     | /dev/sdd                          | 
  12. | scsi_wwn | 360000000000000000e00000000010001 | 
  13. | type     | block                             | 
  14. +----------+-----------------------------------+ 
  15. root@devstack:~# mount /dev/sdd /mnt 

查看前面我们写的文件:

  
 
 
  1. root@devstack:~# cat /mnt/hello.txt 
  2. HelloWorld 

可见输出了我们通过容器写的HelloWorld。

通过docker cinder driver基本验证了我们之前的猜想是正确的。

3 fuxi

3.1 fuxi项目简介

OpenStack fuxi是一个比较新的项目,最初是从magnum项目分离出来,于2016年2月26号被OpenStack社区接受成为社区项目,目前主要由华为主导开发,其目标是使Docker容器可以使用Cinder volume和Manila share作为持久化存储卷。

3.2 环境准备

OpenStack环境仍然使用之前的DevStack环境,fuxi安装过程如下。

首先安装依赖的包,这些包其实DevStack基本都已经安装完成了。

  
 
 
  1. sudo apt-get update 
  2. sudo apt-get install python-dev git libffi-dev libssl-dev gcc 
  3. sudo apt-get install open-iscsi 
  4. sudo apt-get install sysfsutils 

下载fuxi源码并安装:

  
 
 
  1. git clone https://github.com/openstack/fuxi.git 
  2. cd fuxi 
  3. sudo pip install -r requirements.txt 
  4. sudo python setup.py install 
  5. ln -s /lib/udev/scsi_id /usr/local/bin # for root 

使用generate_config_file_samples.sh生成配置文件模板,并拷贝到/etc/fuxi目录下。

  
 
 
  1. ./tools/generate_config_file_samples.sh 
  2. sudo cp etc/fuxi.conf.sample /etc/fuxi/fuxi.conf 

修复配置文件,最终配置文件如下:

  
 
 
  1. root@devstack:~# cat /etc/fuxi/fuxi.conf  | grep -v '^#' | grep -v '^$' 
  2. [DEFAULT] 
  3. my_ip = 10.0.2.15 
  4. volume_providers = cinder 
  5. [cinder] 
  6. region_name = RegionOne 
  7. volume_connector = osbrick 
  8. fstype = ext4 
  9. auth_url = http://10.0.2.15/identity/v3 
  10. project_name = admin 
  11. project_domain_name = Default 
  12. username = admin 
  13. user_domain_name = Default 
  14. password = nomoresecret 
  15. [keystone] 
  16. [manila] 
  17. volume_connector = osbrick 
  18. auth_type = password 
  19. [nova] 

注意auth_url必须包含版本,如/v3。

启动服务:

  
 
 
  1. fuxi-server --config-file /etc/fuxi/fuxi.conf 

3.3 功能验证

使用Docker创建一个volume:

  
 
 
  1. $ docker volume create -d fuxi --name int32bit-test-fuxi 
  2. int32bit-test-fuxi 
  3. $ docker volume ls | grep int32bit-test-fuxi 
  4. fuxi                int32bit-test-fuxi 

挂载volume到Docker容器中:

  
 
 
  1. $ docker run -ti --rm -v int32bit-test-fuxi:/int32bit-test-fuxi busybox 
  2. / # cd /int32bit-test-fuxi/ 
  3. /int32bit-test-fuxi # ls 
  4. a           b           c           lost+found 

我们可以验证volume其实是映射到本地路径的:

  
 
 
  1. $ lsblk -Sf 
  2. NAME HCTL       TYPE VENDOR   MODEL             REV TRAN   NAME FSTYPE LABEL UUID                                 MOUNTPOINT 
  3. sda  2:0:0:0    disk ATA      VBOX HARDDISK    1.0  sata   sda 
  4. sdb  11:0:0:1   disk IET      VIRTUAL-DISK     0001 iscsi  sdb  ext4         d04b16a1-3392-41df-999f-e6c36b5d0cd6 /fuxi/data/cinder/int32bit-test-fuxi 
  5. 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类实现的,该类实现了provider.Provider接口,而该接口就是对应前面介绍的Docker volume plugin接口。我们主要研究其mount方法:

  
 
 
  1. t(self, docker_volume_name): 
  2.     cinder_volume, state = self._get_docker_volume(docker_volume_name) 
  3.     LOG.info("Get docker volume %(d_v)s %(vol)s with state %(st)s", 
  4.              {'d_v': docker_volume_name, 'vol': cinder_volume, 
  5.               'st': state}) 
  6.  
  7.     connector = self._get_connector() 
  8.     if state == NOT_ATTACH: 
  9.         connector.connect_volume(cinder_volume) 
  10.     elif state == ATTACH_TO_OTHER: 
  11.         if cinder_volume.multiattach: 
  12.             connector.connect_volume(cinder_volume) 
  13.         else: 
  14.             msg = _("Volume {0} {1} is not shareable").format( 
  15.                 docker_volume_name, cinder_volume) 
  16.             raise exceptions.FuxiException(msg) 
  17.     elif state != ATTACH_TO_THIS: 
  18.         msg = _("Volume %(vol_name)s %(c_vol)s is not in correct state, " 
  19.                 "current state is %(state)s") 
  20.         LOG.error(msg, {'vol_name': docker_volume_name, 
  21.                         'c_vol': cinder_volume, 
  22.                         'state': state}) 
  23.         raise exceptions.NotMatchedState() 

以上主要通过Cinder API获取volume信息,检查其attach情况:

  • 如果volume没有attach,则直接attach。
  • 如果volume已经attach(in-use)到其它主机,则检查其是否支持multiattach,如果支持多挂载,则直接挂载,否则抛出异常,挂载失败。
  • 如果volume已经attach到当前主机,则说明已经挂载到本地了,但这不是我们所期望的,因此直接抛出异常。

假设前面都没有问题,顺利把volume attach到本地,则我们可以获取映射到本地的虚拟设备名,接下来的代码就是检查该路径是否就绪:

  
 
 
  1. link_path = connector.get_device_path(cinder_volume) 
  2.     if not os.path.exists(link_path): 
  3.         LOG.warning("Could not find device link file, " 
  4.                     "so rebuild it") 
  5.         connector.disconnect_volume(cinder_volume) 
  6.         connector.connect_volume(cinder_volume) 
  7.  
  8.     devpath = os.path.realpath(link_path) 
  9.     if not devpath or not os.path.exists(devpath): 
  10.         msg = _("Can't find volume device path") 
  11.         LOG.error(msg) 
  12.         raise exceptions.FuxiException(msg) 

如果前面顺利获取的volume到设备名,比如/dev/sdd,则最后的工作就是mount到本地文件系统了:

  
 
 
  1. mountpoint = self._get_mountpoint(docker_volume_name) 
  2.   self._create_mountpoint(mountpoint) 
  3.   fstype = cinder_volume.metadata.get('fstype', cinder_conf.fstype) 
  4.   mount.do_mount(devpath, mountpoint, fstype) 
  5.   return mountpoint 

其中mountpoint是挂载的目标目录,其路径为volume_dir + volume_type + volume_name,其中volume_dir通过配置文件配置,默认为/fuxi/data,volume_type这里为cinder,假设volume name为int32bit-test-volume,则挂载路径为/fuxi/data/cinder/int32bit-test-volume。

create_mountpoint就是创建挂载目录:

  
 
 
  1. (self, mountpoint): 
  2.     """Create mount point directory for Docker volume. 
  3.     :param mountpoint: The path of Docker volume. 
  4.     """ 
  5.     try: 
  6.         if not os.path.exists(mountpoint) or not os.path.isdir(mountpoint): 
  7.             utils.execute('mkdir', '-p', '-m=755', mountpoint, 
  8.                           run_as_root=True) 
  9.             LOG.info("Create mountpoint %s successfully", mountpoint) 
  10.     except processutils.ProcessExecutionError as e: 
  11.         LOG.error("Error happened when create volume " 
  12.                   "directory. Error: %s", e) 

最后调用mount.do_mount,mount是fuxi实现的一个通用的mount库,代码位于fuxi/common/mount.py。

  
 
 
  1. (devpath, mountpoint, fstype): 
  2.     """Execute device mount operation. 
  3.  
  4.     :param devpath: The path of mount device. 
  5.     :param mountpoint: The path of mount point. 
  6.     :param fstype: The file system type. 
  7.     """ 
  8.     try: 
  9.         if check_already_mounted(devpath, mountpoint): 
  10.             return 
  11.  
  12.         mounter = Mounter() 
  13.         mounter.mount(devpath, mountpoint, fstype) 
  14.     except exceptions.MountException: 
  15.         try: 
  16.             mounter.make_filesystem(devpath, fstype) 
  17.             mounter.mount(devpath, mountpoint, fstype) 
  18.         except exceptions.FuxiException as e: 
  19.             with excutils.save_and_reraise_exception(): 
  20.                 LOG.error(str(e)) 

该方法直接调用Mounter的mount方法,如果mount失败,则重新创建格式化文件系统后再次挂载(第一次挂载时没有安装文件系统,因此mount必然失败)。mount方法如下:

  
 
 
  1. (self, devpath, mountpoint, fstype=None): 
  2.     try: 
  3.         if fstype: 
  4.             utils.execute('mount', '-t', fstype, devpath, mountpoint, 
  5.                           run_as_root=True) 
  6.         else: 
  7.             utils.execute('mount', devpath, mountpoint, 
  8.                           run_as_root=True) 
  9.     except processutils.ProcessExecutionError as e: 
  10.         msg = _("Unexpected error while mount block device. " 
  11.                 "Devpath: {0}, " 
  12.                 "Mountpoint: {1} " 
  13.                 "Error: {2}").format(devpath, mountpoint, e) 
  14.         raise exceptions.MountException(msg) 

由此我们通过研究源码,再次验证了我们之前的猜想是正确的。

4 REX-Ray

REX-Ray是一个EMC团队领导的开源项目,为Docker、Mesos及其他容器运行环境提供持续的存储访问。其设计旨在囊括通用存储、虚拟化和云平台,提供高级的存储功能。换句话说,REX-Ray进一步封装,提供一个统一的为Docker提供volume的工具,整合了各种不同的provide,如Ceph、Cinder、EBS等。但遗憾的是,目前Docker挂载Cinder卷,Docker必须安装在Nova虚拟机中,虚拟机还必须能够和OpenStack管理网打通,参考Cinder: failed to attach volume while using cinder driver,因此实际使用场景有限,本文不再详细介绍。

【本文是51CTO专栏作者“付广平”的原创文章,如需转载请通过51CTO获得联系】

戳这里,看该作者更多好文

文章名称:Docker使用OpenStackCinder持久化volume原理分析及实践
链接URL:http://www.shufengxianlan.com/qtweb/news27/242077.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联