Posted on

使用 rsync 實做重複資料刪除技術 差異備份 版本控制 增量備份

使用 rsync 實做重複資料刪除技術 差異備份 版本控制 增量備份

5662轉錄

前言 原文
很多人使用rsync這個工具軟體進行日常檔案備份的工作。這個用來取代rcp的遠端檔案複製工具使用非 常有效率的演算法進行檔案比對、傳輸與複製,並且支援遠端shell (remote-shell) 和rsync伺服器 (rsync daemon) 的彈性架構,讓整個檔案同步、備份的工作可以簡單的進入排程,達到完全自動。 


而 隨著軟體版本的演進,超過一百個以上的功能選項,對於絕大部分的使用者來說可能還只是單純使用「歸檔」 (-a) 和「同步刪除」 (–delete) 這兩個選項就滿足了日常需求。不過像這樣簡單的設定在重複操作之下只能保有一份備份當時的全備份,卻也同時讓之前的備份直接被覆蓋而無法參考。因此,如果 需要保留多次長週期的備份而使用相同的方法,這似乎意味著需要倍數的磁碟空間,和指定不同的歸檔路徑才可以容納得下多次全備份。 


其 實rsync早已支援「增量備份」的實做方法。透過不同的功能選項可以讓每次同步備份過程中遇到的「差異」用不同方式保留下來,做到增量備份的效果。只是 在技巧上必須撰寫指令稿 (script) 將整個備份流程包裝起來才能達到實用的目的。因此已經有許多配合rsync的「前導程式」 (不用修改軟體而達到修改軟體的目的的工具軟體) 被開發出來,使用整合的操作介面針對每次備份出來的檔案進行版本控制,並提供類似高階儲存設備的「快照備份」 (snapshot) 呈現方式,讓使用者可以任意切換到歷史版本的目錄當中直接找到之前備份的檔案,而且重點是「不需要倍數的磁碟空間就可以容納多次的全備份」,這似乎與「重 複資料刪除技術」 (Data De-duplication) 有異曲同工之妙! 


以 下就針對rsync實做增量備份相關的功能選項做一個簡單的說明,並介紹一個以perl語言所開發的前導程式「rsnapshot」,看看它是如何將週期 性的rsync備份模擬成檔案系統的快照備份,讓使用者可以用最節省的空間的方式保有多次的備份,而且可以直接存取任一版本的全備份。 


rsync 的增量備份
為了方便說明,以下出現的語法是以直接在本機檔案系統下操作為範例。如果需要透過rsync daemon或remote-shell模式應用在遠端檔案系統,則直接代換對應的語法即可。 


基本同步
如果我們需要備份目錄a,最基本的語法會是像這個樣子: 


rsync -a –delete a/ b/


這 會在目的端產生一個目錄b,兩個目錄以下的內容在同步備份之後會一模一樣。-a代表歸檔模式,是-rlptgoD的簡化寫法,意味遞迴地進入來源端目錄a 下所有的目錄,將比對、複製所有的檔案和目錄,不僅保留時間、權限、擁有者、群組等屬性,連符號連結及設備檔案也一併複製。而–delete功能選項的 效果,在重複操作的時候,若目錄a下有檔案或目錄被刪除,會同步反應在目錄b。這裡需要注意到的地方,就是目錄a後面的斜線是必要的,如果沒有這個斜線, 則同步備份之後的結果就是將整個目錄a備份在目錄b之下,變成b/a/的效果。 


增量備份
使用–backup選項在同步備份過程中可以讓目的端的檔案,被覆蓋或刪除之前先進行更名的動作以保留下來。 


rsync -a –delete –backup a/ b/


使 用這個功能選項在預設的情況下,如果目錄a下的檔案file1被更新或刪除,則在下一次同步備份時,在目的端目錄b會將前一次備份的那個版本先更名為 file1~保留下來,然後如果是更新檔案則再儲存新版本的file1,如果是刪除檔案則就不動作。這樣的作法只能保有更新之前的版本,或之前還在的檔 案。在反覆同步備份操作之下,在目的端目錄b對任一檔案而言,頂多只能多留一份過去版本的備份,而形成諸多不同時間點不同檔案的「備份的備份」散落在目錄 b之下。 


所以解決這個問題的方法是另外透過–backup-dir來指定目的端的檔案在被覆蓋或刪除之前,先被複製到特定的路徑之下。這樣的作法相當是實做最簡易的增量備份。 


rsync -a –delete –backup –backup-dir=../c/ a/ b/


