Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

题外话

我们先讲什么是文件系统和需要的工具。

文件系统

现在的存储种类众多、堆栈繁杂。但是用户直接的接口还是文件系统。对于Linux和开源社区而言,众多的软件也是依赖文件系统。比如MySQL和PostgreSQL数据库就是直接调用文件系统接口的,而不像Oracle数据库,能够越过文件系统层。

Linux系统上文件系统众多。但是比较常用的是ExtXfs文件系统。当前使用比较普遍的是Ext4文件系统。

这里我们以Ext4文件系统为例。

三大利器

关于工具的重要性我们就不絮叨了。解决启停问题,一定要有三大利器的辅助:快照、VNC和录屏工具。

关于这些工具,请参考Linux系统诊断小技巧(13):启停问题之如何修复grub损坏

bind挂载

修复启停时经常需要做chroot操作,即我们要在一个新的根文件系统内操作。但是,只执行chroot是不够的。我们还需要挂载/dev、/proc和/sys目录。这样才能使用在chroot后继续使用系统数据。

关于bind挂载,同样请参考Linux系统诊断小技巧(13):启停问题之如何修复grub损坏有关章节。

回到话题

文件系统损坏,是导致系统启动失败比较常见的原因。文件系统损坏,比较常见的原因是分区丢失和文件系统需要手工修复。

现场复现

我们人为的破坏分区表信息。

准备测试环境

我们先准备一个供测试的文件系统。具体操作如下

#
# 下面我们以第二块磁盘为例,并且假设其磁盘路径是/dev/vdb
#
# 1. 检查新磁盘的情况
fdisk -l -u /dev/vdb
# 2. 新建分区
fdisk /dev/vdb
# 3. 创建文件系统
mkfs.ext4 /dev/vdb1
# 4. 配置系统启动挂载新建文件系统
echo '/dev/vdb1 /opt ext4    defaults 0       2' >> /etc/fstab
# 5. 重启系统以便验证配置符合预期
reboot
# 6. 验证挂载成功
mount -l | egrep /dev/vdb1

先看准备文件系统的具体操作

[root@iz2ze122w6gewwurz1e637z ~]# fdisk -l -u /dev/vdb 1. 检查新磁盘的情况

Disk /dev/vdb: 1099.5 GB, 1099511627776 bytes, 2147483648 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x00088bd9

   Device Boot      Start         End      Blocks   Id  System
[root@iz2ze122w6gewwurz1e637z ~]# fdisk /dev/vdb # 2. 新建分区
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 
First sector (2048-2147483647, default 2048): 
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-2147483647, default 2147483647): 
Using default value 2147483647
Partition 1 of type Linux and of size 1024 GiB is set

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
[root@iz2ze122w6gewwurz1e637z ~]# mkfs.ext4 /dev/vdb1 # 3. 创建文件系统
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
67108864 inodes, 268435200 blocks
13421760 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2415919104
8192 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
    4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
    102400000, 214990848

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done     

[root@iz2ze122w6gewwurz1e637z ~]# echo '/dev/vdb1 /opt ext4    defaults 0       2' >> /etc/fstab # 4. 配置系统启动挂载新建文件系统

现在重启系统,验证挂载情况

Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

可见挂载是成功的。

损坏测试:fstab配置错误

我们通过下面的方式测试下

# defaults -> default
# 可以直接手工调整
# 可以用ed
ed -s /etc/fstab <<EOF
/\/dev\/vdb1/s/defaults/default/
w
EOF
# 当然也可以使用sed
sed -i -e '/\/dev\/vdb1/s/defaults/default/' /etc/fstab

Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

看下启动失败情况(后续的记录系统抱怨截图时的按键无法识别)。系统启动中断了

Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

损坏测试:分区表丢失

我们先把文件系统的挂载恢复正常,再手工破坏分区表进行测试

# 1. 卸载文件系统
umount /dev/vdb1
# 2. 检查分区情况
fdisk -l -u /dev/vdb
# 3. 直接使用fdisk来删除分区
fdisk -u /dev/vdb
# 4. 验证删除了分区
fdisk -l -u /dev/vdb

操作示例如下
Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

看下启动失败情况(系统启动过程止步不前,但是也没有明显的报错信息。所以录屏工具是有必要的)

Linux系统诊断小技巧(15):启停问题之如何修复文件系统损坏

如何修复问题?

因为当前实例启动失败,这种情况下在当前实例上,我们没有用shell来解决问题。所以,我们需要先解决在哪里来修复的问题。

解决这个问题的方案比较多,运维人员常见的手段是使用LiveCD。这里我们推荐使用快照的方式。具体使用请参考Linux系统诊断小技巧(13):启停问题之如何修复grub损坏的有关信息。

配置错误

这种情况只要把错误配置修改完毕即可解决问题,我们略过不谈。

分区表丢失问题

修复方案的依据

分区表丢失,只要重新创建分区表即可。因为分区表信息只涉及变更磁盘上第1个扇区指定位置的内容。所以,只要确认有分区情况,在分区表丢失的情况下,重做分区是不会损坏磁盘上的数据的。但是分区起始位置和尺寸需要正确,否则不能解决问题。

分区的大小,因为大家(基本)都是一个磁盘分一个区,所以(几乎)不用考虑分区大小(从文件系统信息是可以计算分区大小的)。起始位置确定后,大小选择fdisk给出的就好。所以,问题的关键是如何确定分区的开始位置。

我们以常见的Ext文件系统为例,具体请参看如何从Ext3或者Ext4文件系统推断分区位置

重建分区

重建分区可以按照下面的步骤来实现。

确定分区起始位置

