Linux 網路檔案系統簡介
網路檔案系統(NFS)協議是由 Sun MicroSystem 公司在 20 世紀 80 年代為了提供對共享檔案的遠端訪問而設計和實現的,它採用了經典的客戶機/伺服器模式提供服務。為了達到如同 NFS 協議通過使用 Sun 公司開發的遠在本機上使用本地檔案系統一樣便捷的效果,NFS 通過使用遠端過程呼叫協議(RPC Protocol)來實現執行在一臺計算機上的程式來呼叫在另一臺遠端機器上執行的子程式。同時,為了解決不同平臺上的資料互動問題,它提供了外部資料表示(XDR)來解決這個問題。為了靈活地提供檔案共享服務,該協議可以在 TCP 協議或者是 UDP 協議上執行,典型的情況是在 UDP 協議上執行。在此基礎上,NFS 在資料的傳送過程中需要 RPC 命令得到確認,而且在需要的時候會要重傳,這樣既可以通過 UDP 協議獲得較高的通訊效率,也能通過 RPC 來獲得較高的通訊可靠性。
由於 NFS 基於 C/S 模式提供服務,所以它的核心元件主要包括客戶機和伺服器兩部分。圖 1 詳細說明了 NFS 的主要元件以及主要的配置檔案。在伺服器端,portmap、mountd、nfsd 三個監控程式將在後臺執行。portmap 監控程式用來註冊基於 RPC 的服務。當一個 RPC 的監控程式啟動的時候,它告訴 portmap 監控程式它在哪一個埠進行偵聽,並且它在進行什麼樣的 RPC 服務。當一個客戶機向伺服器提出一個 RPC 請求,那麼它就會和 portmap 監控程式取得聯絡以確定 RPC 訊息應該發往的埠號。而 Mountd 監控程式的功能是來讀取伺服器端的/etc/exportfs 檔案並且建立一個將伺服器的本地檔案系統匯出的主機和網路列表,因而客戶機的掛接(mount)請求都被定位到 mountd 監控程式(daemon)。當驗證了伺服器確實具有掛接所請求的檔案系統的許可權以後,mountd 為請求的掛接點返回一個檔案控制程式碼。而 nfsd 監控程式則被伺服器用來處理客戶機端傳送過來的請求。由於伺服器需要同時處理多個客戶機的請求,所以在預設情況下,作業系統將會自動啟動八個 nfsd 執行緒。當然,如果 NFS 伺服器特別忙的時候,系統有可能根據實際情況啟動更多的執行緒。
圖 1 網路檔案系統簡圖
NFS 的客戶機與伺服器之間通過 RPC 進行通訊,通訊過程如下所示:
- 使用者將 NFS 伺服器的共享目錄掛載到本地檔案系統中。
- 客戶訪問 NFS 目錄中的檔案時,NFS 客戶端向 NFS 伺服器端傳送 RPC 請求。
- NFS 服務端接收客戶端發來的 RPC 請求,並將這個請求傳遞給本地檔案訪問程式,然後訪問伺服器主機上的一個本地的磁碟檔案。NFS 伺服器可以同時接收多個 NFS 客戶端的請求,並對其進行併發控制。
- NFS 客戶端向伺服器主機發出一個 RPC 呼叫,然後等待伺服器的應答。NFS 客戶端收到伺服器的應答後,把結果資訊展現給使用者或應用程式。
NFS 下的資料備份、恢復的主要功能
對資料進行備份與恢復是保證資料安全和業務連續性的非常成熟的做法。在 Linux 下的本地檔案系統(例如 Ext2、Ext3 等)中,資料備份和恢復一般採用常規的辦法來進行操作,例如使用 Tar、Archive 等。而對於 NFS 來說,其資料備份需要採用量身定製的方法來進行。
為了保證資料在災難環境中的可用性和業務連續性,針對它的資料備份、恢復方案應具備如下重要功能:
- 通過對系統重要資料的快速備份,切實保證系統資料的安全;
- 可以根據指令完成備份系統的實時切入,保證服務不被中斷,保持系統持續執行的能力;
- 通過實時記錄所有檔案的操作日誌,系統管理員能夠在發生災難的情況下對日誌進行分析和取證,從而發現入侵者的蛛絲馬跡。
NFS 多版本備份技術
為了保證伺服器出現故障後能迅速恢復,要求系統資料能快速恢復到一個最近的正確狀態,所有這些都需要多版本技術的支援,通過同步記錄檔案的在某些時刻的狀態,在整個系統範圍內建立起類似於資料庫系統的”檢查點”,以保證上述目標的實現。
對於多版本系統而言,需要較好地解決兩個方面的問題:效能和空間利用率。對於前者,最主要的是保證在生成版本的時候能夠快速完成,同時恢復時也具有較好的效能。此外,系統引入多版本造成的整體開銷也應該比較理想。對於第二點,主要考慮是節約磁碟空間,雖然隨著硬體技術的不斷髮展,磁碟空間越來越大,價效比也越來越高,但是當版本較多而且檔案數量較多、較大時,引入多版本增加的開銷也可能相當可觀,同時,較大的空間也意味著版本生成時可能需要更多的寫操作,這樣也必將影響總體效能。
為了保證引入多版本特性後檔案系統仍具有較好的效能,以及保證較高的空間利用率,我們開發了一種高效的惰性版本生成演算法。主要思想是:生成版本時不進行檔案的複製,僅複製目錄結構,在新版本生成後到下一版本生成前,如果有檔案需要修改,則第一次修改時對該檔案進行復制,從而保證該檔案狀態與對應的版本保持一致。
在一般情況下,目錄結構的資料量遠遠小於檔案的資料量,因而這種方法可以大大降低版本生成時需要複製的資料量,因而具有較高的效能。同時,這種把單個檔案版本生成的實際操作推後到非做不可的時候,並且任意檔案在兩次版本之間最多生成一次版本,因此這種惰性策略可以使需要實際生成版本的檔案數量達到最少,同時還可以把多個檔案版本生成操作分散到具體的檔案操作中,從而避免了集中的一次性版本生成方法可能造成的服務暫時停頓的問題。
版本生成後的結構如圖 2 所示。
圖 2 多版本生成示意圖
具體演算法包括兩個部分,即版本生成演算法和檔案第一次修改處理演算法,版本生成演算法主要完成版本生成工作,主要過程如下:
- 找到需要形成版本的最高層目錄作為原目錄;
- 利用檔案系統提供的函式,生成新的目錄節點,稱為新目錄;
- 把原目錄中的結構複製到新目錄;
- 在原目錄中找到所有的子目錄,重複 2、3 步;
- 把新的子目錄對應的 inode 號替換上一層目錄中的老 inode 號;
- 重複上述過程,及到目錄樹中的所有目錄得到複製為止。
在上述策略中,新版本並沒有複製所有的檔案,只是在複製的目錄結構中記錄下了該檔案的 inode 號(即複製了目錄的結構,而不是把檔案都進行復制,從而節省了儲存和計算資源),因此,當有 NFS 請求需要對檔案進行版本生成後的第一次修改時,需要複製該檔案,生成新的版本。該實現過程參見如下流程圖:
圖 3 寫時複製演算法示意圖
這種檔案複製策略其實是一種惰性演算法,也即我們常說的寫時複製的方法,這個方法在 Linux 作業系統的子程式對父程式資源的繼承中有所體現。這個策略一方面可以最大限度減少複製檔案的數量,另一方面則可以避免瞬間過大的檔案複製工作量,影響檔案服務的效能。該演算法的過程如下:當檔案操作為寫操作時,判斷該檔案是否版本生成後的第一次寫操作;若是則利用檔案系統提供的底層函式生成一個新的檔案,複製原始檔的資料到新生成的檔案,同時把該檔案當前版本的 inode 節點中的版本號置為當前版本號,這樣新檔案就成為該檔案的最新版本。
雖然我們採用的演算法可以有較好的效能,儲存開銷也是最優,但是,每次版本生成肯定會造成服務效能的下降和空間的佔用,而這些代價在一個比較安全可靠的環境中是可以適當降低的,即當系統比較安全的時候,可以選擇讓系統以更低的頻率進行版本生成,相反,當系統安全狀況比較糟糕的時候,可以通過提高版本生成頻率適當降低服務效能來獲得更高的資料安全效能,當系統處於緊急狀態時,甚至可以要求立即進行版本生成。
基於這些考慮,我們採用了自適應的備份策略,災情評估系統可以動態評估系統的災情程度,然後可以立即修改版本生成策略,以適應當時的安全要求。
NFS 資料恢復技術
企業應用 NFS 的一個重要目標就是要保證系統的高可用性,即使在出現嚴重災難、故障、攻擊等情況下能具有較好的生存能力。因此,當一個系統出現故障時,如何快速地恢復系統,迅速投入到服務備份中去是相當重要的,所以,對於檔案系統資料的恢復而言,也需要專門的考慮和設計。
本方案被配置成多個站點互為備份的情況,即平時只有一個主站點在服務,其他站點處於同步備份狀態,當某個站點出現故障或災難時,或者是被非法入侵者攻破時,系統可以立即分配新的主站點把被破壞的站點替換下來,進入恢復狀態,其他正常的站點仍可提供正常的服務。
當然,也存在所有站點均出現故障的情況,但是由於我們採用了多種措施,如動態隨機遷移、災情評估與響應策略等,再配合傳統的防火牆、IDS 等安全系統,可以極大限度地減少這種機率。因此,我們的資料恢復問題主要考慮上述這種情形,即個別伺服器出現故障退出服務而其他系統依然正常的情況。
首先,我們來分析一下系統退出後資料的情形,主要涉及到退出的伺服器和正常的主伺服器與備份伺服器,如圖 4 所示:
圖 4 一個系統退出後資料狀態示意圖
在上圖中,退出伺服器最後生成的版本號為 i,系統退出後,一方面主檔案伺服器會察覺到同步資料無法從退出伺服器返回結果,這樣的話它就會重發同步請求,經過 3 次重發後,如果依然沒有返回資訊,則認為該伺服器退出服務,因此會把同步資料備份到磁碟檔案中,並記錄下該伺服器在同步資料檔案中的起始位置,這當由多個檔案伺服器退出時可以分別識別出來。由於退出系統無法繼續保持同步,因此其狀態會與工作的檔案伺服器不一致,具體表現在以下幾個方面:
- 當退出時間很短時,資料不一致僅存在於緩衝區中,這時如果退出伺服器能立即重新投入使用,則不需要進行額外的資料恢復,資料同步可以通過主伺服器同步請求的重試來達到。
- 當主伺服器確認退出伺服器退出後,會把未同步的資料寫入特定的同步資料檔案中,這時的不一致性包括了緩衝區中的資料和同步資料檔案中的資料,這時的資料恢復需要做兩方面的工作:
- 把同步資料檔案中的正確資料一次性傳送給退出伺服器,退出伺服器把它寫入本地的同步資料檔案;
- 建立本地的緩衝區,建立起同步機制,接收同步資料,同時啟動資料同步程式,先同步資料檔案中的資料,當緩衝區資料因沒有處理而達到一定程度時,會自動把部分資料追加到同步資料檔案的後面,這時,退出伺服器已經恢復了正常工作,實際上也不需要過多的資料恢復工作。
由於主檔案伺服器一般需要處理檔案的讀寫請求,寫請求僅佔一部分,需要同步而執行的操作造成的負載要小於主伺服器,因此可能在較短的時間內完成同步。當需要退出伺服器(此時已經進入同步階段)成為主伺服器時,則必須等所有同步資料同步完成後才能開始服務。
- 如果退出伺服器是因為較嚴重的故障或災難而退出的,則可能需要較長時間的處理,如更換硬體、系統重啟、甚至重灌系統等,這時就可能出現一些困難的情況,一種是如上圖所示的,工作正常的系統已經生成了新的版本,如伺服器退出時的最新版本是 i,經過一段時間後,正常系統生成了新的版本,這時主系統會清空同步資料檔案,重新從版本生成後進行記錄。對於這種情況,可以有兩種處理辦法:
- 基於本地版本的快速恢復:當退出檔案伺服器本地至少存在一個版本與其他正常機器上的版本相同時,可以採用這種恢復策略。具體而言,先確定一個最新的正確版本,用本地版本恢復,這一過程非常簡單快捷,僅涉及到兩次 inode 的修改;然後選擇一臺正常伺服器,請求它生成一個正常系統上最新版本與恢復版本的增量升級資料,這樣的資料量不會很大,而且不需要象基於操作的同步那樣逐步進行,同步效率非常高,因此可以大大提高恢復速度。同步到正常系統的最新版本後,然後就按照上述第 2 條的情況進行同步資料檔案的同步。
- 基於分佈版本的快速恢復:當停頓時間太長而不存在一個相同的版本,或檔案伺服器資料出現損壞(如磁碟故障造成資料損毀)時,需要採用此種方法。具體辦法如下:直接把正常伺服器上的最新版本傳送到退出伺服器,然後按照上述的第 2 種情況進行同步資料檔案的同步。
正如上面所述,全部伺服器均出現問題的概率是很小的,但是,不能簡單的排除這種情況的出現,特別是本方案採用資料同步機制,即多個站點的資料是保持快速同步的,這雖然能保證動態遷移的順利完成,但是也帶來較大的風險,就是會出現資料”汙染”的自動傳播,當主檔案伺服器中的檔案資料因為某些原因(主要是對檔案的非法訪問)造成資料非法修改時,會立即傳播到其他備份節點,這樣的話,不管服務遷移到哪臺機器均會出現錯誤。
針對這種情況,我們採取了以下措施:當發現非法修改造成資料汙染時,系統可以自動命令各站點恢復到指定的版本,如前一版本(可以由管理員配置成前一、二、三個版本);管理員也可以干預這一過程,強制各站點恢復到同一指定的版本,從而保證全域性檔案系統使用同一正確版本。
NFS 檔案細粒度恢復技術
在傳統恢復技術中,一方面由於資料備份不是實時進行的,當出現事故需要恢復時,最新的備份資料與最新資料之間存在一個時間差,這樣就造成了該時間段內資料的丟失(見圖 5);同時,傳統的資料備份是一定時間段後資料的增量備份,是一段時間內所有檔案操作疊加後的結果,因而無法精確知道在這段時間內實際資料的變化過程,因而也無法從所有這些操作中定位非法操作,並進行選擇性的恢復,以保證資料的正確性。
圖 5 因非實時備份造成恢復時的資料丟失示意圖
基於上述考慮,我們不但採用了增量方式的多版本備份恢復技術,同時還對檔案的修改日誌進行了實時的備份,這樣就可以在事故發生後進行基於檔案操作的精確恢復,並支援允許剔除非法操作的選擇性恢復,這樣既能儘量避免因事故造成的資料丟失問題,又能通過選擇性恢復較好保證資料的正確性,同時,還可以通過對日誌的分析,結合資料的精確恢復,達到發現犯罪線索、獲得有效證據的目的,為打擊網路犯罪提供有力的技術手段。在這裡,精確性恢復指的是恢復某一時段的所有操作,一般是在某一版本後的所有操作,不由使用者進行選擇,而選擇性恢復則指的是某一時間段內的所有操作構成的集合的子集,需要恢復的操作由使用者通過查詢、瀏覽等工具來進行選擇。在我們的定義中,實際上可以認為精確恢復為選擇性恢復的一個特例。
我們首先需要有相關的解決方法來記錄下具體的操作資訊,形成操作日誌檔案,從而作為分析的證據(參見圖 6)。我們使用的策略是通過修改伺服器作業系統核心呼叫 nfsd_write()、nfsd_create()……。從中取到呼叫處理物件的檔案、目錄的全路徑名,寫進檔案,在核心中截獲相應的檔案操作請求。下面以 nfsd_rename()系統呼叫為例,進行擴充、修改而實現記錄操作日誌的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
int nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,struct svc_fh *tfhp, char *tname, int tlen) { struct dentry *fdentry, *tdentry, *odentry, *ndentry; struct inode *fdir, *tdir; int err; char *name; mem_segment_t oldfs; int fd; err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); if (err) goto out; rqstp->rq_path1 = rqstp->rq_path2; err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); if (err) goto out; fdentry = ffhp->fh_dentry; fdir = fdentry->d_inode; tdentry = tfhp->fh_dentry; tdir = tdentry->d_inode; …… //加入的程式碼進行處理工作 if((!rqstp->rq_recover)&&(!S_ISDIR(odentry->d_inode->i_mode)) &&(odentry->d_inode->i_nlink>1)){ rqstp->rq_copy->wait = 1; rqstp->rq_copy->done = 0; name = get_total_name(dentry,NULL); oldfs = get_fs(); set_fs(KERNEL_DS); //進入核心模式 fd = sys_open("/backupserv/changfilename.c",0,31681); sys_write(fd,name,strlen(name)); sys_close(fd); set_fs(oldfs); //從核心模式返回 while(!rqstp->rq_copy->done){ schedule_timeout((HZ+99)/100); …… } } |
該檔案是在 nfs 伺服器端執行 nfs 客戶機傳送過來的修改檔案或者是目錄的原函式。在這裡,我們可以通過新增自己的程式碼,來將建立的目錄和檔名存入一個磁碟檔案當中,以備後面的備份和恢復操作。
圖 6 NFS 檔案細粒度恢復日誌產生示意圖
獲得了操作日誌資訊,然後就可以進行精確恢復和選擇性恢復時。首先由使用者利用資料查詢、瀏覽工具確定需要恢復的檔案操作集,然後利用相應的日誌資料按記錄產生順序逐條生成恢復請求,傳送給檔案伺服器端的代理程式,由它通過 proc 檔案請求 NFS 檔案系統恢復模組進行恢復,恢復模組收到請求後,取出相關資料,然後通過呼叫底層 ext3 檔案系統基本操作完成該次檔案操作的”重放”,最後返回執行結果,通過 proc 檔案通知代理程式,代理程式再通知管理端,管理端再傳送下一條恢復請求,及到所有選中的操作全完成為止。具體實現模式請參看圖 7:
圖 7 恢復流程示意圖
資料快速同步技術
在系統中,各檔案伺服器之間的資料需要及時同步更新,這樣才能保證服務遷移後到新的環境後相關資料環境的一致性,從而保證服務遷移在語義上的正確性。在本方案中,每個檔案伺服器均採用 NFSv3 協議向外提供檔案服務,當系統開始工作時,管理員會指定一臺主伺服器,由該伺服器負責向外提供服務,其他檔案伺服器為備份伺服器,接收同步資料,進行資料的同步更新,並不對外提供服務,只有當系統決定遷移後,選定的遷移目標對應的檔案伺服器才成為主檔案伺服器。
由於主檔案伺服器負責對外的檔案服務,因此,資料同步的發起者應該是主檔案伺服器,而所有的備份伺服器均為被動的同步資料接收者。因此,資料的快速同步包含兩方面的工作:主檔案伺服器產生同步資料和備份檔案伺服器接收同步資料完成同步。具體的資料流向如圖 8 所示:
圖 8 同步資料的產生與流動示意圖
為了達到資料快速同步的目的,我們採用了記錄檔案寫操作(包括建立、修改、刪除、改名、屬性修改等所有的改變檔案或目錄屬性、內容的操作)的具體引數的方法來生成同步資料,這樣每次生成的資料量比較少,而且可以滿足及時更新的目的。同步資料的格式及相關程式碼段如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
struct Log { int length; //整個資料包的長度 int ops; //操作的型別 char* data; //與操作相關資料 }; //下面程式碼段從核心將同步資料包發往其他檔案伺服器 long send(struct socket* sock, void * buff,size_t len) { int err; mm_segment_t oldfs; struct msghdr msg; struct iovec iov; static int total = 0; down(&log_sem); iov.iov_base=buff; iov.iov_len=len; msg.msg_name=NULL; msg.msg_iov=&iov; msg.msg_iovlen=1; msg.msg_control=NULL; msg.msg_controllen=0; msg.msg_namelen=0; total += len; msg.msg_flags = MSG_SYN;//DONTWAIT; oldfs=get_fs(); set_fs(KERNEL_DS); err = sock_sendmsg(sock, &msg, len); set_fs(oldfs); if(err<0){ dprintk("send err(errNo=%d len = %d)\n",err,len); netbroken = 1; } …… up(&log_sem); return(err); } |
同步資料產生後,先放入一個緩衝區中,而不是立即傳送到備份檔案伺服器,這樣可以較大程度改善系統的總體效能。緩衝區中的資料由同步管理程式管理,當達到一定資料量時,同步管理程式負責把緩衝區中的資料傳送到備份檔案伺服器上,並根據返回的應答結果決定是否需要把重發資料,當確認某個伺服器無法響應後,自動把同步資料定期寫入一個僅可追加的檔案,以便於隨後可能需要的恢復階段同步的需要,當這個寫入的檔案資料量超出一定限制時,並且系統確認已經至少有一個新的版本生成,可以把該檔案清空。
當資料到達備份檔案伺服器時,由獨立的接收程式負責把資料放入接收緩衝區,經核對資料無誤後給主伺服器傳送確認訊號,另一個獨立程式即更新管理程式把接收緩衝區作為輸入,從中解析出一個個的順序的操作日誌,從每個日誌中得到操作型別,然後在剩餘的資料中按照特定的操作型別提取所需的引數,利用檔案系統呼叫完成相應操作。
總結
Linux 網路檔案系統已經為企業在資料備份和共享領域得到了廣泛應用。如何保證其多版本備份、實時恢復是一個非常關鍵的問題,本文將詳細介紹針對該網路檔案系統的資料備份、恢復及同步機制在核心的具體實現,給廣大系統管理員和研發人員提供技術參考。