上面左边是个人我的微信,如需进一步沟通,请加微信。 右边是个人公众号“Openstack私有云”,若有兴趣,请关注。git
Ceph是当前很是流行的开源分布式存储系统,具备高扩展性、高性能、高可靠性等优势,同时提供块存储服务(rbd)、对象存储服务(rgw)以及文件系统存储服务(cephfs)。目前也是OpenStack的主流后端存储,和OpenStack亲如兄弟,为OpenStack提供统一共享存储服务。使用Ceph做为OpenStack后端存储,具备以下优势:swift
全部的计算节点共享存储,迁移时不须要拷贝根磁盘,即便计算节点挂了,也能当即在另外一个计算节点启动虚拟机(evacuate)。后端
利用COW(Copy On Write)特性,建立虚拟机时,只须要基于镜像clone便可,不须要下载整个镜像,而clone操做基本是0开销,从而实现了秒级建立虚拟机。微信
Ceph RBD支持thin provisioning,即按需分配空间,有点相似Linux文件系统的sparse稀疏文件。建立一个20GB的虚拟硬盘时,最开始并不占用物理存储空间,只有当写入数据时,才按需分配存储空间。app
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
,表示在openstack
pool中test-volume
image的快照test-snap
。所以如下两个命令效果是等同的:分布式
rbd snap create --pool openstack --image test-image --snap test-snap rbd snap create openstack/test-image@test-snap
在openstack
pool上建立一个1G的image命令为:ide
rbd -p openstack create --size 1024 int32bit-test-1
image支持快照(snapshot)的功能,建立一个快照即保存当前image的状态,至关于git commit
操做,用户能够随时把image回滚到任意快照点上(git reset
)。建立快照命令以下:oop
rbd -p openstack snap create int32bit-test-1@snap-1
查看rbd列表:源码分析
$ rbd -p openstack ls -l | grep int32bit-test int32bit-test-1 1024M 2 int32bit-test-1@snap-1 1024M 2
基于快照能够建立一个新的image,称为clone,clone不会当即复制原来的image,而是使用COW策略,即写时拷贝,只有当须要写入一个对象时,才从parent中拷贝那个对象到本地,所以clone操做基本秒级完成,而且须要注意的是基于同一个快照建立的全部image共享快照以前的image数据,所以在clone以前咱们必须保护(protect)快照,被保护的快照不容许删除。clone操做相似于git branch
操做,clone一个image命令以下:性能
rbd -p openstack snap protect int32bit-test-1@snap-1 rbd -p openstack clone int32bit-test-1@snap-1 int32bit-test-2
咱们能够查看一个image的子image(children)有哪些,也能查看一个image是基于哪一个image clone的(parent):
$ rbd -p openstack children int32bit-test-1@snap-1 openstack/int32bit-test-2 $ rbd -p openstack info int32bit-test-2 | grep parent 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
须要一层一层拷贝全部顶层不存在的数据,所以一般会很是耗时。
$ rbd -p openstack flatten int32bit-test-2 Image flatten: 31% complete...
此时咱们再次查看其parrent-children关系:
rbd -p openstack children int32bit-test-1@snap-1
此时int32bit-test-1
没有children了,int32bit-test-2
彻底独立了。
固然Ceph也支持彻底拷贝,称为copy
:
rbd -p openstack cp int32bit-test-1 int32bit-test-3
copy
会彻底拷贝一个image,所以会很是耗时,但注意copy
不会拷贝原来的快照信息。
Ceph支持将一个RBD image导出(export
):
rbd -p openstack export int32bit-test-1 int32bit-1.raw
导出会把整个image导出,Ceph还支持差量导出(export-diff),即指定从某个快照点开始导出:
rbd -p openstack export-diff \ int32bit-test-1 --from-snap snap-1 \ --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实际占用的物理存储空间:
$ rbd du int32bit-test-1 NAME PROVISIONED USED int32bit-test-1 1024M 12288k
以上image分配的大小为1024M,实际占用的空间为12288KB。
删除image,注意必须先删除其全部快照,而且保证没有依赖的children:
rbd -p openstack snap unprotect int32bit-test-1@snap-1 rbd -p openstack snap rm int32bit-test-1@snap-1 rbd -p openstack rm int32bit-test-1
OpenStack是一个IaaS层的云计算平台开源实现,关于OpenStack的更多介绍欢迎访问个人我的博客,这里只专一于当OpenStack对接Ceph存储系统时,基于源码分析一步步探测Ceph到底作了些什么工做。本文不会详细介绍OpenStack的整个工做流程,而只关心与Ceph相关的实现。
阅读完本文能够理解如下几个问题:
为何上传的镜像必需要转化为raw格式?
如何高效上传一个大的镜像文件?
为何可以实现秒级建立虚拟机?
为何建立虚拟机快照须要数分钟时间,而建立volume快照可以秒级完成?
为何当有虚拟机存在时,不能删除镜像?
为何必定要把备份恢复到一个空卷中,而不能覆盖已经存在的volume?
从镜像中建立volume,可否删除镜像?
注意本文都是在基于使用Ceph存储的前提下,即Glance、Nova、Cinder都是使用的Ceph,其它状况下结论不必定成立。
(注:原文有源代码,已经超过5000字的篇幅限制,所以作了精简,若是须要看详细推导验证过程,请查看原文连接,另外你能够快速跳到总结部分查看OpenStack各个操做对应的Ceph工做。)
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地址中下载镜像。
。。。。。。。。。。。此处省略分析验证过程。。。。。。。。。。。。。
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、qcow二、ploop等,若是image type为rbd,则image存储到Ceph中。不一样的image type由不一样的image backend负责,其中rbd的backend为nova/virt/libvirt/imageackend
中的Rbd
类模块实现。
。。。。。。。。。。。此处省略分析验证过程。。。。。。。。。。。。。
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集群中的状况。
。。。。。。。。。。。此处省略分析验证过程。。。。。。。。。。。。。
1. 上传镜像
rbd -p ${GLANCE_POOL} create --size ${SIZE} ${IMAGE_ID}rbd -p ${GLANCE_POOL} snap create ${IMAGE_ID}@snap rbd -p ${GLANCE_POOL} snap protect ${IMAGE_ID}@snap
2. 删除镜像
rbd -p ${GLANCE_POOL} snap unprotect ${IMAGE_ID}@snap rbd -p ${GLANCE_POOL} snap rm ${IMAGE_ID}@snap rbd -p ${GLANCE_POOL} rm ${IMAGE_ID}
1 建立虚拟机
rbd clone \${GLANCE_POOL}/${IMAGE_ID}@snap \${NOVA_POOL}/${SERVER_ID}_disk
2 建立虚拟机快照
# 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 rbd -p ${GLANCE_POOL} snap protect ${IMAGE_ID}@snap
3 删除虚拟机
for image in $(rbd -p ${NOVA_POOL} ls | grep "^${SERVER_ID}");do rbd -p ${NOVA_POOL} rm "$image"; done
1 建立volume
(1) 建立空白卷
rbd -p ${CINDER_POOL} create \--new-format --size ${SIZE} \volume-${VOLUME_ID}
(2) 从快照中建立
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中建立
# Do full copy if rbd_max_clone_depth <= 0.if [[ "$rbd_max_clone_depth" -le 0 ]]; then rbd copy \ ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID} \ ${CINDER_POOL}/volume-${VOLUME_ID} 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 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 # Flatten destination volume rbd flatten ${CINDER_POOL}/volume-${VOLUME_ID} # remove temporary snap rbd snap unprotect \ ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap rbd snap rm \ ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snapfi
(4) 从镜像中建立
rbd clone \${GLANCE_POOL}/${IMAGE_ID}@snap \${CINDER_POOL}/volume-${VOLUME_ID}if [[ -n "${SIZE}" ]]; then rbd resize --size ${SIZE} ${CINDER_POOL}/volume-${VOLUME_ID}fi
2 建立快照
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) 第一次备份
rbd -p ${BACKUP_POOL} create \ --size ${VOLUME_SIZE} \ volume-${VOLUME_ID}.backup.base NEW_SNAP=volume-${VOLUME_ID}@backup.${BACKUP_ID}.snap.${TIMESTAMP} rbd -p ${CINDER_POOL} snap create ${NEW_SNAP} rbd export-diff ${CINDER_POOL}/volume-${VOLUME_ID}${NEW_SNAP} - \ | rbd import-diff --pool ${BACKUP_POOL} - \ volume-${VOLUME_ID}.backup.base
(2) 增量备份
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 rbd -p ${CINDER_POOL} snap rm \volume-${VOLUME_ID}.backup.base@backup.${PARENT_ID}.snap.${LAST_TIMESTAMP}
rbd export-diff --pool ${BACKUP_POOL} \volume-${SOURCE_VOLUME_ID}.backup.base@backu