首先,我们需要先确认分区上的文件系统信息。这有多种方式:

  1. 查看/etc/fstab/etc/mtab配置文件。
  2. 如果文件系统还是挂载情形,则查看/proc/mounts
  3. 直接检查文件系统魔数。
  4. 从系统日志追查。

我们看下直接检查文件系统魔数的例子,并用fdisk工具核实下

#
# 1. ext*文件系统的魔数是: 53 ef
#
[root@iz2ze122w6gewwurz1e637z ~]# dd if=/dev/vdb bs=512 count=4096 2>/dev/null | \
> od -tx1 | perl -ne '
>     chomp;
>     if (/^([0-7]+)\s # 磁盘数据的位置
>        ([0-9a-f][0-9a-f]\s){8} # 越过无关数据
>        53\sef\s # 模数
>        0[124]\s00\s0[123]\s00\s # 文件系统状态和出错后的行为配置
>        /x) {
>              my $s=int((oct $1)/512)-2;
>              print qq[$s $_\n];
>             }'
56 0072060 72 46 cd 5b 00 00 ff ff 53 ef 01 00 01 00 00 00
#
# 2. 检查到魔数,即是Ext*文件系统。从魔数反推出的分区起始扇区是56扇区。
#
[root@iz2ze122w6gewwurz1e637z ~]# fdisk -l -u /dev/vdb

磁盘 /dev/vdb:1099.5 GB, 1099511627776 字节,2147483648 个扇区
Units = 扇区 of 1 * 512 = 512 bytes
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x00088bd9

   设备 Boot      Start         End      Blocks   Id  System
/dev/vdb1              56  2147483647  1073741796   83  Linux
[root@iz2ze122w6gewwurz1e637z ~]#
#
# 3. fdisk给出的起始扇区是56。这与我们从魔数推断到的结果是一致的。
#

修复

示例:复现问题现场

我们如果通过fdisk工具来对/dev/vdb重新分区。如果在/etc/fstab中做了配置,那么重启会失败。具体失败报错请参考上面的信息。如果不重启直接挂载,则会遇到下面的报错

#
# 1. 直接挂载
#
[root@iz2ze122w6gewwurz1e637z ~]# mount /dev/vdb1 /opt
mount: /dev/vdb1 is write-protected, mounting read-only
mount: unknown filesystem type '(null)'
#
# 2. 挂载失败了
# 
[root@iz2ze122w6gewwurz1e637z ~]# mount -l | egrep /dev/vdb
#
# 3.为什么挂载失败了呢?因为fdisk把分区起始位置设定为2048了
#
[root@iz2ze122w6gewwurz1e637z ~]# fdisk -u -l /dev/vdb

Disk /dev/vdb: 1099.5 GB, 1099511627776 bytes, 2147483648 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x00088bd9

   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048  2147483647  1073740800   83  Linux
[root@iz2ze122w6gewwurz1e637z ~]# 
重建分区

因为fdisk工具总是把起始扇区设定为2048,不能调整,所以我们使用parted工具。具体修复过程如下

[root@iz2ze122w6gewwurz1e637z ~]# parted /dev/vdb
GNU Parted 3.1
Using /dev/vdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
#
# 1. 使用扇区作为默认单位
#
(parted) unit s         
#
# 2. 检查当前的分区情况
#
(parted) print                                                            
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 2147483648s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start  End          Size         Type     File system  Flags
 1      2048s  2147483647s  2147481600s  primary

#
# 3. 删除问题分区
#
(parted) rm 1
#
# 4. 重建分区
#
(parted) mkpart                                                           
Partition type?  primary/extended? primary                                
File system type?  [ext2]? ext4                                           
Start? 56       # 起始扇区调整为56                                            
End? 2147483647 # 扇区编号是从0开始的。因此最后一个扇区,是扇区总数减1
Warning: The resulting partition is not properly aligned for best performance.
Ignore/Cancel? Ignore # 这是我们所要的分区情况,因此忽略报警
(parted) q                                                                
Information: You may need to update /etc/fstab.

#
# 5. 分区重建成功
#
[root@iz2ze122w6gewwurz1e637z ~]# tune2fs -l /dev/vdb1  
tune2fs 1.42.9 (28-Dec-2013)
Filesystem volume name:   <none>
Last mounted on:          <not available>
Filesystem UUID:          0d51624e-5f97-46bb-8423-63a6e5e72d1c
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash 
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              67108864
Block count:              268435449
Reserved block count:     13421772
Free blocks:              264170080
Free inodes:              67108853
First block:              0
Block size:               4096
Fragment size:            4096
Group descriptor size:    64
Reserved GDT blocks:      1024
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
Flex block group size:    16
Filesystem created:       Mon Oct 22 11:39:29 2018
Last mount time:          Mon Oct 22 12:12:30 2018
Last write time:          Mon Oct 22 12:12:30 2018
Mount count:              1
Maximum mount count:      -1
Last checked:             Mon Oct 22 11:39:29 2018
Check interval:           0 (<none>)
Lifetime writes:          134 MB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:              256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
Default directory hash:   half_md4
Directory Hash Seed:      0f70f076-e2f1-4f0e-bc37-e950a58e0d0c
Journal backup:           inode blocks
[root@iz2ze122w6gewwurz1e637z ~]# mount /dev/vdb1 /opt
[root@iz2ze122w6gewwurz1e637z ~]# 
[root@iz2ze122w6gewwurz1e637z ~]# mount -l | egrep /dev/vdb1
/dev/vdb1 on /opt type ext4 (rw,relatime,data=ordered)
[root@iz2ze122w6gewwurz1e637z ~]#

终焉

祝探索愉快。

上一篇:CDN新品发布:阿里云SCDN安全加速开放公测


下一篇:Linux系统诊断小技巧(14):启停问题之如何修复initrd损坏