ZFS排错和池恢复(part 2)
解决ZFS存储池中的数据问题
数据问题的示例包括:
- 池或文件系统空间不足
- 由于磁盘或控制器损坏导致的瞬间I/O错误
- 宇宙射线导致的磁盘数据损坏
- 驱动程序错误导致数据传输到错误的位置或从错误的位置传输
- 用户意外覆盖了物理设备的一部分
在某些情况下,这些错误是暂时的,例如控制器出现问题时出现随机I/O错误。
在其他情况下,损坏是永久性的,例如磁盘损坏。
即使如此,损坏是否永久并不一定表明错误可能再次发生。例如,如果您意外地覆盖了磁盘的一部分,则不会发生任何类型的硬件故障,并且不需要更换设备。
识别设备的确切问题不是一项容易的任务,在后面的章节中会详细介绍。
解决ZFS空间问题
如果您不确定ZFS如何报告文件系统和池空间记帐,请查看以下部分。
ZFS文件系统空间报告
zpool list和zfs list命令在确定可用池和文件系统空间方面优于以前的df和du命令。
传统命令无法区分池空间和文件系统空间,也无法说明子文件系统或快照所占的空间。
例如,下面的池(rpool)分配了5.46GB空间和68.5GB的空余。
$ zpool list rpool
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
rpool 74G 5.46G 68.5G 7% 1.00x ONLINE -
如果通过查看各个文件系统的USED列将池空间核算与文件系统空间核算进行比较,则可以看到ALLOC中报告的池空间计入了文件系统的已使用总量。
例如:
$ zfs list -r rpool
NAME USED AVAIL REFER MOUNTPOINT
rpool 5.41G 67.4G 74.5K /rpool
rpool/ROOT 3.37G 67.4G 31K legacy
rpool/ROOT/solaris 3.37G 67.4G 3.07G /
rpool/ROOT/solaris/var 302M 67.4G 214M /var
rpool/dump 1.01G 67.5G 1000M -
rpool/export 97.5K 67.4G 32K /rpool/export
rpool/export/home 65.5K 67.4G 32K /rpool/export/home
rpool/export/home/admin 33.5K 67.4G 33.5K /rpool/export/home/admin
rpool/swap 1.03G 67.5G 1.00G -
top
ZFS存储池空间报告
zpool list命令输出结果中SIZE值通常指的是物理磁盘空间量,但根据池的冗余级别而有所不同。参见以下例子。
zfs list命令列出文件系统可用的空间,即磁盘空间减去zfs池冗余元素的开销(如果有)。
以下ZFS数据集配置由zfs list命令作为已分配空间进行跟踪,但在zpool list输出中不作为已分配空间进行跟踪:
- ZFS文件系统配额
- ZFS文件系统保留
- ZFS逻辑卷大小
以下项目描述了使用不同的池配置、ZFS卷和ZFS保留如何影响已消耗和可用的磁盘空间。根据配置,应使用下面列出的步骤耿总监视池空间。
- 无冗余存储池
使用一个136GB磁盘创建池时,zpool list命令会将SIZE和初始的FREE值报告为136GB。
zfs list命令将初始的AVAIL报告为134GB,这是由于池元数据的开销较小。例如:
$ zpool create system1 c0t6d0
$ zpool list system1
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
system1 136G 95.5K 136G 0% 1.00x ONLINE -
$ zfs list system1
NAME USED AVAIL REFER MOUNTPOINT
system1 72K 134G 21K /system1
- 镜像存储池
当使用两个136GB磁盘创建镜像池时,zpool list命令报告SIZE和初始FREE都为136GB。
此报告称为“缩小(deflated)空间值”。
zfs list命令报告的初始AVAIL空间为134GB,这是由于池元数据开销较小。例如:
$ zpool create system1 mirror c0t6d0 c0t7d0
$ zpool list system1
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
system1 136G 95.5K 136G 0% 1.00x ONLINE -
$ zfs list system1
NAME USED AVAIL REFER MOUNTPOINT
system1 72K 134G 21K /system1
- RAID-Z存储池
当使用三个136GB磁盘创建raidz2池时,zpool list命令报告SIZE和初始FREE都为408GB。
此报告称为“膨胀(inflated)空间值”,其中包括冗余开销,如奇偶校验信息。
由于池冗余开销,zfs list命令报告的初始“AVAIL”空间为133GB。RAID-Z池在zpool list和zfs list命令分别输出的空间差异是因为zpool list命令报告了膨胀的池空间。
$ zpool create system1 raidz2 c0t6d0 c0t7d0 c0t8d0
$ zpool list system1
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
system1 408G 286K 408G 0% 1.00x ONLINE -
$ zfs list system1
NAME USED AVAIL REFER MOUNTPOINT
system1 73.2K 133G 20.9K /system1
- NFS挂载的文件系统空间
对于NFS挂载的文件系统空间,命令zpool list和zfs list账户都不可用。但是,本地数据可以隐藏在已挂载的NFS文件系统下。
如果缺少文件系统空间,应确保没有数据文件隐藏在NFS文件系统下。
- 使用ZFS卷
创建ZFS文件系统并占用池空间时,可以使用zpool list命令查看文件系统空间占用情况。例如:
$ zpool create nova mirror c1t1d0 c2t1d0
$ zfs create nova/fs1
$ mkfile 10g /nova/fs1/file1_10g
$ zpool list nova
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
nova 68G 10.0G 58.0G 14% 1.00x ONLINE -
$ zfs list -r nova
NAME USED AVAIL REFER MOUNTPOINT
nova 10.0G 56.9G 32K /nova
nova/fs1 10.0G 56.9G 10.0G /nova/fs1
如果创建了10GB的ZFS卷,zpool list命令中不会占用该空间。空间在zfs list命令中进行说明。
如果在存储池中使用ZFS卷,应使用zfs list命令监视ZFS卷空间消耗。例如:
$ zfs create -V 10g nova/vol1
$ zpool list nova
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
nova 68G 10.0G 58.0G 14% 1.00x ONLINE -
$ zfs list -r nova
NAME USED AVAIL REFER MOUNTPOINT
nova 20.3G 46.6G 32K /nova
nova/fs1 10.0G 46.6G 10.0G /nova/fs1
nova/vol1 10.3G 56.9G 16K -
此外,由于ZFS卷的行为类似于原始设备,因此会通过refreservation属性自动为元数据保留一定量的空间,这回导致卷消耗的空间略大于创建卷时指定的空间量。
请勿删除ZFS卷上的refreservation,否则可能会耗尽卷空间。
- 使用ZFS保留
如果创建具有保留的文件系统或将保留添加到现有文件系统,则zpool list命令不会跟踪保留或重新保留。
通过使用zfs list -r命令识别增加的已用空间,确定文件系统保留所占用的空间。例如:
$ zfs create -o reservation=10g nova/fs2
$ zpool list nova
NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
nova 68G 10.0G 58.0G 14% 1.00x ONLINE -
$ zfs list -r nova
NAME USED AVAIL REFER MOUNTPOINT
nova 30.3G 36.6G 33K /nova
nova/fs1 10.0G 36.6G 10.0G /nova/fs1
nova/fs2 31K 46.6G 31K /nova/fs2
nova/vol1 10.3G 46.9G 16K -
如果使用refreservation创建文件系统,则可以使用zfs list -r命令来识别它。例如:
$ zfs create -o refreservation=10g nova/fs3
$ zfs list -r nova
NAME USED AVAIL REFER MOUNTPOINT
nova 40.3G 26.6G 35K /nova
nova/fs1 10.0G 26.6G 10.0G /nova/fs1
nova/fs2 31K 36.6G 31K /nova/fs2
nova/fs3 10G 36.6G 31K /nova/fs3
nova/vol1 10.3G 36.9G 16K -
使用以下命令标识所有现有保留,以说明总使用空间:
$ zfs get -r reserv,refreserv nova
NAME PROPERTY VALUE SOURCE
nova reservation none default
nova refreservation none default
nova/fs1 reservation none default
nova/fs1 refreservation none default
nova/fs2 reservation 10G local
nova/fs2 refreservation none default
nova/fs3 reservation none default
nova/fs3 refreservation 10G local
nova/vol1 reservation none default
nova/vol1 refreservation 10.3G local
top
检查ZFS文件系统完整性
ZFS不存在与fsck命令等效的实用程序。该实用程序传统上有两个用途:文件系统修复和文件系统验证。
文件系统修复
对于传统的文件系统,写入数据的方式本质上容易受到意外故障的影响,从而导致文件系统不一致。
因为传统的文件系统不是事务性的,所以可能存在未引用的块、错误的链接计数或其他不一致的文件系统结构。
日志记录的添加确实解决了其中一些问题,但当日志无法回滚时,可能会带来其他问题。
如果ZFS中的硬件或软件配置不一致,则ZFS中的数据池应以不一致的方式存在。
fsck实用程序修复特定于UFS文件系统的已知问题。
大多数ZFS存储池问题通常与硬件故障或电源故障有关。使用冗余池可以避免许多问题。
如果您的池由于硬件故障或断电而损坏,请参阅修复ZFS存储池范围的损坏。
如果您的池不是冗余的,则文件系统损坏可能导致部分或全部数据无法访问的风险始终存在。
文件系统验证
除了执行文件系统修复外,fsck实用程序还验证磁盘上的数据是否没有问题。
传统上,此任务需要卸载文件系统并运行fsck实用程序,在此过程中可能会将系统转换为单用户模式。这种情况导致的停机时间与所检查的文件系统的大小成正比。
ZFS不需要显式实用程序来执行必要的检查,而是提供了一种机制来执行所有不一致性的例行检查。
这种被称为擦洗(scrubbing)的功能通常用于内存和其他系统中,作为一种在错误导致硬件或软件故障之前检测和防止错误的方法。
top
控制ZFS数据清理
每当ZFS遇到错误时,无论是通过清理还是在按需访问文件时,都会在内部记录该错误,以便可以快速查看池中的所有已知错误。
显式ZFS数据清理
检查数据完整性的最简单方法是启动池内所有数据的显式清理。
此操作将遍历池中的所有数据一次,并验证是否可以读取所有块。
尽管任何I/O的优先级仍低于正常操作的优先级,但清理仍以设备允许的速度进行。
此操作可能会对性能产生负面影响,尽管池的数据在清理过程中应该保持可用性,并且几乎具有同样的响应能力。
要启动显式清理,请使用zpool scrub命令。例如:
$ zpool scrub system1
实用zpool status命令可以显示当前擦洗操作的状态。例如:
$ zpool status -v system1
pool: system1
state: ONLINE
scan: scrub in progress since Mon Jun 7 12:07:52 2010
201M scanned out of 222M at 9.55M/s, 0h0m to go
0 repaired, 90.44% done
config:
NAME STATE READ WRITE CKSUM
system1 ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
c1t0d0 ONLINE 0 0 0
c1t1d0 ONLINE 0 0 0
errors: No known data errors
每个池同时只能有一个活动的擦洗操作。
可以实用-s选项停止正在进行的擦洗操作:
$ zpool scrub -s system1
在大多数情况下,为确保数据完整性而进行的擦洗操作应持续完成。如果系统性能受到擦洗操作的影响,请自行决定停止擦洗操作。
执行例行擦洗可确保系统上所有磁盘的连续I/O。例行擦洗具有防止电源管理将空闲磁盘置于低功耗模式的副作用。
如果系统通常一直在执行I/O,或者如果功耗不是问题,则可以安全地忽略此问题。
如果系统在很大程度上是空闲的,并且您想节省磁盘的功率,则应该考虑使用cron计划的明确擦洗而不是后台擦洗。这仍然会执行完整的数据擦洗,尽管在擦洗完成之前,它只会生成大量的I/O,此时磁盘可以正常进行电源管理。
缺点(除了增加的I/O)是,在很长一段时间内根本不会进行擦洗,这可能会增加这些期间的数据损坏风险。
计划数据清理
随着时间的推移,可能会出现数据不一致的情况。定期擦洗数据有助于发现这些不一致并尽早解决它们。因此,定期擦洗可确保数据可用性。
在此版本中,添加了自动擦洗作为预防性维护工具。自动或计划的数据清理现在是日常操作的一部分。
默认情况下,此功能在安装时启用。两个ZFS属性与计划清理关联:(以下两个属性FreeBSD中没有)
可以在计划的时间之外手动启动擦洗操作。如果此时计划的擦洗正在进行,则手动的操作将失败。
就像手动清理一样,您可以取消正在进行的计划清理。
在这种情况下,将计划在间隔指定的下一个时段运行清理操作,并从取消清理的开始时间开始计算。
要取消正在进行的清理操作,请使用以下命令:
$ zpool scrub -s
仅当没有其他清理或重定大小器操作正在进行时,计划的清理才会运行。
启动resilver操作时,正在进行的计划清理将立即取消,并将在resliver完成后重新启动。
ZFS数据清理和重定大小
更换设备时,将启动重定大小操作,以将数据从完好副本移动到新设备。此操作是磁盘清理的一种形式。
因此,在池中的给定时间只能发生一个这样的操作。如果正在进行清理操作,则重定位操作将暂停当前清理,并在重定位完成后重新启动。
top
修复损坏的ZFS数据
当一个或多个设备错误(指示一个或多个丢失或损坏的设备)影响顶级虚拟设备时,会发生数据损坏。
例如,镜像的一半可能会遇到数千个设备错误,而不会导致数据损坏。
如果镜像的另一侧在完全相同的位置遇到错误,则会导致数据损坏。
数据损坏总是永久性的,在修复期间需要特别考虑。即使底层设备被修复或更换,原始数据也将永远丢失。
通常,此场景需要从备份中恢复数据。数据错误会在遇到时记录下来,并且可以通过下一节中解释的例行池清理来控制这些错误。
删除损坏的块后,下一次清理过程将识别损坏不再存在,并从系统中删除任何错误跟踪。
ZFS使用校验和、冗余和自愈数据将数据损坏的风险降至最低。
尽管如此,如果池不是冗余的,如果池降级时发生损坏,或者一系列不太可能的事件合谋损坏一段数据的多个副本,则可能会发生数据损坏。
无论来源如何,结果都是一样的:数据已损坏,因此不再可访问。
所采取的操作取决于被破坏的数据类型及其相对值。两种基本类型的数据可能会损坏:
- 池元数据
ZFS需要解析一定数量的数据才能打开池并访问数据集。如果此数据已损坏,则整个池或数据集层次结构的一部分将不可用。
- 对象数据
在这种情况下,损坏发生在特定的文件或目录中。此问题可能导致文件或目录的一部分无法访问,或者此问题可能导致对象完全中断。
数据在正常操作期间以及通过清洗进行验证。
以下各节介绍如何识别数据损坏的类型以及如何修复数据(如果可能)。
识别数据损坏的类型
默认情况下,zpool status命令仅显示发生了损坏,而不显示此损坏发生的位置。例如:
$ zpool status system1
pool: system1
state: ONLINE
status: One or more devices has experienced an error resulting in data
corruption. Applications may be affected.
action: Restore the file in question if possible. Otherwise restore the
entire pool from backup.
see: http://support.oracle.com/msg/ZFS-8000-8A
config:
NAME STATE READ WRITE CKSUM
system1 ONLINE 4 0 0
c0t5000C500335E106Bd0 ONLINE 0 0 0
c0t5000C500335FC3E7d0 ONLINE 4 0 0
errors: 2 data errors, use '-v' for a list
每个错误仅表示在给定的时间点发生错误。每个错误不一定仍然存在于系统上。
在正常情况下,情况就是这样。某些临时中断可能会导致数据损坏,在中断结束后会自动修复这些损坏。
池的完整清理保证检查池中的每个活动块,因此每次清理完成时都会重置错误日志。
如果确定错误不再存在,并且不想等待清理完成,请使用zpool online命令重置池中的所有错误。
如果数据损坏发生在池范围的元数据中,则输出略有不同。例如:
$ zpool status -v morpheus
pool: morpheus
id: 13289416187275223932
state: UNAVAIL
status: The pool metadata is corrupted.
action: The pool cannot be imported due to damaged devices or data.
see: http://support.oracle.com/msg/ZFS-8000-72
config:
morpheus FAULTED corrupted data
c1t10d0 ONLINE
在池范围损坏的情况下,池将处于FAULTED状态,因为池无法提供所需的冗余级别。
top
修复损坏的文件或目录
如果文件或目录已损坏,系统可能仍能正常工作,具体取决于损坏的类型。
如果系统上不存在数据的良好副本,则任何损坏实际上都是不可恢复的。
如果数据有价值,则必须从备份中恢复受影响的数据。
即使如此,您也可以从这种损坏中恢复,而无需恢复整个池。
如果损坏在文件数据块内,则可以安全地删除该文件,从而清除系统中的错误。
使用zpool status –v命令显示具有持续错误的文件名列表。例如:
$ zpool status system1 -v
pool: system1
state: ONLINE
status: One or more devices has experienced an error resulting in data
corruption. Applications may be affected.
action: Restore the file in question if possible. Otherwise restore the
entire pool from backup.
see: http://support.oracle.com/msg/ZFS-8000-8A
config:
NAME STATE READ WRITE CKSUM
system1 ONLINE 4 0 0
c0t5000C500335E106Bd0 ONLINE 0 0 0
c0t5000C500335FC3E7d0 ONLINE 4 0 0
errors: Permanent errors have been detected in the following files:
/system1/file.1
/system1/file.2
具有持续错误的文件名列表可能描述如下:
- 如果找到文件的完整路径并装入数据集,则会显示文件的完整路径。例如:
/path1/a.txt
- 如果找到了文件的完整路径,但未装入数据集,则会显示数据集名称(前面没有斜杠(/)),后面是数据集中到文件的路径。例如:
path1/documents/e.txt
- 如果由于错误或对象没有与之关联的实际文件路径(如dnode_t),无法成功将对象编号转换为文件路径,则会显示数据集名称后跟对象编号。例如:
path1/dnode:<0x0>
- 如果元对象集(MOS)中的对象已损坏,则会显示一个特殊标记<metadata>,后跟对象编号。
可以尝试通过在多次迭代中清除池并清除池错误来解决更轻微的数据损坏。如果第一次清理和清除迭代无法解决损坏的文件,请再次运行它们。例如:
$ zpool scrub system1
$ zpool clear system1
如果损坏发生在目录或文件元数据中,则唯一的选择是将文件移到其他位置。
可以安全地将任何文件或目录移动到不太方便的位置,从而可以将原始对象还原到原来的位置。
如果损坏的文件系统具有多个块引用(如快照)的损坏数据,则zpool status –v命令无法显示所有损坏的数据路径。
损坏数据的当前zpool status报告受元数据损坏量以及执行zpool status命令后是否重用了任何块的限制。
消除重复的数据块使报告所有损坏的数据变得更加复杂。
如果数据已损坏,并且zpool status –v命令确定快照数据受到影响,则考虑运行以下命令以确定其他损坏路径:
$ find mount-point -inum $inode -print
$ find mount-point/.zfs/snapshot -inum $inode -print
第一个命令搜索指定文件系统及其所有快照中报告的损坏数据的索引节点号。
第二个命令搜索具有相同inode编号的快照。
top
修复ZFS存储池范围内的损坏
如果损坏位于池元数据中,并且该损坏阻止打开或导入池,则您可以使用以下选项:
- 可以尝试使用zpool clear –F命令或zpool import –F命令恢复池。这些命令试图将最后几个池事务回滚到操作状态。您可以使用zpool status命令查看损坏的池和建议的恢复步骤。例如:
$ zpool status
pool: storpool
state: UNAVAIL
status: The pool metadata is corrupted and the pool cannot be opened.
action: Recovery is possible, but will result in some data loss.
Returning the pool to its state as of Fri Jun 29 17:22:49 2012
should correct the problem. Approximately 5 seconds of data
must be discarded, irreversibly. Recovery can be attempted
by executing 'zpool clear -F tpool'. A scrub of the pool
is strongly recommended after recovery.
see: http://support.oracle.com/msg/ZFS-8000-72
scrub: none requested
config:
NAME STATE READ WRITE CKSUM
storpool UNAVAIL 0 0 1 corrupted data
c1t1d0 ONLINE 0 0 2
c1t3d0 ONLINE 0 0 4
前面输出中描述的恢复过程是使用以下命令:
$ zpool clear -F storpool
如果尝试导入损坏的存储池,将看到类似以下消息:
$ zpool import storpool
cannot import 'storpool': I/O error
Recovery is possible, but will result in some data loss.
Returning the pool to its state as of Fri Jun 29 17:22:49 2012
should correct the problem. Approximately 5 seconds of data
must be discarded, irreversibly. Recovery can be attempted
by executing 'zpool import -F storpool'. A scrub of the pool
is strongly recommended after recovery.
前面输出中描述的恢复过程是使用以下命令:
$ zpool import -F storpool
Pool storpool returned to its state as of Fri Jun 29 17:22:49 2012.
Discarded approximately 5 seconds of transactions
如果损坏的池位于zpool.cache文件中,则在系统启动时会发现问题,并在zpool status命令中报告损坏的池。如果池不在zpool.cache文件中,它将无法成功导入或打开,并且在尝试导入池时,您将看到损坏的池消息。
- 可以以只读模式导入损坏的池。此方法使您能够导入池,以便访问数据。例如:
$ zpool import -o readonly=on storpool
- 可以使用zpool import –m命令导入缺少日志设备的池。
- 如果两种池恢复方法都无法恢复池,则必须从备份副本中恢复池及其所有数据。根据池配置和备份策略的不同,您使用的机制差异很大。
首先,保存zpool status命令显示的配置,以便在销毁池后重新创建它。然后,使用zpool destroy –f命令销毁池。
另外,将描述数据集布局和各种本地设置属性的文件保存在安全的地方,因为如果池被呈现为不可访问,则这些信息将变得不可访问。
使用池配置和数据集布局,您可以在销毁池后重建完整的配置。然后,可以使用您使用的任何备份或恢复策略填充数据。
top
修复损坏的ZFS配置
ZFS在根文件系统中维护活动池及其配置的缓存。
如果此缓存文件已损坏或与存储在磁盘上的配置信息不同步,则无法再打开池。
ZFS试图避免这种情况,尽管考虑到底层存储的质量,任意损坏总是可能的。
这种情况通常会导致一个本来应该可用的池从系统中消失。这种情况还可以表现为缺少未知数量的顶级虚拟设备的部分配置。
在任何一种情况下,都可以通过导出池(如果池可见)并重新导入来恢复配置。
top
修复不可启动的系统
ZFS被设计为具有强壮性和稳定性,尽管存在错误。
即使如此,当访问池时,软件错误或某些意外问题可能会导致系统死机。
作为启动过程的一部分,必须打开每个池,这意味着此类故障将导致系统进入紧急重启循环。
要从这种情况中恢复,必须通知ZFS不要在启动时查找任何池。
ZFS在/etc/zfs/zpool.cache中维护可用池及其配置的内部缓存。此文件的位置和内容是私有的,可能会发生更改。
如果系统变得不可引导,则使用–m milestone=none引导选项引导至里程碑none。
系统启动后,将根文件系统重新装载为可写,然后重命名/etc/zfs/zpool.cache文件或将其移动到其他位置。
这些操作会导致ZFS忘记系统上存在任何池,从而阻止其尝试访问导致问题的不健康池。
然后,可以通过发出svcadm milestone all命令进入正常系统状态。当从备用根目录引导以执行修复时,可以使用类似的过程。
系统启动后,可以尝试使用zpool import命令导入池。但是,这样做可能会导致启动期间发生的相同错误,因为该命令使用相同的机制访问池。
如果系统上存在多个池,请执行以下操作:
- 将zpool.cache文件重命名或移动到其他地方
- 通过使用fmdump -eV命令显示报告有致命错误的池,确定哪个池可能有问题
- 逐个导入池,跳过有问题的池(如fmdump输出中所述)
top