FreeBSD 上的 ZFS:從數據損壞中恢復
我在 zpool 中有幾 TB 非常有價值的個人數據,由於數據損壞,我無法訪問這些數據。該池最初是在 2009 年左右在 FreeBSD 7.2 系統上建立的,該系統在 Ubuntu 8.04 系統之上的 VMWare 虛擬機內執行。FreeBSD VM 仍然可用並且執行良好,只有主機作業系統現在已更改為 Debian 6。通過 VMWare 通用 SCSI 設備(總共 12 個),來賓 VM 可以訪問硬碟驅動器。
有2個游泳池:
- zpool01:2x 4x 500GB
- zpool02:1x 4x 160GB
有效的是空的,損壞的則包含所有重要數據:
[user@host~]$ uname -a FreeBSD host.domain 7.2-RELEASE FreeBSD 7.2-RELEASE #0: \ Fri May 1 07:18:07 UTC 2009 \ root@driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 [user@host ~]$ dmesg | grep ZFS WARNING: ZFS is considered to be an experimental feature in FreeBSD. ZFS filesystem version 6 ZFS storage pool version 6 [user@host ~]$ sudo zpool status pool: zpool01 state: UNAVAIL scrub: none requested config: NAME STATE READ WRITE CKSUM zpool01 UNAVAIL 0 0 0 insufficient replicas raidz1 UNAVAIL 0 0 0 corrupted data da5 ONLINE 0 0 0 da6 ONLINE 0 0 0 da7 ONLINE 0 0 0 da8 ONLINE 0 0 0 raidz1 ONLINE 0 0 0 da1 ONLINE 0 0 0 da2 ONLINE 0 0 0 da3 ONLINE 0 0 0 da4 ONLINE 0 0 0 pool: zpool02 state: ONLINE scrub: none requested config: NAME STATE READ WRITE CKSUM zpool02 ONLINE 0 0 0 raidz1 ONLINE 0 0 0 da9 ONLINE 0 0 0 da10 ONLINE 0 0 0 da11 ONLINE 0 0 0 da12 ONLINE 0 0 0 errors: No known data errors
幾週前我可以進入游泳池。從那以後,我不得不更換主機的幾乎所有硬體並安裝幾個主機作業系統。
我的懷疑是這些作業系統安裝中的一個向 500GB 驅動器中的一個(第一個?)寫入了引導載入程序(或其他)並破壞了一些 zpool 元數據(或其他)-“或其他”意味著這只是一個非常模糊的想法而那個主題並不完全是我的強項……
有很多關於 ZFS 的網站、部落格、郵件列表等。我在這裡發布這個問題,希望它能幫助我收集足夠的資訊,以便以一種理智的、結構化的、受控的、知情的、知識淵博的方法來取回我的數據——並希望在同樣的情況下幫助其他人。
Google搜尋“zfs recover”時的第一個搜尋結果是 Solaris ZFS Administration Guide 中的ZFS Troubleshooting and Data Recovery一章。在第一個ZFS 故障模式部分,它在“損壞的 ZFS 數據”段落中說:
數據損壞始終是永久性的,在修復過程中需要特別考慮。即使底層設備被修復或更換,原始數據也將永遠失去。
有些令人沮喪。
然而,第二個Google搜尋結果是Max Bruning 的部落格,在那裡,我讀到
最近,有人向我發送了一封電子郵件,他將 15 年的影片和音樂儲存在一個 10TB ZFS 池中,在斷電後出現了缺陷。不幸的是,他沒有備份。他在 FreeBSD 7 上使用 ZFS 版本 6
$$ … $$ 在花了大約 1 週的時間檢查磁碟上的數據後,我基本上能夠恢復所有數據。
和
至於 ZFS 失去您的數據,我對此表示懷疑。我懷疑你的數據在那裡,但你需要找到正確的方法來獲取它。
(這聽起來更像是我想听的……)
第一步:究竟是什麼問題?
如何診斷為什麼 zpool 被報告為損壞?我看到有一個 zdb,它似乎沒有被 Sun 或 Oracle 在網路上的任何地方正式記錄。從它的手冊頁:
NAME zdb - ZFS debugger SYNOPSIS zdb pool DESCRIPTION The zdb command is used by support engineers to diagnose failures and gather statistics. Since the ZFS file system is always consistent on disk and is self-repairing, zdb should only be run under the direction by a support engineer. If no arguments are specified, zdb, performs basic consistency checks on the pool and associated datasets, and report any problems detected. Any options supported by this command are internal to Sun and subject to change at any time.
此外,Ben Rockwood 發表了一篇詳細的文章,並且有一段Max Bruning 在 2008 年 6 月 28 日在布拉格舉行的 Open Solaris 開發者大會上談論它(和 mdb)的影片。
在損壞的 zpool 上以 root 身份執行 zdb 會產生以下輸出:
[user@host ~]$ sudo zdb zpool01 version=6 name='zpool01' state=0 txg=83216 pool_guid=16471197341102820829 hostid=3885370542 hostname='host.domain' vdev_tree type='root' id=0 guid=16471197341102820829 children[0] type='raidz' id=0 guid=48739167677596410 nparity=1 metaslab_array=14 metaslab_shift=34 ashift=9 asize=2000412475392 children[0] type='disk' id=0 guid=4795262086800816238 path='/dev/da5' whole_disk=0 DTL=202 children[1] type='disk' id=1 guid=16218262712375173260 path='/dev/da6' whole_disk=0 DTL=201 children[2] type='disk' id=2 guid=15597847700365748450 path='/dev/da7' whole_disk=0 DTL=200 children[3] type='disk' id=3 guid=9839399967725049819 path='/dev/da8' whole_disk=0 DTL=199 children[1] type='raidz' id=1 guid=8910308849729789724 nparity=1 metaslab_array=119 metaslab_shift=34 ashift=9 asize=2000412475392 children[0] type='disk' id=0 guid=5438331695267373463 path='/dev/da1' whole_disk=0 DTL=198 children[1] type='disk' id=1 guid=2722163893739409369 path='/dev/da2' whole_disk=0 DTL=197 children[2] type='disk' id=2 guid=11729319950433483953 path='/dev/da3' whole_disk=0 DTL=196 children[3] type='disk' id=3 guid=7885201945644860203 path='/dev/da4' whole_disk=0 DTL=195 zdb: can't open zpool01: Invalid argument
我想最後出現“無效參數”錯誤是因為 zpool01 實際上並不存在:它不會發生在工作的 zpool02 上,但似乎也沒有任何進一步的輸出……
好的,在這個階段,最好在文章變得太長之前發布這個。
也許有人可以就如何從這裡繼續前進給我一些建議,在我等待回复的同時,我將觀看影片,查看上面 zdb 輸出的詳細資訊,閱讀 Bens 文章並嘗試弄清楚是什麼什麼…
20110806-1600+1000
更新01:
我想我找到了根本原因:Max Bruning 非常好心地很快回復了我的一封電子郵件,要求提供
zdb -lll
. 在池的“好”raidz1 一半的 4 個硬碟驅動器中的任何一個上,輸出類似於我上面發布的內容。但是,在“損壞”一半的 4 個驅動器中的前 3 個上,zdb
報告failed to unpack label
標籤 2 和 3。池中的第四個驅動器似乎沒問題,zdb
顯示所有標籤。Google搜尋該錯誤消息會顯示此文章。從對該文章的第一個回复:
使用 ZFS,每個物理 vdev 上都有 4 個相同的標籤,在這種情況下是單個硬碟驅動器。L0/L1 位於 vdev 的開頭,L2/L3 位於 vdev 的末尾。
池中的所有 8 個驅動器都是相同型號的Seagate Barracuda 500GB。但是,我確實記得我用 4 個驅動器啟動了池,然後其中一個死了,並由希捷在保修期內更換。後來,我又添加了 4 個驅動器。因此,驅動器和韌體標識符是不同的:
[user@host ~]$ dmesg | egrep '^da.*?: <' da0: <VMware, VMware Virtual S 1.0> Fixed Direct Access SCSI-2 device da1: <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device da2: <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device da3: <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device da4: <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device da5: <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device da6: <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device da7: <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device da8: <ATA ST3500418AS CC35> Fixed Direct Access SCSI-5 device da9: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device da10: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device da11: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device da12: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device
我確實記得所有驅動器的大小都相同。現在查看驅動器,它顯示其中三個的大小發生了變化,它們縮小了 2 MB:
[user@host ~]$ dmesg | egrep '^da.*?: .*?MB ' da0: 10240MB (20971520 512 byte sectors: 255H 63S/T 1305C) da1: 476940MB (976773168 512 byte sectors: 255H 63S/T 60801C) da2: 476940MB (976773168 512 byte sectors: 255H 63S/T 60801C) da3: 476940MB (976773168 512 byte sectors: 255H 63S/T 60801C) da4: 476940MB (976773168 512 byte sectors: 255H 63S/T 60801C) da5: 476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <-- da6: 476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <-- da7: 476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <-- da8: 476940MB (976773168 512 byte sectors: 255H 63S/T 60801C) da9: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C) da10: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C) da11: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C) da12: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C)
因此,從外觀上看,它不是“將引導載入程序寫入驅動器”的作業系統安裝之一(正如我之前所假設的那樣),它實際上是創建 2 MB主機的新主機板(華碩 P8P67 LE )三個驅動器末尾的受保護區域弄亂了我的 ZFS 元數據。
為什麼它沒有在所有驅動器上創建 HPA?我相信這是因為 HPA 創建只在較舊的驅動器上完成,該驅動器具有稍後通過 Seagate 硬碟驅動器 BIOS 更新修復的錯誤:當整個事件在幾週前開始時,我執行 Seagate 的SeaTools以檢查是否存在驅動器有任何物理問題(仍在舊硬體上),我收到一條消息,告訴我我的一些驅動器需要更新 BIOS。當我現在試圖重現該消息的確切細節和韌體更新下載的連結時,似乎由於主機板創建了 HPA,兩個 SeaTools DOS 版本都無法檢測到有問題的硬碟驅動器 - 快速
invalid partition
或類似當他們開始時閃過,就是這樣。具有諷刺意味的是,他們確實找到了一套三星驅動器。(我已經跳過了在非網路系統上使用 FreeDOS shell 的痛苦、耗時且最終徒勞的細節。)最後,我在另一台機器上安裝了 Windows 7 以執行 SeaTools Windows版本 1.2.0.5。只是關於 DOS SeaTools 的最後一句話:不要費心嘗試獨立啟動它們 - 相反,花幾分鐘時間,用很棒的Ultimate Boot CD製作一個可啟動的 USB 記憶棒- 除了 DOS SeaTools 之外,它還為您提供了許多其他功能有用的工具。
啟動時,SeaTools for Windows 會顯示此對話框:
這些連結指向序列號檢查器(由於某種原因,它受驗證碼保護 - 我的是“入侵使用者”)和有關韌體更新的知識庫文章。可能還有更多特定於硬碟型號的連結和一些下載等等,但我暫時不會遵循這條路徑:
我不會急於一次更新三個驅動器的韌體,這些驅動器具有截斷的分區並且是損壞的儲存池的一部分。那是自找麻煩。對於初學者來說,韌體更新很可能無法撤消 - 這可能會不可逆轉地破壞我取回數據的機會。
因此,接下來我要做的第一件事是對驅動器進行映像並使用副本,因此如果出現任何問題,可以返回原件。這可能會引入額外的複雜性,因為 ZFS 可能會注意到驅動器已被交換(通過驅動器序列號或另一個 UUID 或其他方式),即使它是位精確的 dd 副本到同一硬碟驅動器模型上。此外,zpool 甚至都不是實時的。男孩,這可能會變得棘手。
然而,另一種選擇是使用原件並將鏡像驅動器作為備份,但是當原件出現問題時,我可能會遇到上述複雜性。吶,不好。
為了清除三個硬碟驅動器,這些硬碟驅動器將用作損壞池中帶有錯誤 BIOS 的三個驅動器的映像替換,我需要為現在在那裡的東西創建一些儲存空間,所以我將深入探勘硬體箱並從一些舊驅動器組裝一個臨時 zpool - 我也可以用它來測試 ZFS 如何處理交換 dd 驅動器。
這可能需要一段時間…
20111213-1930+1100
更新02:
這確實需要一段時間。我花了幾個月的時間在我的桌子上打開幾個打開的電腦機箱,掛著不同數量的硬碟驅動器堆棧,還帶著耳塞睡了幾個晚上,因為我無法在睡覺前關閉機器,因為它正在執行一些冗長的關鍵操作. 不過,我終於贏了!:-) 我在這個過程中也學到了很多東西,我想在這裡與處於類似情況的任何人分享這些知識。
這篇文章已經比任何 ZFS 文件伺服器停止工作的人有時間閱讀要長得多,所以我將在這裡詳細介紹,並根據下面的基本發現創建一個答案。
我深入探勘過時的硬體盒,以組裝足夠的儲存空間,以便將有缺陷的驅動器鏡像到的單個 500GB 驅動器中的東西移走。我還不得不從他們的 USB 盒中取出一些硬碟驅動器,這樣我就可以直接通過 SATA 連接它們。還有一些不相關的問題,當我將它們重新投入使用需要 zpool 更換時,一些舊驅動器開始出現故障,但我將跳過這一點。
**提示:**在某個階段,總共涉及大約 30 個硬碟驅動器。有了這麼多硬體,將它們正確堆疊是一個巨大的幫助。電纜鬆動或硬碟驅動器從您的辦公桌上掉下來肯定不會在此過程中提供幫助,並且可能會進一步損害您的數據完整性。
我花了幾分鐘時間製作了一些臨時的硬紙板硬碟固定裝置,它們確實有助於保持物品的分類:
具有諷刺意味的是,當我第一次連接舊驅動器時,我意識到那裡有一個舊 zpool 我必須創建用於測試舊版本的一些但不是所有失去的個人數據,所以雖然數據失去是有所減少,這意味著額外的文件來回移動。
最後,我將有問題的驅動器鏡像到備份驅動器,將這些驅動器用於 zpool,並將原始驅動器斷開連接。備份驅動器具有更新的韌體,至少 SeaTools 不會報告任何所需的韌體更新。我用一個簡單的 dd 從一個設備到另一個設備進行了鏡像,例如
sudo dd if=/dev/sda of=/dev/sde
我相信 ZFS 確實注意到了硬體變化(通過一些硬碟 UUID 或其他),但似乎並不在意。
然而,zpool 仍處於相同狀態,副本不足/數據損壞。
正如前面提到的HPA Wikipedia 文章中提到的,當 Linux 啟動時會報告主機保護區的存在,並且可以使用hdparm進行調查。據我所知,FreeBSD 上沒有可用的 hdparm 工具,但到了這個時候,我還是安裝了 FreeBSD 8.2 和 Debian 6.0 作為雙引導系統,所以我引導到 Linux:
user@host:~$ for i in {a..l}; do sudo hdparm -N /dev/sd$i; done ... /dev/sdd: max sectors = 976773168/976773168, HPA is disabled /dev/sde: max sectors = 976771055/976773168, HPA is enabled /dev/sdf: max sectors = 976771055/976773168, HPA is enabled /dev/sdg: max sectors = 976771055/976773168, HPA is enabled /dev/sdh: max sectors = 976773168/976773168, HPA is disabled ...
所以問題顯然是新主機板在驅動器末端創建了一個幾兆字節的 HPA,它“隱藏”了上面的兩個 ZFS 標籤,即阻止 ZFS 看到它們。
涉足 HPA 似乎是一項危險的業務。從 hdparm 手冊頁,參數 -N:
Get/set max visible number of sectors, also known as the Host Protected Area setting. ... To change the current max (VERY DANGEROUS, DATA LOSS IS EXTREMELY LIKELY), a new value should be provided (in base10) immediately following the -N option. This value is specified as a count of sectors, rather than the "max sector address" of the drive. Drives have the concept of a temporary (volatile) setting which is lost on the next hardware reset, as well as a more permanent (non-volatile) value which survives resets and power cycles. By default, -N affects only the temporary (volatile) setting. To change the permanent (non-volatile) value, prepend a leading p character immediately before the first digit of the value. Drives are supposed to allow only a single permanent change per session. A hardware reset (or power cycle) is required before another permanent -N operation can succeed. ...
就我而言,HPA 是這樣刪除的:
user@host:~$ sudo hdparm -Np976773168 /dev/sde /dev/sde: setting max visible sectors to 976773168 (permanent) max sectors = 976773168/976773168, HPA is disabled
對於其他帶有 HPA 的驅動器也是如此。如果你得到錯誤的驅動器或者你指定的大小參數不合理,hdparm 足夠聰明,可以計算:
user@host:~$ sudo hdparm -Np976773168 /dev/sdx /dev/sdx: setting max visible sectors to 976773168 (permanent) Use of -Nnnnnn is VERY DANGEROUS. You have requested reducing the apparent size of the drive. This is a BAD idea, and can easily destroy all of the drive's contents. Please supply the --yes-i-know-what-i-am-doing flag if you really want this. Program aborted.
之後,我重新啟動了最初創建 zpool 的 FreeBSD 7.2 虛擬機,zpool status 再次報告了一個工作池。耶!:-)
我在虛擬系統上導出了池,然後在主機 FreeBSD 8.2 系統上重新導入了它。
一些更重要的硬體升級,另一個主機板更換,ZFS 池更新到 ZFS 4 / 15,徹底清理,現在我的 zpool 包含 8x1TB 和 8x500GB raidz2 部分:
[user@host ~]$ sudo zpool status pool: zpool state: ONLINE scrub: none requested config: NAME STATE READ WRITE CKSUM zpool ONLINE 0 0 0 raidz2 ONLINE 0 0 0 ad0 ONLINE 0 0 0 ad1 ONLINE 0 0 0 ad2 ONLINE 0 0 0 ad3 ONLINE 0 0 0 ad8 ONLINE 0 0 0 ad10 ONLINE 0 0 0 ad14 ONLINE 0 0 0 ad16 ONLINE 0 0 0 raidz2 ONLINE 0 0 0 da0 ONLINE 0 0 0 da1 ONLINE 0 0 0 da2 ONLINE 0 0 0 da3 ONLINE 0 0 0 da4 ONLINE 0 0 0 da5 ONLINE 0 0 0 da6 ONLINE 0 0 0 da7 ONLINE 0 0 0 errors: No known data errors [user@host ~]$ df -h Filesystem Size Used Avail Capacity Mounted on /dev/label/root 29G 13G 14G 49% / devfs 1.0K 1.0K 0B 100% /dev zpool 8.0T 3.6T 4.5T 44% /mnt/zpool
最後一句話,在我看來,ZFS 池非常非常難以殺死。創建該系統的 Sun 公司的人有充分的理由稱其為文件系統中的硬道理。尊重!
問題在於新主機板的 BIOS 在某些驅動器上創建了主機保護區 (HPA),這是 OEM 用於系統恢復目的的一小部分,通常位於硬碟驅動器的末端。
ZFS 維護 4 個帶有分區元資訊的標籤,HPA 阻止 ZFS 看到上面的兩個標籤。
解決方案:啟動 Linux,使用 hdparm 檢查並刪除 HPA。要非常小心,這很容易永久破壞您的數據。有關詳細資訊,請參閱文章和 hdparm 手冊頁(參數 -N)。
這個問題不僅出現在新主機板上,我在將驅動器連接到 SAS 控制器卡時也遇到了類似的問題。解決方法是一樣的。