如 果目錄a的檔案被更新或刪除,則在下一次同步備份時,在目的端目錄b的前一次備份的那個版本會先複製到目錄c,然後再儲存新的版本到目錄b。 這與傳統增量備份的概念正好相反,傳統增量備份的第一份是全備份,之後的備份只留與前一次備份的差異;而rsync用–backup-dir方式進行增 量備份則是最後一次備份是全備份 (目錄b) ,同時將差異記錄在另外一個目錄裡 (目錄c) 。所以透過每次備份指定不同backup dir的技巧,就可以記錄每次備份當時的差異,這樣不用倍數的儲存空間,就可以保留多次備份當時的狀態。但這裡會有的問題是,在backup dir裡出現的檔案,是之前備份之後被修改或刪除的檔案,在利用上必須跟最後一次同步備份的目錄做比較,才大概可以看得出來檔案是被修改還是被刪除。因此 這個備份方式並不一定適用在任何環境。 


在這裡要注意的是,若使用相對路徑表示法,–backup-dir所指定的路徑就必須是相對於目錄b的路徑。所以前面範例的目錄b與目錄c是在同一層。而且–backup-dir是配合–backup同時使用的功能選項。 


另外一種與–backup-dir類似的功能選項是–compare-dest。這是一個獨立的選項,它讓來源端目錄a與指定的compare dest目錄c先進行比對,然後把是否覆蓋、刪除檔案的動作反應在目錄b。 


rsync -a –delete –compare-dest=../c/ a/ b/


這樣的作法必須是目錄c與目錄a之前曾先同步過,之後目錄a的任何檔案更新可以經由這個方式與目錄c比對後再將更新的檔案複製到目錄b。這個用途比較適合追蹤在前一次同步備份過後,有哪些檔案被更動過,但不包含被刪除。 


所以如果需要兩次完整的備份內容,可以用–copy-dest這個功能選項。 


rsync -a –delete –copy-dest=../c/ a/ b/


它 與–compare-dest類似,作法是目錄c與目錄a之前曾先同步過,之後一樣是讓來源端目錄a與指定的copy dest目錄c先進行比對,然後將沒有被更動的檔案直接從目錄c複製到目錄b,有被更動的檔案從目錄a複製到目錄b,被刪除的檔案就不儲存。這樣的好處是 從目錄c或從目錄b來看都是完整的某一次備份,而且應用在不同主機之間的備份可以節省很多網路頻寬,因為原本單純同步複製一個新的目的端就必須重新傳輸所 有檔案一次,而利用這個方式則直接在目的端主機上拿之前的備份來複製,進而提高同步的效率,而且每個目錄都是備份當時的全備份,直接拿來還原也很方便。 


不過這樣的操作就等於備份幾次就需要幾倍的空間,與原來往增量備份方向去想的作法就背道而馳了。 


所以在rsync版本2.5.6之後新增加–link-dest的功能選項,它把–copy-dest當中「沒有更動的檔案直接複製的動作」用檔案的「硬式連結」 (hard link) 來取代。 


rsync -a –delete –link-dest=../c/ a/ b/


所 以同樣的,目錄a與目錄c之前曾同步備份過,之後透過這個方式目錄a會先與目錄c進行比對,沒有被更動過的檔案直接在目錄b利建立硬式連結到目錄c的檔 案,有被更動的檔案則從目錄a複製到目錄b,被刪除的檔案則不儲存。這樣一來同樣從目錄c或從目錄b來看都是完整的某一次全備份,而且因為是檔案的硬式連 結,所以相同版本的檔案只佔據同一份空間。如果反覆備份多次,由於大部分的檔案都是靜態未被更動,那麼所有需要的容量就是最早的全備份與之後每次備份的差 異量的總和而已。 


其實在這個功能選項出現之前,就已經有方法可以做到相同的效果。一樣是用rsync反覆操作基本同步的方法 (-a –delete) ,只是在每次操作之前,先在目的端把之前備份好的目錄進行一次用硬式連結的複製。 


第一次備份:來源端目錄a同步備份到目的端目錄c。 


rsync -a –delete a/ c/


在目的端之前的備份是目錄c,則有兩種方式可以產生用硬式連結複製出來的目錄b: 


cp -al c/ b/


或 


cd c && find . -print | cpio -dpl ../b


第二次備份:來源端目錄a同步備份到目的端目錄b。 


rsync -a –delete a/ b/


