OpenStack使用Ceph存储,Ceph到底做了什么?

存储 存储软件 OpenStack
Ceph是当前非常流行的开源分布式存储系统,具有高扩展性、高性能、高可靠性等优点,同时提供块存储服务(rbd)、对象存储服务(rgw)以及文件系统存储服务(cephfs)。目前也是OpenStack的主流后端存储,和OpenStack亲如兄弟,为OpenStack提供统一共享存储服务。

1 背景知识

1.1 Ceph简介

  • Ceph是当前非常流行的开源分布式存储系统,具有高扩展性、高性能、高可靠性等优点,同时提供块存储服务(rbd)、对象存储服务(rgw)以及文件系统存储服务(cephfs)。目前也是OpenStack的主流后端存储,和OpenStack亲如兄弟,为OpenStack提供统一共享存储服务。使用Ceph作为OpenStack后端存储,具有如下优点:
  • 所有的计算节点共享存储,迁移时不需要拷贝根磁盘,即使计算节点挂了,也能立即在另一个计算节点启动虚拟机(evacuate)。
  • 利用COW(Copy On Write)特性,创建虚拟机时,只需要基于镜像clone即可,不需要下载整个镜像,而clone操作基本是0开销,从而实现了秒级创建虚拟机。

Ceph RBD支持thin provisioning,即按需分配空间,有点类似Linux文件系统的sparse稀疏文件。创建一个20GB的虚拟硬盘时,最开始并不占用物理存储空间,只有当写入数据时,才按需分配存储空间。

[[229974]]

Ceph的更多知识可以参考官方文档,这里我们只关注RBD,RBD管理的核心对象为块设备(block device),通常我们称为volume,不过Ceph中习惯称之为image(注意和OpenStack image的区别)。Ceph中还有一个pool的概念,类似于namespace,不同的pool可以定义不同的副本数、pg数、放置策略等。每个image都必须指定pool。image的命名规范为pool_name/image_name@snapshot,比如openstack/test-volume@test-snap,表示在openstackpool中test-volumeimage的快照test-snap。因此以下两个命令效果是等同的:

  1. rbd snap create --pool openstack --image test-image --snap test-snap 
  2. rbd snap create openstack/test-image@test-snap 

在openstack pool上创建一个1G的image命令为:

  1. rbd -p openstack create --size 1024 int32bit-test-1 

image支持快照(snapshot)的功能,创建一个快照即保存当前image的状态,相当于git commit操作,用户可以随时把image回滚到任意快照点上(git reset)。创建快照命令如下:

  1. rbd -p openstack snap create int32bit-test-1@snap-1 

查看rbd列表:

  1. $ rbd -p openstack ls -l | grep int32bit-test 
  2. int32bit-test-1        1024M 2 
  3. int32bit-test-1@snap-1 1024M 2 

基于快照可以创建一个新的image,称为clone,clone不会立即复制原来的image,而是使用COW策略,即写时拷贝,只有当需要写入一个对象时,才从parent中拷贝那个对象到本地,因此clone操作基本秒级完成,并且需要注意的是基于同一个快照创建的所有image共享快照之前的image数据,因此在clone之前我们必须保护(protect)快照,被保护的快照不允许删除。clone操作类似于git branch操作,clone一个image命令如下:

  1. rbd -p openstack snap protect int32bit-test-1@snap-1 
  2. rbd -p openstack clone int32bit-test-1@snap-1 int32bit-test-2 

我们可以查看一个image的子image(children)有哪些,也能查看一个image是基于哪个image clone的(parent):

  1. $ rbd -p openstack children int32bit-test-1@snap-1 
  2. openstack/int32bit-test-2 
  3. $ rbd -p openstack info int32bit-test-2 | grep parent 
  4. parent: openstack/int32bit-test-1@snap-1 

以上我们可以发现int32bit-test-2是int32bit-test-1的children,而int32bit-test-1是int32bit-test-2的parent。

不断地创建快照并clone image,就会形成一条很长的image链,链很长时,不仅会影响读写性能,还会导致管理非常麻烦。可幸的是Ceph支持合并链上的所有image为一个独立的image,这个操作称为flatten,类似于git merge操作,flatten需要一层一层拷贝所有顶层不存在的数据,因此通常会非常耗时。

  1. $ rbd -p openstack flatten int32bit-test-2 
  2. Image flatten: 31% complete... 

此时我们再次查看其parrent-children关系:

  1. rbd -p openstack children int32bit-test-1@snap-1 

此时int32bit-test-1没有children了,int32bit-test-2完全独立了。

