在現在的雲計算大數據環境下,備份容災已經變成了一個炙手可熱的話題,今天,和大家一起分享一下openstack是怎么做災備的。
【首先介紹快照】
snapshot可以為volume創建快照,快照中保存了volume當前的狀態,此后可以通過snapshot回溯。
主要采用了Copy On Write算法。進行快照時,不牽涉到任何檔案復制動作,它所作的只是通知服務器將目前有數據的磁盤區塊全部保留起來,不被覆寫。
接下來檔案修改或任何新增、刪除動作,均不會覆寫原本數據所在的磁盤區塊,而是將修改部分寫入其它可用的磁盤區塊中。
資源的復制只有在需要寫入的時候才進行,此前,是以只讀方式共享,使實際的拷貝被推遲到實際發生寫入的時候。
【備份和快照區別】
在openstack中,cinder主要負責volume的創建以及管理工作,cinder-backup負責備份操作,cinder-snapshot負責快照操作。
大家都知道,備份和快照具有類似的功能,都可以保存當前volume的內容,以便於以后進行恢復。但是二者在用途上是由差別的:具體表如有以下幾點:
- snapshot依賴於源volume,不能獨立存在;而backup不依賴於volume,就算volume被刪除了,也可以單獨做恢復。
- snapshot通常和源volume存儲在一起,而backup一般會被作為異地容災放在其他的存儲后端。
以上兩點,注定了快照和備份是作為不同用法出現的,快照主要用於便捷回溯,備份主要用於異地容災。
【cinder-backup配置】
Cinder 的 backup 功能是由 cinder-backup 服務提供的,openstack 默認沒有啟用該服務,需要手工啟用。
與 cinder-volume 類似,cinder-backup 也通過 driver 架構支持多種備份 backend,包括 POSIX 文件系統、NFS、Ceph、GlusterFS、Swift 和 IBM TSM。
支持的driver 源文件放在 /cinder/cinder/backup/drivers/中
不同的備份存儲系統以Driver的形式得以支持,driver.py文件中定義了各種Driver的基類backupDriver,所有具體Driver的實現都位於drivers子目錄,通過配置文件的backup_driver選項指定使用的Driver。
如:
backup_drive=cinder.backup.drivers.swift |
【backup基本流程】
cinder-backup服務在接到RPC請求后會找到該操作對應的host以及相應的存儲后端Driver,然后調用該Driver中與backup相關的接口,比如backup_volume()和restore_backup(),這些接口最終會調用cinder-backup服務中Driver來對該Volume進行備份或者恢復操作。
下面代碼是備份一個卷的入口(早期版本的代碼,P版本代碼比較冗長未列)
#cinder/backup/manager.py class BackupManager(manager.SchedulerDependentManager): def create_backup(self, context, backup_id): """Create volume backups using configured backup service.""" backup = self.db.backup_get(context, backup_id)
#創建一個卷先找到對應的主機 volume_host = volume_utils.extract_host(volume['host'], 'backend') backend = self._get_volume_backend(host=volume_host)
self.db.backup_update(context, backup_id, {'host': self.host, 'service': self.driver_name}) backup_service = self.service.get_backup_driver(context)
#調用Driver中與backup相關的接口 self._get_driver(backend).backup_volume(context, backup, backup_service)
|
工作流程大致如下:
cinder-api接收到備份請求后,會轉發到cinder-backup,由cinder-backup負責監聽請求並轉發,中間會包括一些卷類型的檢查以及服務的檢測,這里不多說,我們主要查看driver的工作情況。
如下圖所示:
對於不同的備份后端,后文會對ceph和chunk兩種模式分別做介紹。
【接口介紹】
表中,是backup相關的層次結構,備注了接口功能以及調用關系:
層次 |
功能 |
cinder-api的API層接口 |
暴露給客戶端的API接口 |
cinder-backup的API層接口 |
將RPC的接口進行封裝使被import |
cinder-backup的RPCAPI層接口 |
與manager.py進行交互 |
cinder-backup的manager層接口 |
與cinder-backup的driver進行交互 |
cinder-backup的driver層接口 |
與存儲后端或后端提供的庫進行交互 |
【代碼結構】
/cinder/backup/__init__.py |
指定並導入cinder-backup的API類; |
/cinder/backup/api.py |
處理所有與卷備份服務相關的請求; class API(base.Base): 卷備份管理的接口API; 主要定義了卷的備份相關的三個操作的API: create:實現卷的備份的建立; delete:實現刪除卷的備份; restore:實現恢復備份; 這三個操作都需要通過backup_rpcapi定義的RPC框架類的遠程調用來實現; |
/cinder/backup/driver.py |
class BackupDriver(base.Base): 所有備份驅動類的基類; |
/cinder/backup/manager.py |
卷備份的管理操作的實現; 核心代碼; class BackupManager(manager.SchedulerDependentManager):塊存儲設備的備份管理; 主要實現的是三個遠程調用的方法: create_backup:實現卷的備份的建立(對應api.py中的create方法); restore_backup:實現恢復備份(對應api.py中的restore方法); delete_backup:實現刪除卷的備份(對應api.py中的delete方法); |
/cinder/backup/rpcapi.py |
volume rpc API客戶端類; class BackupAPI(cinder.openstack.common.rpc.proxy.RpcProxy):volume rpc API客戶端類 主要實現了三個方法: create_backup:遠程調用實現卷的備份的建立(對應api.py中的create方法); restore_backup:遠程調用實現恢復備份(對應api.py中的restore方法); delete_backup:遠程調用實現刪除卷的備份(對應api.py中的delete方法); |
/cinder/backup/drivers/ceph.py |
ceph備份服務實現; class CephBackupDriver(BackupDriver):Ceph對象存儲的Cinder卷備份類; 這個類確認備份Cinder卷到Ceph對象存儲系統; |
/cinder/backup/drivers/swift.py |
用swift作為后端的備份服務的實現; class SwiftBackupDriver(ChunkedBackupDriver):用swift作為后端的備份服務的各種管理操作實現類; |
/cinder/backup/drivers/tsm.py |
IBM Tivoli存儲管理(TSM)的備份驅動類; class TSMBackupDriver(BackupDriver):實現了針對TSM驅動的卷備份的備份、恢復和刪除等操作; |
/cinder/backup/drivers/other driver |
同上 |
【chunk driver】
swift、nfs,Google,glusterfs,posix的備份是基於chunk的備份。
其中有兩個參數解釋一下:
chunk_size |
表示將volume分成多大的塊進行備份。 在NFS中這個值叫做backup_file_size,默認是1999994880 Byte,大約1.8G。 在Swift中這個值叫做backup_swift_object_size,默認是52428800Byte,也就是50M。 在Ceph中這個值叫做backup_ceph_chunk_size,默認值是128M。 |
sha_block_size |
這個值用於增量備份,它決定了增量備份的粒度。 在NFS中,這個值叫做backup_sha_block_size_bytes。 在Swift中,這個值叫做backup_swift_block_size。 默認都是32768Byte,也就是32K。 在Ceph,沒有對應的概念。 |
swift、nfs,Google,glusterfs,posix繼承自ChunkedBackupDriver(/cinder/backup/chunkeddriver.py),實現機制為將原始volume拆分成chunk,然后保存到對應的存儲上。
做全量備份時,每次從volume讀入chunk_size字節數據,從頭每sha_block_size個字節做一次SHA計算,並保存結果,然后將chunk_size數據(可壓縮)保存到存儲上,形成一個object,循環將整個volume保存到backend 上,會生成兩個文件:metadata、sha256file。
其中metadata記錄volume對應存儲上的哪些object、object大小、壓縮算法、長度、偏移量。
sha256file按順序記錄每次SHA計算結果。
當增量備份時,將每sha_block_size數據的SHA值與上次保存在sha256file中的SHA值比對,若不等則備份相應sha_block。
全量備份恢復時先指定volume id,通過metadata文件將存儲端的備份文件和volume對應起來,而增量備份恢復時,由於增量備份的特性,使得增量備份間有依賴,形成備份鏈。
配置如下:
[DEFAULT] backup_driver = cinder.backup.drivers.nfs
backup_mount_point_base = /backup_mount backup_share=172.16.8.99:/backup |
手動啟動cinder-backup服務:
/usr/bin/python /usr/local/bin/cinder-backup --config-file /etc/cinder/cinder.conf |
我們可以通過日志去查看cinder-backup到底做了些什么事情:
- 啟動 backup 操作,mount NFS。
- 創建 volume 的臨時快照。
- 創建存放 backup 的 container 目錄。
- 對臨時快照數據進行壓縮,並保存到 container 目錄。
- 創建並保存 sha256(加密)文件和 metadata 文件。
- 刪除臨時快照。
Backup 完成后,可以通過 cinder backup-list 查看當前存在的 backup。
查看一下 container 目錄的內容,發現下面有以下內容:
backup-00001 |
壓縮后的 backup 文件 |
backup_metadata |
metadata 文件 |
backup_sha256file |
sha值文件 |
在cinder backup-create中有以下幾個可選項:
--force |
允許 backup 已經 attached 的 volume |
--incremental |
表示可以執行增量備份。 |
【恢復操作】
restore 的過程其實很簡單,兩步走:
在存儲節點上創建一個空白 volume。
將 backup 的數據 copy 到空白 volume 上。
詳細流程如下:
- 向 cinder-api 發送 restore 請求
- cinder-api 發送消息
- cinder-scheduler 挑選最合適的 cinder-volume
- cinder-volume 創建空白 volume
- 啟動 restore 操作,mount NFS。
- 讀取 container 目錄中的 metadata。
- 將數據解壓並寫到 volume 中。
- 恢復 volume 的 metadata,完成 restore 操作。
所以,其實對於卷恢復來講,其實就是創建了一個與原備份卷時間點內容相同的新卷,不會對老的卷造成任何的影響。
【CEPH備份】
按照cinder-volume所用的backend分兩種情況介紹:一種是使用非RBD作為backend,另一種是使用RBD作為backend。
cinder-volume使用非RBD作為backend
這種情況下比較簡單,並且僅支持全量備份。
在創建備份時,首先創建一個base backup image,然后每次從源卷讀入chunk_size(即backup_ceph_chunk_size,默認是128MB)大小的數據,寫入到backup image,直到把整個源卷都復制完。
注意,這里不支持對chunk的壓縮。
因為volume上的數據都會寫入到創建的這個backup image上去,也就是說volume和backup是一對一的,因此也不需要metadata文件。
cinder-volume使用RBD作為backend
在這種情況下,即cinder-volume和cinder-backup都是用rbd作為backend,是支持增量備份的。
增量備份的實現完全依賴於ceph處理差量文件的特性,所謂ceph處理差量文件的能力,即ceph可以將某個rbd image不同時刻的狀態進行比較,並且將其差量導出成文件。
另外,ceph也可以將這個差量文件導入到某個image中。
全量備份:
- 調用librbd創建backup base image,與源 volume 大小一致,name 的形式為 "volume-VOLUMD_UUID.backup.base"
- 調用cinder-volume的rbd driver獲取該image相關元數據( 池子、用戶、配置文件等);
-
從源卷傳輸數據到該image;
源 rbd image 新建一個快照,name形式為 backup.BACKUP_ID.snap.TIMESTRAMP
源rbd image 上使用 export-diff 命令導出從剛開始創建時到上一步快照時的差異數據,其實就是現在整個rbd的數據,然后通過import-diff將差量數據導入剛剛在備份集群上新創建的base image中。
增量備份:
- 判斷是否有backup base image;
-
若沒有,判斷source image是否具有snap(from_snap),有的話刪除該snap;
若有,尋找滿足"^backup.([a-z0-9-]+?).snap.(.+)$" 的最近一次快照,若不存在則報錯;
- 給source image創建一個新的snap,name 形式為 backup.BACKUP_ID.snap.TIMESTRAMP;
- 源rbd image 上使用export-diff命令導出與最近的一次快照比較的差量數據,然后使用import-diff進行數據傳輸。
ceph 增量備份其實就是基於ceph rbd的export-diff,import-diff 功能實現的:
export-diff |
將某個 rbd image 在兩個不同時刻的數據狀態比較不同后導出補丁文件。 |
import-diff |
將某個補丁文件合並到某個 rbd image 中。 |
恢復時相反,只需要從備份集群找出對應的快照並導出差量數據,導入到原volume即可。