题外话
我们先讲什么是文件系统和需要的工具。
文件系统
现在的存储种类众多、堆栈繁杂。但是用户直接的接口还是文件系统。对于Linux和开源社区而言,众多的软件也是依赖文件系统。比如MySQL和PostgreSQL数据库就是直接调用文件系统接口的,而不像Oracle数据库,能够越过文件系统层。
Linux系统上文件系统众多。但是比较常用的是Ext和Xfs文件系统。当前使用比较普遍的是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. 配置系统启动挂载新建文件系统
现在重启系统,验证挂载情况
可见挂载是成功的。
损坏测试: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
看下启动失败情况(后续的记录系统抱怨截图时的按键无法识别)。系统启动中断了
损坏测试:分区表丢失
我们先把文件系统的挂载恢复正常,再手工破坏分区表进行测试
# 1. 卸载文件系统
umount /dev/vdb1
# 2. 检查分区情况
fdisk -l -u /dev/vdb
# 3. 直接使用fdisk来删除分区
fdisk -u /dev/vdb
# 4. 验证删除了分区
fdisk -l -u /dev/vdb
操作示例如下
看下启动失败情况(系统启动过程止步不前,但是也没有明显的报错信息。所以录屏工具是有必要的)
如何修复问题?
因为当前实例启动失败,这种情况下在当前实例上,我们没有用shell来解决问题。所以,我们需要先解决在哪里来修复的问题。
解决这个问题的方案比较多,运维人员常见的手段是使用LiveCD。这里我们推荐使用快照的方式。具体使用请参考Linux系统诊断小技巧(13):启停问题之如何修复grub损坏的有关信息。
配置错误
这种情况只要把错误配置修改完毕即可解决问题,我们略过不谈。
分区表丢失问题
修复方案的依据
分区表丢失,只要重新创建分区表即可。因为分区表信息只涉及变更磁盘上第1个扇区指定位置的内容。所以,只要确认有分区情况,在分区表丢失的情况下,重做分区是不会损坏磁盘上的数据的。但是分区起始位置和尺寸需要正确,否则不能解决问题。
分区的大小,因为大家(基本)都是一个磁盘分一个区,所以(几乎)不用考虑分区大小(从文件系统信息是可以计算分区大小的)。起始位置确定后,大小选择fdisk给出的就好。所以,问题的关键是如何确定分区的开始位置。
我们以常见的Ext文件系统为例,具体请参看如何从Ext3或者Ext4文件系统推断分区位置。
重建分区
重建分区可以按照下面的步骤来实现。
确定分区起始位置
首先,我们需要先确认分区上的文件系统信息。这有多种方式:
- 查看/etc/fstab和/etc/mtab配置文件。
- 如果文件系统还是挂载情形,则查看/proc/mounts
- 直接检查文件系统魔数。
- 从系统日志追查。
我们看下直接检查文件系统魔数的例子,并用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 ~]#
终焉
祝探索愉快。