当然Ceph也支持完全拷贝,称为copy:

  1. rbd -p openstack cp int32bit-test-1 int32bit-test-3 

copy会完全拷贝一个image,因此会非常耗时,但注意copy不会拷贝原来的快照信息。

Ceph支持将一个RBD image导出(export):

  1. rbd -p openstack export int32bit-test-1 int32bit-1.raw 

导出会把整个image导出,Ceph还支持差量导出(export-diff),即指定从某个快照点开始导出:

  1. rbd -p openstack export-diff \ 
  2. int32bit-test-1 --from-snap snap-1 \ 
  3. --snap snap-2 int32bit-test-1-diff.raw 

以上导出从快照点snap-1到快照点snap-2的数据。

当然与之相反的操作为import以及import-diff。通过export/import支持image的全量备份,而export-diff/import-diff实现了image的差量备份。

Rbd image是动态分配存储空间,通过du命令可以查看image实际占用的物理存储空间:

  1. $ rbd du int32bit-test-1 
  2. NAME            PROVISIONED   USED 
  3. int32bit-test-1       1024M 12288k 

以上image分配的大小为1024M,实际占用的空间为12288KB。

删除image,注意必须先删除其所有快照,并且保证没有依赖的children:

  1. rbd -p openstack snap unprotect int32bit-test-1@snap-1 
  2. rbd -p openstack snap rm int32bit-test-1@snap-1 
  3. rbd -p openstack rm int32bit-test-1 

1.2 OpenStack简介

OpenStack是一个IaaS层的云计算平台开源实现,关于OpenStack的更多介绍欢迎访问我的个人博客,这里只专注于当OpenStack对接Ceph存储系统时,基于源码分析一步步探测Ceph到底做了些什么工作。本文不会详细介绍OpenStack的整个工作流程,而只关心与Ceph相关的实现,如果有不清楚OpenStack源码架构的,可以参考我之前写的文章如何阅读OpenStack源码。

阅读完本文可以理解以下几个问题:

  1. 为什么上传的镜像必须要转化为raw格式?
  2. 如何高效上传一个大的镜像文件?
  3. 为什么能够实现秒级创建虚拟机?
  4. 为什么创建虚拟机快照需要数分钟时间,而创建volume快照能够秒级完成?
  5. 为什么当有虚拟机存在时,不能删除镜像?
  6. 为什么一定要把备份恢复到一个空卷中,而不能覆盖已经存在的volume?
  7. 从镜像中创建volume,能否删除镜像?

注意本文都是在基于使用Ceph存储的前提下,即Glance、Nova、Cinder都是使用的Ceph,其它情况下结论不一定成立。

(注:原文有源代码,已经超过5000字的篇幅限制,因此做了精简,如果需要看详细推导验证过程,请查看原文链接,另外你可以快速跳到总结部分查看OpenStack各个操作对应的Ceph工作。)

2 Glance

2.1 Glance介绍

Glance管理的核心实体是image,它是OpenStack的核心组件之一,为OpenStack提供镜像服务(Image as Service),主要负责OpenStack镜像以及镜像元数据的生命周期管理、检索、下载等功能。Glance支持将镜像保存到多种存储系统中,后端存储系统称为store,访问镜像的地址称为location,location可以是一个http地址,也可以是一个rbd协议地址。只要实现store的driver就可以作为Glance的存储后端,其中driver的主要接口如下:

  • get: 获取镜像的location。
  • get_size: 获取镜像的大小。
  • get_schemes: 获取访问镜像的URL前缀(协议部分),比如rbd、swift+https、http等。
  • add: 上传镜像到后端存储中。
  • delete: 删除镜像。
  • set_acls: 设置后端存储的读写访问权限。

为了便于维护,glance store目前已经作为独立的库从Glance代码中分离出来,由项目glance_store维护。目前社区支持的store列表如下:

  • filesystem: 保存到本地文件系统,默认保存/var/lib/glance/images到目录下。
  • cinder: 保存到Cinder中。
  • rbd:保存到Ceph中。
  • sheepdog:保存到sheepdog中。
  • swift: 保存到Swift对象存储中。
  • vmware datastore: 保存到Vmware datastore中。

http: 以上的所有store都会保存镜像数据,唯独http store比较特殊,它不保存镜像的任何数据,因此没有实现add方法,它仅仅保存镜像的URL地址,启动虚拟机时由计算节点从指定的http地址中下载镜像。

本文主要关注rbd store,它的源码在这里,该store的driver代码主要由国内Fei Long Wang负责维护,其它store的实现细节可以参考源码glance store drivers.

3 Nova

3.1 Nova介绍