這 裡的cp是GNU cp,在Linux環境下是預設的複製工具,可以直接使用。cp -al會以歸檔的方式複製檔案,但所有的檔案以硬式連結來建立。可是在其他平台上若沒有GNU cp,就不會有-al的選項,所以只能選擇用cpio來進行複製。 當完成這個複製動作之後,兩個目錄下檔案其實對應到相同的Inode,檔案本身的連結計數 (link counter) 加一之後為二,檔案對應到的實體區塊只有一份,所以除了目錄結構以外,不佔額外的空間。 接下來之後的備份使用rsync基本的同步方式指定目的端為目錄b。在這裡不用擔心因為是檔案的硬式連結,目錄b和目錄c下同一檔案當再次同步備份時會導 致一起被覆蓋或刪除,因為rsync預設的行為會先將異動的檔案以另外的檔名複製出來進行更新,然後再更名為原來的檔名。所以不管是覆蓋還是刪除,都只是 對原本檔案的連結計數做減一的動作,而在連結計數歸零之前,也就是檔案真正被刪除之前,都不會更動到原本備份的那個版本。 


所 以不管使用哪一個版本的rsync,再搭配一些操作的技巧就可以實做出類似「重複資料刪除技術」的效果。而我們不需要另外撰寫script來實做這個部 分,因為已經有許多現成的工具軟體可以自動化這些操作流程。以下就以rsnapshot這個軟體為例子,說明這是怎麼做到的。 