Nova管理的核心实体为server,为OpenStack提供计算服务,它是OpenStack最核心的组件。注意Nova中的server不只是指虚拟机,它可以是任何计算资源的抽象,除了虚拟机以外,也有可能是baremetal裸机、容器等。

不过我们在这里假定:

  • server为虚拟机。
  • image type为rbd。
  • compute driver为libvirt。

启动虚拟机之前首先需要准备根磁盘(root disk),Nova称为image,和Glance一样,Nova的image也支持存储到本地磁盘、Ceph以及Cinder(boot from volume)中。需要注意的是,image保存到哪里是通过image type决定的,存储到本地磁盘可以是raw、qcow2、ploop等,如果image type为rbd,则image存储到Ceph中。不同的image type由不同的image backend负责,其中rbd的backend为nova/virt/libvirt/imageackend中的Rbd类模块实现。

4 Cinder

4.1 Cinder介绍

Cinder是OpenStack的块存储服务,类似AWS的EBS,管理的实体为volume。Cinder并没有实现volume provide功能,而是负责管理各种存储系统的volume,比如Ceph、fujitsu、netapp等,支持volume的创建、快照、备份等功能,对接的存储系统我们称为backend。只要实现了cinder/volume/driver.py中VolumeDriver类定义的接口,Cinder就可以对接该存储系统。

Cinder不仅支持本地volume的管理,还能把本地volume备份到远端存储系统中,比如备份到另一个Ceph集群或者Swift对象存储系统中,本文将只考虑从源Ceph集群备份到远端Ceph集群中的情况。

5 总结

5.1 Glance

1. 上传镜像

  1. rbd -p ${GLANCE_POOL} create --size ${SIZE} ${IMAGE_ID}rbd -p ${GLANCE_POOL} snap create ${IMAGE_ID}@snap 
  2. rbd -p ${GLANCE_POOL} snap protect ${IMAGE_ID}@snap 

2. 删除镜像

  1. rbd -p ${GLANCE_POOL} snap unprotect ${IMAGE_ID}@snap 
  2. rbd -p ${GLANCE_POOL} snap rm ${IMAGE_ID}@snap 
  3. rbd -p ${GLANCE_POOL} rm ${IMAGE_ID}  

5.2 Nova

1 创建虚拟机

  1. rbd clone \${GLANCE_POOL}/${IMAGE_ID}@snap \${NOVA_POOL}/${SERVER_ID}_disk 

2 创建虚拟机快照

  1. # Snapshot the disk and clone # it into Glance's storage poolrbd -p ${NOVA_POOL} snap create \${SERVER_ID}_disk@${RANDOM_UUID}rbd -p ${NOVA_POOL} snap protect \${SERVER_ID}_disk@${RANDOM_UUID}rbd clone \${NOVA_POOL}/${SERVER_ID}_disk@${RANDOM_UUID} \${GLANCE_POOL}/${IMAGE_ID} # Flatten the image, which detaches it from the # source snapshotrbd -p ${GLANCE_POOL} flatten ${IMAGE_ID} # all done with the source snapshot, clean it uprbd -p ${NOVA_POOL} snap unprotect \${SERVER_ID}_disk@${RANDOM_UUID}rbd -p ${NOVA_POOL} snap rm \${SERVER_ID}_disk@${RANDOM_UUID} # Makes a protected snapshot called 'snap' on # uploaded images and hands it outrbd -p ${GLANCE_POOL} snap create ${IMAGE_ID}@snap 
  2. rbd -p ${GLANCE_POOL} snap protect ${IMAGE_ID}@snap 

3 删除虚拟机

  1. for image in $(rbd -p ${NOVA_POOL} ls | grep "^${SERVER_ID}");do  
  2.     rbd -p ${NOVA_POOL} rm "$image"; done  

5.3 Cinder

1 创建volume

(1) 创建空白卷

  1. rbd -p ${CINDER_POOL} create \--new-format --size ${SIZE} \volume-${VOLUME_ID}  