rsnapshot
rsnapshot 是一個自由軟體 (free software) ,可以從官方網站 (http://www.rsnapshot.org/) 下載,或從作業系統的軟體管理工具當中找到既有的port或package來進行安裝。它是一個以perl語言開發的工具軟體,一個遠端檔案系統的快照工 具 (remote filesystem snapshot utility) 。在架構上,可以用來備份本機或遠端的檔案系統,它在本機端是藉由rsync進行檔案複製,或透過ssh建立安全的rsync遠端shell與遠端主機進 行檔案傳輸,或直接與遠端主機的rsync伺服器連線。它是用「拉」的方式進行同步備份,也就是說,安裝rsnapshot的主機就是發動備份的主機,也 是儲存備份資料的主機。rsnapshot透過排程 (crontab) 週期地啟動同步備份,而所有需要備份檔案的遠端主機都安裝rsync或啟動rsync伺服器來等待週期的同步備份。當同步備份發生時,rsnapshot 根據設定將遠端主機需要備份的目錄「拉回」到本機特定的目錄架構裡。而這個目錄架構就是模擬高階儲存設備所提供的「快照備份」 (snapshot) 的目錄結構。依照不同的備份週期產生不同時間間隔的目錄,而在每個目錄下的檔案都是備份當時的全備份。透過檔案的「硬式連結」,在每次同步備份時,未曾更 動的檔案直接使用之前備份的版本進行硬式連結到最新的備份目錄下,也因此整體備份空間的需求就相當是一次全備份與異動檔案的不同版本所佔用的空間。 


在配置上,需要設定rsnapshot.conf這個檔案,和建立週期備份的排程 (crontab) 。 


設 定rsnapshot.conf非常簡單,使用預設的設定檔rsnapshot.conf.default直接拿來修改就可以了。在這個檔案裡面的每一個 段落都有完整的說明可以參考。其中需要注意的地方是,由於rsnapshot可以在不同平台上運作,為了實做「硬式連結」來節省相同檔案的使用空間,所以 必須根據所在的平台選擇最適當的外部工具。如果使用的rsync版本支援–link-dest的功能選項,則可以直接設定link_dest為1代表支 援。如果不支援則就會使用GNU cp的-al的複製方式來建立硬式連結,因此可以指定GNU cp的所在路徑。但如果作業環境裡沒有GNU cp工具而無法指定,則程式內部會額外模擬這個動作。 


另外要注意的就是備份時間間隔的表示法,這是與建立週期備份排程密切相關的設定。根據需要來決定多久備份一次,換句話說就是單位時間可以保留幾次備份。如果每四小時備份一次,意味著一天可以保留六次每四小時的備份;如果每天備份一次,代表著一週可以保留七次每天的備份。 


在rsnapshot.conf.default裡關於備份時間間隔的預設值如下: 


interval        hourly  6
interval        daily   7
interval        weekly  4
#interval       monthly 3


在系統設定相對應的crontab: 


0 */4 * * *         /usr/bin/rsnapshot hourly
50 23 * * *         /usr/bin/rsnapshot daily
40 23 * * 6         /usr/bin/rsnapshot weekly
#30 23 1 * *        /usr/bin/rsnapshot monthly


如 果根據預設值配合相對應的排程,那麼每四小時就會進行一次週期名稱為hourly的備份,它會在snapshot_root所定義的路徑下產生 hourly.0到hourly.5的六個目錄,目錄裡面就存放著備份的內容。hourly.0是最新的備份,每四小時啟動備份時,最舊的備份 hourly.5會先被刪除,然後依序將4,3,2,1的目錄尾數更名為5,4,3,2,再依照硬式連結產生的方式,如果是使用cp -al的模式,則將hourly.0複製出hourly.1後,直接用rsync基本同步的方式備份到hourly.0,如果是使用rsync –link-dest的模式,則在hourly.0更名為hourly.1後,rsync指定hourly.1為link dest然後備份到hourly.0。 


其 他不是排在最前面的週期名稱不需要實際使用rsync進行備份,因為可以直接保留前一個週期名稱的最舊的那個備份。譬如,週期名稱為daily的排程在每 晚23:50進行,因為設定保留七份,所以最舊的一份也就是daily.6在備份開始的時候先被刪除,然後依序將5,4,3,2,1,0的目錄尾數更名為 6,5,4,3,2,1,最後再把hourly.5更名為daily.0就完成了備份。所以排程上刻意比hourly提前十分鐘,搶在hourly.5被 刪除之前先更名為daily.0。而同樣的,weekly.0也是從daily.6更名得來。 


如果使用預設值進行hourly,daily,weekly的備份排程,那麼總共可以保有17份的備份。使用rsnapshot du的命令來可以觀察集中在snapshot_root的這些備份空間使用的情形。 


# rsnapshot du
15G     /backup/.snapshots/hourly.0/
16M     /backup/.snapshots/hourly.1/
21M     /backup/.snapshots/hourly.2/
23M     /backup/.snapshots/hourly.3/
19M     /backup/.snapshots/hourly.4/
19M     /backup/.snapshots/hourly.5/
22M     /backup/.snapshots/daily.0/
38M     /backup/.snapshots/daily.1/
31M     /backup/.snapshots/daily.2/
28M     /backup/.snapshots/daily.3/
53M     /backup/.snapshots/daily.4/
30M     /backup/.snapshots/daily.5/
38M     /backup/.snapshots/daily.6/
50M     /backup/.snapshots/weekly.0/
50M     /backup/.snapshots/weekly.1/
42M     /backup/.snapshots/weekly.2/
38M     /backup/.snapshots/weekly.3/
16G     total


這 裡顯示的是一個真實環境一個月下來保留17份備份所需要的空間。一次全備份大約需要容量15G,如果使用傳統備份方式保留相同數量的備份需要大約 255G。而rsnapshot實做硬式連結增量備份全部17份備份只用到大約16G的容量。而且每個週期名稱目錄下的所有檔案就是備份當時的備份內容, 所以要找回某一次備份取得所有檔案在時間上大約的一致性變得非常容易,因為這非常接近使用檔案系統或儲存設備所提供快照備份的功能,差別只是rsync執 行一回合從頭到尾畢竟還是會有「時間差」,開始備份到結束的時間點會有段距離,相對於真正的快照備份技術等於是「慢速快門」,所以這樣的作法適合用來備份 一般的檔案,卻不適合用來備份檔案之間時間相依性非常高而且不斷在變動的資料庫或其他環境。 


結語
其 實真正的「重複資料刪除技術」並不是像前述這樣的應用,主要是配合在一個既有的備份環境,譬如是一個D2D或D2D2T的架構當中做為備份軟體暫時緩衝用 的磁碟空間 (staging disk) 所使用的技術。透過儲存設備可以識別出重複出現的不定長度資料區塊而選擇不儲存的演算法,讓備份的image file在累積多次備份之後可以維持很高的「壓縮率」。又由於多次備份的image file都是儲存在磁碟上,所以還原的速度會比磁帶還來得快速。而rsync使用硬式連結減少重複檔案所需要的儲存空間,也可以找到特定時間版本備份的檔 案直接取回使用,在儲存原理上與重複資料刪除技術非常類似,在操作上與高階儲存設備提供的快照備份技術的應用方式非常接近。而為了解決rsync「慢速快 門」的問題,其實如果能夠在來源端的檔案系統使用真正的快照備份技術,讓rsync直接備份快照版本的檔案來取得時間點上的一致性,這樣不僅僅可以備份一 般檔案,還可以備份不斷在變動的信件檔或資料庫一直在開啟中的資料檔,進而擴大使用rsync的應用範圍。 




參考資料
http://www.mikerubel.org/computers/rsync_snapshots/ Easy Automated Snapshot-Style Backups with Linux and Rsync