(2) 从快照中创建

  1. rbd clone \${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@snapshot-${SNAPSHOT_ID} \${CINDER_POOL}/volume-${VOLUME_ID}rbd resize --size ${SIZE} \openstack/volume-${VOLUME_ID}  

(3) 从volume中创建

  1. # Do full copy if rbd_max_clone_depth <= 0.if [[ "$rbd_max_clone_depth" -le 0 ]]; then 
  2.     rbd copy \ 
  3.     ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID} \ 
  4.     ${CINDER_POOL}/volume-${VOLUME_ID} 
  5.     exit 0fi# Otherwise do COW clone.# Create new snapshot of source volumerbd snap create \${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap 
  6. rbd snap protect \${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap# Now clone source volume snapshotrbd clone \${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap \${CINDER_POOL}/volume-${VOLUME_ID}# If dest volume is a clone and rbd_max_clone_depth reached,# flatten the dest after cloning.depth=$(get_clone_depth ${CINDER_POOL}/volume-${VOLUME_ID})if [[ "$depth" -ge "$rbd_max_clone_depth" ]]; then 
  7.     # Flatten destination volume  
  8.     rbd flatten ${CINDER_POOL}/volume-${VOLUME_ID} 
  9.     # remove temporary snap 
  10.     rbd snap unprotect \ 
  11.     ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap 
  12.     rbd snap rm \ 
  13.     ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snapfi 

(4) 从镜像中创建

  1. rbd clone \${GLANCE_POOL}/${IMAGE_ID}@snap \${CINDER_POOL}/volume-${VOLUME_ID}if [[ -n "${SIZE}" ]]; then 
  2.     rbd resize --size ${SIZE} ${CINDER_POOL}/volume-${VOLUME_ID}fi 

2 创建快照

  1. rbd -p ${CINDER_POOL} snap create \volume-${VOLUME_ID}@snapshot-${SNAPSHOT_ID}rbd -p ${CINDER_POOL} snap protect \volume-${VOLUME_ID}@snapshot-${SNAPSHOT_ID}  

3 创建备份

(1) ***次备份

  1. rbd -p ${BACKUP_POOL} create \ 
  2. --size ${VOLUME_SIZE} \ 
  3. volume-${VOLUME_ID}.backup.base 
  4. NEW_SNAP=volume-${VOLUME_ID}@backup.${BACKUP_ID}.snap.${TIMESTAMP
  5. rbd -p ${CINDER_POOL} snap create ${NEW_SNAP} 
  6. rbd export-diff ${CINDER_POOL}/volume-${VOLUME_ID}${NEW_SNAP} - \ 
  7. | rbd import-diff --pool ${BACKUP_POOL} - \ 
  8. volume-${VOLUME_ID}.backup.base 

(2) 增量备份

  1. rbd -p ${CINDER_POOL} snap create \volume-${VOLUME_ID}@backup.${BACKUP_ID}.snap.${TIMESTAMP} rbd export-diff  --pool ${CINDER_POOL} \--from-snap backup.${PARENT_ID}.snap.${LAST_TIMESTAMP} \${CINDER_POOL}/volume-${VOLUME_ID}@backup.${BACKUP_ID}.snap.${TIMESTRAMP} - \| rbd import-diff --pool ${BACKUP_POOL} - \${BACKUP_POOL}/volume-${VOLUME_ID}.backup.base 
  2. rbd -p ${CINDER_POOL} snap rm \volume-${VOLUME_ID}.backup.base@backup.${PARENT_ID}.snap.${LAST_TIMESTAMP}  

4 备份恢复

  1. rbd export-diff --pool ${BACKUP_POOL} \volume-${SOURCE_VOLUME_ID}.backup.base@backup.${BACKUP_ID}.snap.${TIMESTRAMP} - \| rbd import-diff --pool ${CINDER_POOL} - \volume-${DEST_VOLUME_ID}rbd -p ${CINDER_POOL} resize \--size ${new_size} volume-${DEST_VOLUME_ID}    

 

责任编辑:武晓燕 来源: Openstack私有云
相关推荐

2017-12-06 14:35:01

OpenStackCeph存储

2018-04-12 08:37:27

2015-04-03 10:43:49

2021-09-30 19:00:17

对象存储Ceph

2021-05-06 21:26:00

BcacheCeph存储

2015-02-09 09:57:56

Ceph 块设备OpenStackLinux

2015-01-07 15:11:00

ITOpenStack云存储

2021-03-24 08:03:50

存储Ceph运维

2015-07-09 13:19:17

Ceph分布式存储性能调优

2015-11-16 14:52:24

CephOpenStack分布式存储

2018-05-23 08:39:18

AlluxioCeph对象存储

2018-04-23 15:14:02

混合云云存储公有云

2018-09-21 11:00:58

Ceph存储系统

2015-11-24 14:14:00

CentOS 7.0Ceph配置

2018-01-30 09:07:36

Ceph分布式存储

2018-07-13 08:45:57

Ceph对象存储混合云

2018-11-15 12:35:25

Ceph分布式存储

2018-08-20 10:14:21

Ceph存储ObjectStore

2011-04-19 10:04:25

NeopPIshell网站后门

2011-11-29 09:10:11

Hadoop
点赞
收藏

51CTO技术栈公众号