Skip to content

Latest commit

 

History

History
1216 lines (838 loc) · 62 KB

File metadata and controls

1216 lines (838 loc) · 62 KB

七、文件系统错误和恢复

第五章,网络故障诊断第六章,诊断和纠正防火墙问题,我们使用了相当多的工具来解决网络连接问题由于配置错误的路线和防火墙。 与网络相关的问题非常常见,两个示例问题也是常见的场景。 在本章中,我们将关注与硬件相关的问题,并从诊断文件系统错误开始。

*就像其他章节一样,我们将从发现的错误开始,并排除问题,直到找到原因和解决方案。 在此过程中,我们将发现诊断文件系统问题所需的许多不同命令和日志。

诊断文件系统错误

不像前面的章节中终端用户向我们报告问题,这一次我们为自己发现了一个问题。 当我们在数据库服务器上执行一些日常任务时,我们试图创建一个数据库备份,并收到以下错误:

[db]# mysqldump wordpress > /data/backups/wordpress.sql
-bash: /data/backups/wordpress.sql: Read-only file system

这个错误很有趣,因为它不一定来自mysqldump命令,而是来自写入/data/backups/wordpress.sql文件的 bash 重定向。

如果我们查看这个错误,它是非常特定的,我们试图将备份写到的文件系统是Read-onlyRead-only是什么意思?

只读文件系统

在 Linux 上定义和挂载文件系统时,您有许多选项,但有两个选项最能定义文件系统的可访问性。 两个选项是:rw用于读写,ro用于只读。 当使用读写选项挂载文件系统时,这意味着可以读取文件系统的内容,具有适当权限的用户可以将新的文件/目录写入文件系统。

当文件系统以只读模式挂载时,这意味着虽然用户可以读取文件系统,但新的写请求将被拒绝。

使用 mount 命令列出已挂载的文件系统

由于我们接收到的错误明确地说明文件系统是只读的,所以我们的下一个逻辑步骤是查看挂载在这个服务器上的文件系统。 为此,我们将使用mount命令:

[db]# mount
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=228500k,nr_inodes=57125,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,seclabel,mode=755)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=33,pgrp=1,timeout=300,minproto=5,maxproto=5,direct)
mqueue on /dev/mqueue type mqueue (rw,relatime,seclabel)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
nfsd on /proc/fs/nfsd type nfsd (rw,relatime)
/dev/sda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
192.168.33.13:/nfs on /data type nfs4 (rw,relatime,vers=4.0,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.33.12,local_lock=none,addr=192.168.33.13)

在处理文件系统时,mount命令非常有用。 它不仅可以用于显示已挂载的文件系统(如上面的命令所示),还可以用于附加(或挂载)和取消附加(卸载)文件系统。

挂载的文件系统

将文件系统称为挂载的文件系统是一种表示文件系统连接到服务器的常用方法。 对于文件系统,它们通常有两种状态,要么是附加的(挂载的)并且用户可以访问内容,要么是未附加的(卸载的)并且用户无法访问内容。 在本章后面,我们将介绍使用mount命令挂载和卸载文件系统。

mount命令并不是查看哪些文件系统已挂载或未挂载的唯一方法。 另一种方法是简单地读取/proc/mounts文件:

[db]# cat /proc/mounts 
rootfs / rootfs rw 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=228500k,nr_inodes=57125,mode=755 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev 0 0
devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0
tmpfs /sys/fs/cgroup tmpfs rw,seclabel,nosuid,nodev,noexec,mode=755 0 0
selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=33,pgrp=1,timeout=300,minproto=5,maxproto=5,direct 0 0
mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
nfsd /proc/fs/nfsd nfsd rw,relatime 0 0
/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0
192.168.33.13:/nfs /data nfs4 rw,relatime,vers=4.0,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.33.12,local_lock=none,addr=192.168.33.13 0 0

事实上,/proc/mounts文件的内容非常接近mount命令的输出,主要区别在于每行末尾有两个编号的列。 为了更好地理解这个文件和mount命令的输出,让我们更好地看看/proc/mounts中的/boot文件系统的条目:

/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0

/proc/mounts文件数据在六列——设备挂载点,文件系统类型选择【显示】,和两个未使用的列存在向后兼容性。 为了更好地理解这些值,让我们更好地理解这些列的。

第一列device指定文件系统使用的设备。 在上述示例中,/boot文件系统所在的设备为/dev/sda1

从设备(sda1)的名称,我们可以识别出一条关键信息。 这个设备是另一个设备的一个分区,我们可以通过设备名称末尾有一个数字来识别它。

设备,从名称上看似乎是一个物理驱动器(假设它是一个硬盘驱动器),并被命名为/dev/sda; 该驱动器至少有一个分区,该分区的设备名称为/dev/sda1。 每当一个驱动器上有分区时,这些分区被创建为它们自己的设备,每个设备被分配一个数字; 在本例中是 1,这意味着它是第一个分区。

使用 fdisk 列出可用分区

我们可以通过查看正在使用fdisk命令的/dev/sda设备来验证这一点:

[db]# fdisk -l /dev/sda

Disk /dev/sda: 42.9 GB, 42949672960 bytes, 83886080 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: 0x0009c844

 Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     1026047      512000   83  Linux
/dev/sda2         1026048    83886079    41430016   8e  Linux LVM

fdisk命令可能比较熟悉,因为它是一个用于创建磁盘分区的跨平台命令。 但是,它也可以用于列出分区。

在前面的命令中,我们使用–l(list)标志列出分区,后面跟着我们想要查看的设备—/dev/sda。 然而,fdisk命令向我们显示的远不止这个驱动器上可用的分区。 它还显示了磁盘的大小:

Disk /dev/sda: 42.9 GB, 42949672960 bytes, 83886080 sectors

我们可以在从fdisk命令打印的第一行中看到这一点,根据这一行,我们的设备/dev/sda的大小为42.9 GB。 如果我们往输出的底部看,我们还可以看到在这个磁盘上创建的分区:

 Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     1026047      512000   83  Linux
/dev/sda2         1026048    83886079    41430016   8e  Linux LVM

从上面的列表中可以看出,/dev/sda有两个分区/dev/sda1/dev/sda2。 使用fdisk,我们已经能够确定关于这个文件系统物理设备的相当多的细节。 如果我们继续看/proc/mounts的细节,我们应该能够找出一些其他非常有用的信息,如下:

/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0

上一行中的第二列挂载点表示这个文件系统被挂载到的路径。 在本例中,路径为/boot; /boot本身不过是/(根)文件系统上的一个目录。 但是,一旦设备/dev/sda1上存在的文件系统被装入/boot,现在它就是自己的文件系统了。

为了更好地理解这个概念,我们将使用mountumount命令来附加和分离/boot文件系统:

[db]# ls /boot/
config-3.10.0-123.el7.x86_64
grub
grub2
initramfs-0-rescue-dee83c8c69394b688b9c2a55de9e29e4.img
initramfs-3.10.0-123.el7.x86_64.img
initramfs-3.10.0-123.el7.x86_64kdump.img
initrd-plymouth.img
symvers-3.10.0-123.el7.x86_64.gz
System.map-3.10.0-123.el7.x86_64
vmlinuz-0-rescue-dee83c8c69394b688b9c2a55de9e29e4
vmlinuz-3.10.0-123.el7.x86_64

如果我们在/boot路径上执行一个简单的ls命令,我们可以在这个目录中看到相当多的文件。 从/proc/mounts文件和mount命令中,我们知道有一个文件系统附加到/boot:

[db]# mount | grep /boot
/dev/sda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

为了卸载或卸载这个文件系统,我们可以使用umount命令:

[db]# umount /boot
[db]# mount | grep /boot

umount命令有一个非常简单的任务,它卸载挂载的文件系统。

提示

上述命令是卸载文件系统的示例,说明卸载文件系统可能是危险的。 通常,在卸载文件系统之前,您应该首先验证文件系统没有被积极地访问。

既然现在已经卸载了/boot文件系统,那么当我们执行ls命令时会发生什么呢?

# ls /boot

路径/boot仍然有效。 然而,它现在只是一个空目录。 这是由于/dev/sda1上的文件系统没有安装; 因此,该文件系统上存在的任何文件目前都不能在这个系统上访问。

如果我们使用mount命令重新挂载文件系统,我们将看到文件重新出现:

[db]# mount /boot
[db]# ls /boot
config-3.10.0-123.el7.x86_64
grub
grub2
initramfs-0-rescue-dee83c8c69394b688b9c2a55de9e29e4.img
initramfs-3.10.0-123.el7.x86_64.img
initramfs-3.10.0-123.el7.x86_64kdump.img
initrd-plymouth.img
symvers-3.10.0-123.el7.x86_64.gz
System.map-3.10.0-123.el7.x86_64
vmlinuz-0-rescue-dee83c8c69394b688b9c2a55de9e29e4
vmlinuz-3.10.0-123.el7.x86_64

正如我们所看到的,当给mount命令一个 path 参数时,该命令将尝试mount该文件系统。 但是,当没有指定参数时,mount命令将只显示当前挂载的文件系统。

在本章的后面,我们将探索使用mount以及它如何理解应该在哪里以及如何安装文件系统; 现在,让我们看看/proc/mounts输出中的下一列:

/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0

第三列文件系统类型表示所使用的文件系统类型。 在许多操作系统中,尤其是 Linux,通常可以使用不止一种类型的文件系统。 在前面的例子中,我们的引导文件系统被设置为xfs,这在 Red Hat Enterprise Linux 7 中是新的默认文件系统。

xfs之前,旧版本的 Red Hat 默认使用ext3ext4文件系统。 Red Hat 仍然支持ext3/4文件系统和其他文件系统,因此/proc/mounts文件中可能列出了许多不同的文件系统类型。

对于/boot文件系统,知道文件系统类型并不是立即有用的; 然而,知道如何查找底层的文件系统类型可能是我们深入研究这个问题:

/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0

第四列选项显示文件系统所使用的选项。

当挂载一个文件系统时,可以为该文件系统提供特定的选项,以便更改文件系统的默认行为。 在前面的例子中,提供了相当多的选项; 让我们分解这个列表,以便更好地理解指定的内容:

  • rw:以读写方式挂载文件系统
  • seclabel:这个选项是由 SELinux 添加的,以表明该文件系统支持标签的额外属性
  • relative:这个告诉文件系统,如果访问时间比文件/目录的修改或更改时间值早,那么只修改访问时间
  • attr2:这个支持改进内联扩展属性在磁盘上的存储方式
  • inode64:这个允许文件系统创建长度大于 32 位的 inode 编号
  • noquota:此禁用该文件系统的磁盘配额和强制执行

正如我们从描述中看到的,这些选项可以极大地改变文件系统的行为方式。 在诊断任何文件系统问题时,它们也非常重要:

/dev/sda1 /boot xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0

输出/proc/mounts的最后两列表示为0 0,实际上在/proc/mounts中没有使用。 这些列实际上只是为/etc/mtab的向后功能而添加的,这是一个类似的文件,但是不像/proc/mounts那样被认为是最新的。

这两个文件的不同之处在于它们的用法。 /etc/mtab文件是为用户或应用设计的,以便在/proc/mounts文件由内核本身使用的地方读取和利用它。 因此,/proc/mounts文件被认为是最权威的版本。

回到故障诊断

如果我们回到手头的问题,我们在将备份写到/data/backups目录时收到了一个错误。 使用mount命令,我们可以确定该目录存在于哪个文件系统上:

# mount | grep "data"
192.168.33.13:/nfs on /data type nfs4 (rw,relatime,vers=4.0,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.33.12,local_lock=none,addr=192.168.33.13)

现在我们更好地理解了mount命令的格式,我们可以从前面的命令行中识别一些关键信息。 我们可以看到,这个文件系统的设备设置为192.168.33.13:/nfs,mount点(path把)设置为(/data),文件系统的类型是(nfs4),和文件系统有相当多的选项集。

NFS -网络文件系统

查看/data文件系统,我们可以看到文件系统类型被设置为nfs4。 这种文件系统类型意味着该文件系统是一个网络文件系统(NFS)。

NFS 是一种允许服务器与其他远程服务器共享导出目录的服务。 文件系统类型是一种特殊的文件系统,它允许远程服务器像访问标准文件系统一样访问该服务。

文件系统类型中的4表示要使用的版本,这意味着远程服务器将使用 NFS 协议的 version 4。

提示

目前,最流行的 NFS 版本是版本 3 和版本 4,其中 4 是 Red Hat Enterprise Linux 6 和 7 的默认版本。 版本 3 和版本 4 之间有很多不同之处; 然而,这些差异并不足以对我们的故障排除方法产生影响。 如果您发现自己在使用 NFS 版本 3 时遇到了问题,那么您很可能会遵循与本章相同的步骤类型。

现在我们已经确定了文件系统是一个 NFS 文件系统,让我们看看它被挂载的选项:

192.168.33.13:/nfs on /data type nfs4 (rw,relatime,vers=4.0,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.33.12,local_lock=none,addr=192.168.33.13)

从我们收到的错误来看,文件系统似乎是Read-Only,但是如果我们查看选项,列出的第一个选项是rw。 这意味着 NFS 文件系统本身已经被挂载为Read-Write; 这应该允许对这个文件系统进行写操作。

要测试问题是出在路径/data/backups还是挂载的文件系统/data,我们可以使用touch命令来测试在这个文件系统中创建一个文件:

# touch /data/file.txt
touch: cannot touch '/data/file.txt': Read-only file system

即使是touch命令也不能在这个文件系统上创建一个新文件。 这清楚地表明文件系统有问题; 唯一的问题是什么导致了这个问题。

如果我们看一下这个文件系统所使用的选项,没有什么会导致文件系统成为Read-Only; 这意味着问题很可能不在于如何安装文件系统,而在于其他方面。

由于这个问题似乎与 NFS 文件系统的挂载方式无关,而且该文件系统是基于网络的,因此下一步应该检查到 NFS 服务器的网络连接。

NFS 和网络连接

与网络故障排除一样,我们的第一个测试将是 pingNFS 服务器,看看是否得到响应; 但问题是:我们应该 ping 哪个服务器?

答案在文件系统所使用的设备名称中(192.168.33.13:/nfs)。 挂载 NFS 文件系统时,设备的格式为<nfs server>:<shared directory>。 对于我们的示例,这意味着我们的/data文件系统正在从服务器192.168.33.13挂载/nfs目录。 为了测试连接性,我们可以简单地pingIP192.168.33.13:

[db]# ping 192.168.33.13
PING 192.168.33.13 (192.168.33.13) 56(84) bytes of data.
64 bytes from 192.168.33.13: icmp_seq=1 ttl=64 time=0.495 ms
64 bytes from 192.168.33.13: icmp_seq=2 ttl=64 time=0.372 ms
64 bytes from 192.168.33.13: icmp_seq=3 ttl=64 time=0.364 ms
64 bytes from 192.168.33.13: icmp_seq=4 ttl=64 time=0.337 ms
^C
--- 192.168.33.13 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 0.337/0.392/0.495/0.060 ms

ping的结果来看,NFS 服务器已经启动; 但是 NFS 服务呢? 我们可以通过对 NFS 端口使用curl命令telnet来验证到 NFS 服务的连接。 但是,首先,我们需要确定应该连接到哪个端口。

在前面的章节中,在对数据库连接进行故障诊断时,我们主要使用知名的端口; 因为 NFS 使用几个端口,这些端口不太常见; 我们需要确定连接到哪个端口:

最简单的方法是在/etc/services文件中搜索端口:

[db]# grep nfs /etc/services 
nfs             2049/tcp        nfsd shilp      # Network File System
nfs             2049/udp        nfsd shilp      # Network File System
nfs             2049/sctp       nfsd shilp      # Network File System
netconfsoaphttp 832/tcp                 # NETCONF for SOAP over HTTPS
netconfsoaphttp 832/udp                 # NETCONF for SOAP over HTTPS
netconfsoapbeep 833/tcp                 # NETCONF for SOAP over BEEP
netconfsoapbeep 833/udp                 # NETCONF for SOAP over BEEP
nfsd-keepalive  1110/udp                # Client status info
picknfs         1598/tcp                # picknfs
picknfs         1598/udp                # picknfs
shiva_confsrvr  1651/tcp   shiva-confsrvr   # shiva_confsrvr
shiva_confsrvr  1651/udp   shiva-confsrvr   # shiva_confsrvr
3d-nfsd         2323/tcp                # 3d-nfsd
3d-nfsd         2323/udp                # 3d-nfsd
mediacntrlnfsd  2363/tcp                # Media Central NFSD
mediacntrlnfsd  2363/udp                # Media Central NFSD
winfs           5009/tcp                # Microsoft Windows Filesystem
winfs           5009/udp                # Microsoft Windows Filesystem
enfs            5233/tcp                # Etinnae Network File Service
nfsrdma         20049/tcp               # Network File System (NFS) over RDMA
nfsrdma         20049/udp               # Network File System (NFS) over RDMA
nfsrdma         20049/sctp              # Network File System (NFS) over RDMA

/etc/services文件是一个静态文件,包含在许多 Linux 发行版中。 它被用作查找将网络端口映射到一个简单的人类可读的名称。 从前面的输出可以看到,名称nfs被映射到 TCP 端口2049; 这是 NFS 服务的默认端口。 我们可以利用这个端口来测试连通性,如下所示:

[db]# curl -vk telnet://192.168.33.13:2049
* About to connect() to 192.168.33.13 port 2049 (#0)
*   Trying 192.168.33.13...
* Connected to 192.168.33.13 (192.168.33.13) port 2049 (#0)

我们的telnet似乎成功了; 我们可以使用命令netstat进一步验证它:

[db]# netstat -na | grep 192.168.33.13
tcp        0      0 192.168.33.12:756       192.168.33.13:2049      ESTABLISHED

似乎连接不是问题,如果我们的问题与连接无关,那么可能是关于 NFS 共享的配置方式。

实际上,我们可以通过一个命令——showmount来验证 NFS 共享的设置和网络连接。

使用 showmount 命令

showmount命令可以用来显示通过-e(显示 exports)标志导出的目录。 该命令通过查询指定主机上的 NFS 服务来实现。

对于我们的问题,我们将在192.168.33.13查询 NFS 服务:

[db]# showmount -e 192.168.33.13
Export list for 192.168.33.13:
/nfs 192.168.33.0/24

showmount命令的格式有两列。 第一列是共享目录。 第二个是共享目录的网络或主机名。

在前面的示例中,我们可以看到从这个主机共享的目录是/nfs目录。 这也与设备名称192.168.33.13:/nfs中列出的目录相匹配。

目录与/nfs共享的网络是192.166.33.0/24网络,正如我们在网络连接一章中了解到的,它是192.168.33.0192.168.33.255的简称。 从以前的故障排除中,我们已经知道我们所在的数据库服务器在该网络中。

我们还可以看到,自从之前执行netstat命令以来,这一点没有改变:

[db]# netstat -na | grep 192.168.33.13
tcp        0      0 192.168.33.12:756       192.168.33.13:2049      ESTABLISHED

netstat命令的第四列显示ESTABLISHEDTCP 连接使用的本端 IP 地址。 通过前面的输出,我们可以看到192.168.33.12地址是数据库服务器的 IP(如前面章节所示)。

到目前为止,关于这个 NFS 共享的所有内容看起来都是正确的,从这里开始,我们需要登录到 NFS 服务器来继续进行故障排除。

NFS 服务器配置

登录到 NFS 服务器后,首先要检查的是 NFS 服务是否在运行:

[db]# systemctl status nfs
nfs-server.service - NFS server and services
 Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled)
 Active: active (exited) since Sat 2015-04-25 14:01:13 MST; 17h ago
 Process: 2226 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
 Process: 2225 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
 Main PID: 2226 (code=exited, status=0/SUCCESS)
 CGroup: /system.slice/nfs-server.service

使用systemctl,我们可以简单地查看服务状态; 从前面的输出来看是正常的。 这是意料之中的事,因为我们既可以通过telnet访问 NFS 服务,又可以使用showmount命令查询它。

探索/etc/exports

由于 NFS 服务正在运行且健康,因此下一步是检查定义导出哪些目录以及如何导出目录的配置; /etc/exports文件:

[nfs]# ls -la /etc/exports
-rw-r--r--. 1 root root 40 Apr 26 08:28 /etc/exports
[nfs]# cat /etc/exports
/nfs  192.168.33.0/24(rw,no_root_squash)

该文件的格式实际上类似于showmount命令的输出。

第一列是要共享的目录,第二列是要共享它的网络。 但是,在这个文件中,在网络定义之后还有一些附加信息。

网络/子网列后面是一组括号,其中包含各种NFS选项。 这些选项的工作原理与我们在/proc/mounts文件中看到的挂载选项非常相似。

这些选项可能是我们的Read-Only文件系统的根本原因吗? 很有可能。 让我们分解这两种选择,以便更好地理解:

  • rw:允许对共享目录进行读写操作
  • no_root_squash:禁用root_squash; root_squash是一个将根用户映射为匿名用户的系统

不幸的是,这些选项中的都不会强制文件系统处于Read-Only模式。 事实上,根据这些选项的描述,他们似乎建议这个 NFS 共享应该处于Read-Write模式。

在对/etc/exports文件执行ls时出现了一个有趣的事实:

[nfs]# ls -la /etc/exports
-rw-r--r--. 1 root root 40 Apr 26 08:28 /etc/exports

/etc/exports文件最近被修改。 可能我们共享的文件系统实际上是作为Read-Only共享的,但是最近有人更改了/etc/exports文件,将文件系统导出为Read-Write

这种情况是完全可能的,而且实际上是 NFS 的一个常见问题。 NFS 服务不会不断读取/etc/exports文件以寻找更改。 事实上,这个文件只在服务启动时被读取。

/etc/exports文件的任何修改都将在重新加载服务或使用exportfs命令刷新导出的文件系统后才生效。

识别当前出口

一个非常常见的场景是,有人对该文件进行了更改,但却忘记运行命令来刷新导出的文件系统。 我们可以通过使用exportfs命令来确定是否为这种情况:

[nfs]# exportfs -s
/nfs  192.168.33.0/24(rw,wdelay,no_root_squash,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

当给出–s(显示当前导出)标志时,exportfs命令将简单地列出现有的共享目录,包括共享这些目录的选项。

查看前面的输出,我们可以看到这个文件系统与许多选项共享,这些选项在/etc/exports中没有列出。 这是因为通过 NFS 共享的所有目录都有一个默认选项列表,这些选项管理如何共享目录。 在/etc/exports中指定的选项实际上用于覆盖默认设置。

为了更好地理解这些选项,让我们将它们进行分解:

  • rw:允许对共享目录进行读写操作。
  • wdelay:这将导致 NFS 在怀疑有另一个客户端写入请求时保持一个写请求。 这是为了减少连接多个客户端时的写冲突。
  • no_root_squash:禁用root_squash,这是一个将根用户映射到匿名用户的系统。
  • no_subtree_check:禁用subtree检查; 子树检查本质上确保对导出子目录的目录的请求将遵循子目录更严格的策略。
  • sec=sys:这告诉 NFS 使用用户 ID 和组 ID 值进行文件访问的权限和授权。
  • secure:这确保 NFS 只处理客户端端口小于 1024 的请求,本质上要求它来自特权 NFS 挂载。
  • no_all_squash:禁用all_squash,该功能用于强制将所有权限映射到匿名用户和组。

似乎这些选项也不能解释Read-Only文件系统。 这个问题似乎很难解决,尤其是在 NFS 服务配置正确的情况下。

从其他客户端测试 NFS

因为 NFS 服务器的配置看起来是正确的,而且客户机(数据库服务器)也看起来是正确的,所以我们需要缩小问题是在客户机端还是在服务器端。

一种方法是将文件系统挂载到另一个客户机上,并尝试相同的写请求。 从配置来看,似乎我们只需要在192.168.33.0/24网络中另一台服务器来执行此测试。 也许我们前面章节提到的博客服务器是一个很好的客户端?

提示

在某些环境中,这个问题的答案可能是no,因为 web 服务器通常被认为不如数据库服务器安全。 然而,因为这只是本书的一个测试环境,所以它是可以的。

一旦我们登录到博客服务器,我们就可以使用showmount命令来测试是否可以看到挂载:

[blog]# showmount -e 192.168.33.13
Export list for 192.168.33.13:
/nfs 192.168.33.0/24

这回答了两个问题。 第一个是是否安装了 NFS 客户端软件; 因为存在showmount命令,所以答案可能是yes

第二个问题是是否可以从博客服务器访问 NFS 服务,答案也是 yes。

要测试挂载,我们只需使用mount命令:

[blog]# mount -t nfs 192.168.33.13:/nfs /mnt

使用mount命令挂载文件系统,语法为:mount –t <filesystem type> <device> <mount point>。 在上面的示例中,我们简单地将192.168.33.13:/nfs设备挂载到/mnt目录,文件系统类型为nfs

在运行命令时,我们没有收到任何错误,但是为了确保文件系统被正确挂载,我们可以像之前那样使用mount命令:

[blog]# mount | grep /mnt
192.168.33.13:/nfs on /mnt type nfs4 (rw,relatime,vers=4.0,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.33.11,local_lock=none,addr=192.168.33.13)

mount命令的输出来看,mount请求已经成功,并且处于Read-Write模式,这意味着mount选项与数据库服务器上使用的选项类似。

现在我们可以通过尝试使用touch命令创建一个文件来测试文件系统:

# touch /mnt/testfile.txt 
touch: cannot touch '/mnt/testfile.txt': Read-only file system

看起来问题不在于客户机的配置,因为即使是我们的新客户机也有写这个文件系统的问题。

提示

作为提示,在前面的示例中,我将/nfs共享挂载到/mnt/mnt目录被用作通用的挂载点,通常认为可以使用。 然而,最好的做法是确保不事先安装其他内容到/mnt

使坐骑永久

目前,即使我们使用mount命令挂载 NFS 共享,这个挂载的文件系统也不被认为是持久的。 下次系统重新引导时,将不会重新挂载 NFS 挂载。

这是因为在系统引导时,引导过程的一部分是读取/etc/fstab文件和mount文件中定义的任何文件系统。

为了更好地理解它是如何工作的,让我们看看数据库服务器上的/etc/fstab文件:

[db]# cat /etc/fstab

#
# /etc/fstab
# Created by anaconda on Mon Jul 21 23:35:56 2014
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/os-root /                       xfs     defaults        1 1
UUID=be76ec1d-686d-44a0-9411-b36931ee239b /boot                   xfs     defaults        1 2
/dev/mapper/os-swap swap                    swap    defaults        0 0
192.168.33.13:/nfs  /data      nfs  defaults  0 0

/etc/fstab文件的内容实际上与/proc/mounts文件的内容非常相似。 第一列的/etc/fstab文件用于指定设备安装,第二列是pathmount指山,第三列是文件系统类型,第四列是mount的选项文件系统。

然而,在/etc/fstab文件中,最后两列是这些文件的不同之处。 最后两列实际上是有意义的。 在fstab文件中,dump命令使用第五列。

dump命令是一个简单的备份实用程序,它读取/etc/fstab以确定要备份哪些文件系统。 当执行转储实用程序时,任何设置了0值的文件系统都不在备份范围内。

虽然目前这个实用程序的使用并不多,但是维护/etc/fstab文件中的这个专栏是为了提供向后功能。

/etc/fstab文件中的第六列也是最后一列与当今的系统非常相关。 这个列用来表示引导过程(通常是在失败之后)中执行文件系统检查或fsck的顺序。

文件系统检查(简称fsck)是一个定期运行的进程,它检查文件系统中是否存在错误并试图纠正它们。 这是一个过程,我们将在本章进一步介绍。

卸载/mnt 文件系统

因为我们不想让 NFS 共享文件系统挂载在博客服务器的/mnt路径上,所以我们需要卸载该文件系统。

我们可以使用与前面处理/boot文件系统相同的方法来完成此操作; 使用umount命令:

[blog]# umount /mnt
[blog]# mount | grep /mnt

在博客服务器上,我们简单地使用umount,然后使用/mntmount点到unmount客户机的 NFSmount点。 现在,我们可以回到 NFS 服务器继续进行故障排除。

NFS 服务器故障处理

由于我们确定了,即使是新客户端也不能写入/nfs共享,因此我们现在已经将问题范围缩小到服务器端,而不是客户端。

前面,在对 NFS 服务器进行故障诊断时,我们几乎检查了关于 NFS 的所有检查。 我们验证了服务实际上正在运行,客户机可以访问该服务,并且验证了/etc/exports中的数据是正确的,并且当前导出的目录与/etc/exports中的目录匹配。 此时,只剩下一个地方需要检查:log文件。

默认情况下,NFS 服务不像 Apache 或 MariaDB 那样拥有自己的日志文件。 相反,RHEL 系统上的这项服务使用了syslog设施; 这意味着我们的日志将在/var/log/messages内。

messages日志是 Red Hat Enterprise Linux 发行版中非常常用的日志文件。 事实上,默认情况下,在 cron 作业和身份验证之外,信息日志级别以上的所有 syslog 消息都被发送到基于 RHEL 的系统上的/var/log/messages

由于 NFS 服务将其日志消息发送到本地syslog服务,因此其消息也包含在messages日志中。

查找 NFS 日志消息

如果我们不知道 NFS 日志被发送到/var/log/messages日志文件会怎样? 有一个相当简单的技巧可以识别哪个日志文件包含 NFS 日志消息。

通常,在 Linux 系统上,所有系统服务的日志文件都位于/var/log中。 由于我们知道大多数日志在系统上的默认位置,我们可以简单地快速查看这些文件,以确定哪些文件可能具有 NFS 日志消息:

[nfs]# cd /var/log
[nfs]# grep -rc nfs ./*
./anaconda/anaconda.log:14
./anaconda/syslog:44
./anaconda/anaconda.xlog:0
./anaconda/anaconda.program.log:7
./anaconda/anaconda.packaging.log:16
./anaconda/anaconda.storage.log:56
./anaconda/anaconda.ifcfg.log:0
./anaconda/ks-script-Sr69bV.log:0
./anaconda/ks-script-lfU6U2.log:0
./audit/audit.log:60
./boot.log:4
./btmp:0
./cron:470
./cron-20150420:662
./dmesg:26
./dmesg.old:26
./grubby:0
./lastlog:0
./maillog:112386
./maillog-20150420:17
./messages:3253
./messages-20150420:11804
./sa/sa15:1
./sa/sar15:1
./sa/sa16:1
./sa/sar16:1
./sa/sa17:1
./sa/sa19:1
./sa/sar19:1
./sa/sa20:1
./sa/sa25:1
./sa/sa26:1
./secure:14
./secure-20150420:63
./spooler:0
./tallylog:0
./tuned/tuned.log:0
./wtmp:0
./yum.log:0

grep命令递归地(-r)在每个文件中搜索字符串“nfs”,并输出文件名以及找到该字符串的行数的计数(-c)。

在前面的输出中,有两个日志文件包含最多的字符串“nfs”实例。 第一个是maillog,它是电子邮件消息的系统日志; 这可能与 NFS 服务无关。

第二个是messages日志文件,如我们所知,它是系统默认的日志文件。

即使事先不了解特定系统的日志记录方法,如果您像前面的示例一样熟悉 Linux 的一般情况和技巧,通常也可以找到哪些日志包含所需的数据。

现在我们知道了要查找的日志文件,让我们看一下/var/log/messages日志。

读取/var/log/messages

由于这个log文件可能非常大,我们将使用带有-100标志的tail命令,这将导致尾部只显示指定文件的最后100行。 通过限制输出为100行,我们应该只看到最相关的数据:

[nfs]# tail -100 /var/log/messages
Apr 26 10:25:44 nfs kernel: md/raid1:md127: Disk failure on sdb1, disabling device.
md/raid1:md127: Operation continuing on 1 devices.
Apr 26 10:25:55 nfs kernel: md: unbind<sdb1>
Apr 26 10:25:55 nfs kernel: md: export_rdev(sdb1)
Apr 26 10:27:20 nfs kernel: md: bind<sdb1>
Apr 26 10:27:20 nfs kernel: md: recovery of RAID array md127
Apr 26 10:27:20 nfs kernel: md: minimum _guaranteed_  speed: 1000 KB/sec/disk.
Apr 26 10:27:20 nfs kernel: md: using maximum available idle IO bandwidth (but not more than 200000 KB/sec) for recovery.
Apr 26 10:27:20 nfs kernel: md: using 128k window, over a total of 511936k.
Apr 26 10:27:20 nfs kernel: md: md127: recovery done.
Apr 26 10:27:41 nfs nfsdcltrack[4373]: sqlite_remove_client: unexpected return code from delete: 8
Apr 26 10:27:59 nfs nfsdcltrack[4375]: sqlite_remove_client: unexpected return code from delete: 8
Apr 26 10:55:06 nfs dhclient[3528]: can't create /var/lib/NetworkManager/dhclient-05be239d-0ec7-4f2e-a68d-b64eec03fcb2-enp0s3.lease: Read-only file system
Apr 26 11:03:43 nfs chronyd[744]: Could not open temporary driftfile /var/lib/chrony/drift.tmp for writing
Apr 26 11:55:03 nfs rpc.mountd[4552]: could not open /var/lib/nfs/.xtab.lock for locking: errno 30 (Read-only file system)
Apr 26 11:55:03 nfs rpc.mountd[4552]: can't lock /var/lib/nfs/xtab for writing

因为即使是100行也可能相当繁琐,所以我将输出截断为只处理相关的行。 这显示了相当多的带有字符串“nfs”的消息; 然而,并非所有这些都是来自 NFS 服务的消息。 因为我们的 NFS 服务器的主机名被设置为nfs,所以这个系统的每个日志条目都有字符串“nfs”。

然而,即使这样,我们仍然可以看到一些与NFS服务相关的消息,特别是以下几行:

Apr 26 10:27:41 nfs nfsdcltrack[4373]: sqlite_remove_client: unexpected return code from delete: 8
Apr 26 10:27:59 nfs nfsdcltrack[4375]: sqlite_remove_client: unexpected return code from delete: 8
Apr 26 11:55:03 nfs rpc.mountd[4552]: could not open /var/lib/nfs/.xtab.lock for locking: errno 30 (Read-only file system)
Apr 26 11:55:03 nfs rpc.mountd[4552]: can't lock /var/lib/nfs/xtab for writing

关于这些日志条目的有趣的事情是其中一个特别声明了服务rpc.mountd不能打开一个文件,因为文件系统是Read-only。 但是,它试图打开的文件/var/lib/nfs/.xtab.lock不是我们的 NFS 共享的一部分。

由于这个文件系统不是我们的 NFS 的一部分,让我们快速查看一下这个服务器上挂载的文件系统。 我们可以再次这样做,使用mount命令:

[nfs]# mount
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=241112k,nr_inodes=60278,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=33,pgrp=1,timeout=300,minproto=5,maxproto=5,direct)
mqueue on /dev/mqueue type mqueue (rw,relatime,seclabel)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel)
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
nfsd on /proc/fs/nfsd type nfsd (rw,relatime)
/dev/mapper/md0-root on / type xfs (ro,relatime,seclabel,attr2,inode64,noquota)
/dev/md127 on /boot type xfs (ro,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/md0-nfs on /nfs type xfs (ro,relatime,seclabel,attr2,inode64,noquota)

和其他服务器一样,这里有很多已安装的文件系统,但是我们对它们并不感兴趣; 只有一小部分。

/dev/mapper/md0-root on / type xfs (ro,relatime,seclabel,attr2,inode64,noquota)
/dev/md127 on /boot type xfs (ro,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/md0-nfs on /nfs type xfs (ro,relatime,seclabel,attr2,inode64,noquota)

前面三行是我们应该感兴趣的。 这三个挂载的文件系统是为我们的系统定义的持久文件系统。 如果我们看一下这三个持久文件系统,我们可以发现一些有趣的信息。

设备/dev/mapper/md0-root上存在/或根文件系统。 这个文件系统实际上对我们的系统非常重要,因为似乎这个服务器被配置为将整个操作系统安装在根文件系统(/)下,这是一种比较常见的设置。 这个文件系统包括所讨论的文件/var/lib/nfs/.xtab.lock文件。

/boot文件系统存在于/dev/md127设备上,从名称上判断,该设备很可能是使用 Linux 的软件 raid 系统的突袭设备。 /boot文件系统与根文件系统一样重要,因为/boot包含服务器启动所需的所有文件。 如果没有/boot文件系统,这个系统很可能不会重新启动,而只是在下一次系统重新启动时出现内核恐慌。

最后一个文件系统/nfs使用/dev/mapper/md0-nfs设备。 根据前面的故障诊断,我们将这个文件系统标识为通过 NFS 服务导出的文件系统。

只读文件系统

如果我们回顾一下错误和mount的输出,我们将开始发现这个系统中一些有趣的错误:

Apr 26 11:55:03 nfs rpc.mountd[4552]: could not open /var/lib/nfs/.xtab.lock for locking: errno 30 (Read-only file system)

该错误报告文件系统在。 xtab.lock文件位于Read-Only:

/dev/mapper/md0-root on / type xfs (ro,relatime,seclabel,attr2,inode64,noquota)

通过mount命令,我们可以看到所讨论的文件系统就是/文件系统。 在查看了/或根文件系统的选项之后,我们可以看到这个文件系统实际上是用ro选项安装的。

事实上,如果我们看一下这三个文件系统的选项,我们可以看到//boot/nfs都用ro选项挂载。 其中rw将文件系统挂载为Read-Writero选项将文件系统挂载为Read-Only。 这意味着目前,这些文件系统不能被任何用户写入。

对于以Read-Only模式挂载的所有三个已定义的文件系统来说,这是一种非常不寻常的配置。 要查看这是否是所需的配置,我们可以检查/etc/fstab文件,它与前面用于标识持久文件系统的文件相同:

[nfs]# cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Wed Apr 15 09:39:23 2015
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/md0-root    /                       xfs     defaults        0 0
UUID=7873e886-78d5-46cc-b4d9-0c385995d915 /boot                   xfs     defaults        0 0
/dev/mapper/md0-nfs     /nfs                    xfs     defaults        0 0
/dev/mapper/md0-swap    swap                    swap    defaults        0 0

/etc/fstab文件的内容来看,这些文件系统似乎没有被配置为以Read-Only模式挂载。 相反,这些文件系统是用“默认”选项挂载的。

在 Linux 上,xfs文件系统的“默认”选项以Read-Write模式而不是Read-Only模式挂载文件系统。 如果我们查看数据库服务器上的/etc/fstab文件,就可以验证这种行为:

[db]# cat /etc/fstab 
#
# /etc/fstab
# Created by anaconda on Mon Jul 21 23:35:56 2014
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/os-root /                       xfs     defaults        1 1
UUID=be76ec1d-686d-44a0-9411-b36931ee239b /boot                   xfs     defaults        1 2
/dev/mapper/os-swap swap                    swap    defaults        0 0
192.168.33.13:/nfs  /data      nfs  defaults  0 0

在数据库服务器上,我们可以看到/或根文件系统也将文件系统选项设置为“defaults”。 然而,当我们使用mount命令查看文件系统选项时,我们可以看到rw选项以及其他一些默认选项正在被应用:

[db]# mount | grep root
/dev/mapper/os-root on / type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

这确认了三个持久文件系统的Read-Only状态不是所需的配置。

硬盘问题

如果指定/etc/fstab文件系统为,以Read-Write方式挂载文件系统,且mount命令显示以Read-Only方式挂载文件系统。 这清楚地表明,有问题的文件系统可能在它们最初作为引导过程的一部分被装载之后被重新装载。

如前所述,当 Linux 系统引导时,它读取/etc/fstab文件并挂载所有定义的文件系统。 但是,安装文件系统的过程到此为止。 没有进程持续监视/etc/fstab文件的更改,并挂载或卸载修改的文件系统,至少在默认情况下不是这样。

事实上,经常会看到新创建的文件系统没有挂载,而是在/etc/fstab文件中指定,因为有人在编辑/etc/fstab文件后忘记使用mount命令对其进行mount操作。

然而,看到文件系统被挂载为Read-Only,但是fstab随后被更改的情况并不常见。

事实上,在我们的场景中,这并不容易实现,因为/文件系统是Read-Only,所以/etc/fstab是不可访问的:

[nfs]# touch /etc/fstab
touch: cannot touch '/etc/fstab': Read-only file system

这意味着我们的文件系统是Read-Only,是在这些文件系统最初安装之后执行的。

这种状态的罪魁祸首实际上在我们之前查看的日志消息中:

Apr 26 10:25:44 nfs kernel: md/raid1:md127: Disk failure on sdb1, disabling device.
md/raid1:md127: Operation continuing on 1 devices.
Apr 26 10:25:55 nfs kernel: md: unbind<sdb1>
Apr 26 10:25:55 nfs kernel: md: export_rdev(sdb1)
Apr 26 10:27:20 nfs kernel: md: bind<sdb1>
Apr 26 10:27:20 nfs kernel: md: recovery of RAID array md127
Apr 26 10:27:20 nfs kernel: md: minimum _guaranteed_  speed: 1000 KB/sec/disk.
Apr 26 10:27:20 nfs kernel: md: using maximum available idle IO bandwidth (but not more than 200000 KB/sec) for recovery.
Apr 26 10:27:20 nfs kernel: md: using 128k window, over a total of 511936k.
Apr 26 10:27:20 nfs kernel: md: md127: recovery done.

/var/log/messages日志文件中,我们可以看到,在某个时刻,软件 raid(md)出现了一个问题,该问题将磁盘/dev/sdb1标记为失败。

默认情况下,在 Linux 中,如果一个物理磁盘驱动器失败或内核无法使用,Linux 内核将以Read-Only模式重新挂载该物理磁盘上的文件系统。 与前面的错误消息一样,似乎是sdb1物理磁盘和md127raid 设备的故障是导致文件系统为Read-Only的根本原因。

由于软件 raid 和硬件问题是下一章的主题,我们将在第 8 章硬件故障中推迟 raid 和磁盘问题的故障排除。

恢复文件系统

既然我们知道了为什么文件系统处于Read-Only模式,我们就可以解决这个问题了。 强迫文件系统从Read-Only转到Read-Write实际上非常简单。 但是,因为我们不知道导致文件系统进入Read-Only模式的失败的所有情况,所以我们必须小心。

从文件系统错误中恢复可能非常棘手; 如果处理不当,我们很容易发现自己处于这样一种情况:我们损坏了文件系统,或者以其他方式导致部分甚至全部数据丢失。

因为我们有多个文件系统处于Read-Only模式,所以我们首先从/boot文件系统开始。 我们从/boot文件系统开始的原因是,从技术上讲,这是最容易发生数据丢失的文件系统。 由于/boot文件系统只在服务器引导过程中使用,所以我们可以简单地确保在/boot文件系统可以恢复之前不重新引导该服务器。

无论何时,在采取任何行动之前,最好先备份数据。 在接下来的步骤中,我们将假定/boot文件系统定期进行备份。

卸载文件系统

为了恢复这个文件系统,我们将执行三个步骤。 在第一步中,我们将卸载/boot文件系统。 通过在采取任何额外步骤之前卸载文件系统,我们将确保文件系统没有被积极地写入。 这一步骤将大大减少恢复过程中文件系统损坏的机会。

然而,在卸载文件系统之前,我们需要确保没有应用或服务试图写入我们试图恢复的文件系统。

为了确保这一点,我们可以使用lsof命令。 lsof命令用于列出打开的文件; 我们可以通过这个列表来确定/boot文件系统中是否有任何文件是打开的。

如果我们只是运行不带选项的lsof,它将打印所有当前打开的文件:

[nfs]# lsof
COMMAND    PID TID           USER   FD      TYPE             DEVICE SIZE/OFF       NODE NAME
systemd      1               root  cwd       DIR              253,1 4096        128 /

通过向lsof添加–r(repeat)标志,我们告诉它以重复模式运行。 然后我们可以将这个输出通过管道传递到grep命令,在那里我们可以过滤出在/boot文件系统上打开的文件:

[nfs]# lsof -r | grep /boot

如果前面的命令在一段时间内没有产生任何输出,那么继续卸载文件系统是安全的。 如果该命令打印任何打开的文件,最好找到适当的进程读写文件系统,并在卸载文件系统之前停止它们。

由于我们的示例在/boot文件系统上没有打开的文件,我们可以继续卸载/boot文件系统。 为此,我们将使用umount命令:

[nfs]# umount /boot

幸运的是,umount命令没有出错。 如果文件正在积极写入,那么卸载时可能会收到错误。 通常,此错误包含一条消息,声明设备正忙。 为了验证文件系统是否被成功卸载,我们可以再次使用mount命令:

[nfs]# mount | grep /boot

现在已经卸载了/boot文件系统,我们可以在恢复过程中执行第二步。 我们现在可以检查和修复文件系统。

使用 fsck 检查文件系统

Linux 有一个非常有用的文件系统检查命令,可以用来检查和修复文件系统。 这个命令称为fsck

然而,fsck命令实际上并不是一个命令。 每种文件系统类型都有自己的检查一致性和修复问题的方法。 fsck命令只是一个包装器,它为所讨论的文件系统调用适当的命令。

例如,当fsck命令在ext4文件系统上运行时,正在执行的命令实际上是e2fscke2fsck命令用于ext2ext4的文件系统类型。

我们可以通过两种方式调用e2fsck,要么直接调用fsck,要么间接调用fsck。 在本例中,我们将使用fsck方法,因为它可以用于 Linux 支持的几乎所有文件系统。

要使用fsck命令简单地检查文件系统的一致性,我们可以在不带标志的情况下运行它,并指定要检查的磁盘设备:

[nfs]# fsck /dev/sda1
fsck from util-linux 2.20.1
e2fsck 1.42.9 (4-Feb-2014)
cloudimg-rootfs: clean, 85858/2621440 files, 1976768/10485504 blocks

在前面的示例中,我们可以看到文件系统没有识别任何错误。 如果有,就会有人问我们是否需要e2fsck实用程序来纠正这些错误。

如果需要,我们可以通过将–y(yes)标志传递给fsck来自动修复发现的问题:

[nfs]# fsck -y /dev/sda1
fsck from util-linux 2.20.1
e2fsck 1.42 (29-Nov-2011)
/dev/sda1 contains a file system with errors, check forced.
Pass 1: Checking inodes, blocks, and sizes
Inode 2051351 is a unknown file type with mode 0137642 but it looks 
like it is really a directory.
Fix? yes

Pass 2: Checking directory structure
Entry 'test' in / (2) has deleted/unused inode 49159\.  Clear? yes

Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information

/dev/sda1: ***** FILE SYSTEM WAS MODIFIED *****
/dev/sda1: 96/2240224 files (7.3% non-contiguous), 3793508/4476416 blocks

此时,e2fsck命令将尝试来纠正它发现的任何错误。 幸运的是,在我们的例子中,错误能够被纠正; 然而,在某些情况下,情况并非如此。

fsck 和 xfs 文件系统

fsck命令为时,在文件系统上运行xfs; 结果却截然不同:

[nfs]# fsck /dev/md127 
fsck from util-linux 2.23.2
If you wish to check the consistency of an XFS filesystem or
repair a damaged filesystem, see xfs_repair(8).

xfs文件系统不同于ext2/3/4系列文件系统,因为每次挂载文件系统时都会执行一致性检查。 这并不意味着您不能手动检查和修复文件系统。 要检查一个xfs文件系统,我们可以使用xfs_repair实用程序:

[nfs]# xfs_repair -n /dev/md127
Phase 1 - find and verify superblock...
Phase 2 - using internal log
 - scan filesystem freespace and inode maps...
 - found root inode chunk
Phase 3 - for each AG...
 - scan (but don't clear) agi unlinked lists...
 - process known inodes and perform inode discovery...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
 - process newly discovered inodes...
Phase 4 - check for duplicate blocks...
 - setting up duplicate extent list...
 - check for inodes claiming duplicate blocks...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
No modify flag set, skipping phase 5
Phase 6 - check inode connectivity...
 - traversing filesystem ...
 - traversal finished ...
 - moving disconnected inodes to lost+found ...
Phase 7 - verify link counts...
No modify flag set, skipping filesystem flush and exiting.

当使用–n(不修改)标志执行,后面跟着要检查的设备时,xfs_repair实用程序只会验证文件系统的一致性。 在此模式下运行时,它不会尝试修复文件系统。

要在修复文件系统的模式下运行xfs_repair,只需省略–n标志,如下所示:

[nfs]# xfs_repair /dev/md127
Phase 1 - find and verify superblock...
Phase 2 - using internal log
 - zero log...
 - scan filesystem freespace and inode maps...
 - found root inode chunk
Phase 3 - for each AG...
 - scan and clear agi unlinked lists...
 - process known inodes and perform inode discovery...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
 - process newly discovered inodes...
Phase 4 - check for duplicate blocks...
 - setting up duplicate extent list...
 - check for inodes claiming duplicate blocks...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
Phase 5 - rebuild AG headers and trees...
 - reset superblock...
Phase 6 - check inode connectivity...
 - resetting contents of realtime bitmap and summary inodes
 - traversing filesystem ...
 - traversal finished ...
 - moving disconnected inodes to lost+found ...
Phase 7 - verify and correct link counts...
Done

从前面的xfs_repair命令的输出来看,我们的/boot文件系统似乎不需要任何修复进程。

这些工具如何修复文件系统?

您可能认为用fsckxfs_repair这样的工具来修复这个文件系统是相当容易的。 原因很简单,就是因为文件系统的设计,比如xfsext2/3/4xfsext2/3/4系列都是日志文件系统; 这意味着这些类型的文件系统将保存对文件系统对象(如文件、目录等)所做更改的日志。

这些更改将保存在此日志中,直到将更改提交到主文件系统。 xfs_repair实用程序只是查看这个日志,并回放未提交到主文件系统的最后更改。 这些文件系统日志允许文件系统在意外断电或系统重新启动等情况下非常有弹性。

不幸的是,有时候文件系统的日志和工具(如xfs_repair)不足以纠正这种情况。

在这种情况下,还有更多的选择,比如以强制模式运行修复。 然而,这些选项应该一直保留到最后一搏,因为它们本身有时会导致文件系统损坏。

如果您发现自己的文件系统已损坏且无法修复,那么最好的办法可能就是重新创建文件系统并恢复备份,如果您有备份的话……

安装文件系统

现在已经检查并修复了/boot文件系统,我们可以简单地重新安装它,以验证数据是否正确。 为此,我们可以简单地运行mount命令,然后运行/boot:

[nfs]# mount /boot
[nfs]# mount | grep /boot
/dev/md127 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

当在/etc/fstab文件中定义文件系统时,只需使用mount点就可以调用mountumount命令。 这将使这两个命令根据文件/etc/fstab中的定义进入文件系统mountunmount

mount的输出来看,我们的/boot文件系统现在是Read-Write而不是Read-Only。 如果我们执行一个ls命令,我们仍然可以看到我们的原始数据:

[nfs]# ls /boot
config-3.10.0-229.1.2.el7.x86_64                         initrd-plymouth.img
config-3.10.0-229.el7.x86_64                             symvers-3.10.0-229.1.2.el7.x86_64.gz
grub                                                     symvers-3.10.0-229.el7.x86_64.gz
grub2                                                    System.map-3.10.0-229.1.2.el7.x86_64
initramfs-0-rescue-3f370097c831473a8cfec737ff1d6c55.img  System.map-3.10.0-229.el7.x86_64
initramfs-3.10.0-229.1.2.el7.x86_64.img                  vmlinuz-0-rescue-3f370097c831473a8cfec737ff1d6c55
initramfs-3.10.0-229.1.2.el7.x86_64kdump.img             vmlinuz-3.10.0-229.1.2.el7.x86_64
initramfs-3.10.0-229.el7.x86_64.img                      vmlinuz-3.10.0-229.el7.x86_64
initramfs-3.10.0-229.el7.x86_64kdump.img

看来我们的恢复措施是成功的! 现在我们已经用/boot文件系统对它们进行了测试,现在我们可以开始修复/nfs文件系统。

修复其他文件系统

修复/nfs文件系统的步骤实际上与/boot文件系统相同,只有一个主要的区别,如下:

[nfs]# lsof -r | grep /nfs
rpc.statd 1075            rpcuser  cwd       DIR              253,1 40     592302 /var/lib/nfs/statd
rpc.mount 2282               root  cwd       DIR              253,1 4096    9125499 /var/lib/nfs
rpc.mount 2282               root    4u      REG                0,3 0 4026532125 /proc/2280/net/rpc/nfd.export/channel
rpc.mount 2282               root    5u      REG                0,3 0 4026532129 /proc/2280/net/rpc/nfd.fh/channel

当使用lsof检查/nfs文件系统上打开的文件时,我们可能看不到 NFS 服务进程。 但是,在lsof命令停止后,NFS 服务很可能会尝试访问这个共享文件系统中的文件。 为了防止这种情况,在对共享文件系统执行任何更改时,最好(尽可能)停止 NFS 服务:

[nfs]# systemctl stop nfs

一旦 NFS 服务停止,其余的步骤是相同的:

[nfs]# umount /nfs
[nfs]# xfs_repair /dev/md0/nfs
Phase 1 - find and verify superblock...
Phase 2 - using internal log
 - zero log...
 - scan filesystem freespace and inode maps...
 - found root inode chunk
Phase 3 - for each AG...
 - scan and clear agi unlinked lists...
 - process known inodes and perform inode discovery...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
 - process newly discovered inodes...
Phase 4 - check for duplicate blocks...
 - setting up duplicate extent list...
 - check for inodes claiming duplicate blocks...
 - agno = 0
 - agno = 1
 - agno = 2
 - agno = 3
Phase 5 - rebuild AG headers and trees...
 - reset superblock...
Phase 6 - check inode connectivity...
 - resetting contents of realtime bitmap and summary inodes
 - traversing filesystem ...
 - traversal finished ...
 - moving disconnected inodes to lost+found ...
Phase 7 - verify and correct link counts...
done

一旦文件系统被修复,我们可以简单地重新挂载它,如下所示:

[nfs]# mount /nfs
[nfs]# mount | grep /nfs
nfsd on /proc/fs/nfsd type nfsd (rw,relatime)
/dev/mapper/md0-nfs on /nfs type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

在重新安装/nfs文件系统之后,我们可以看到选项显示rw,这意味着它是Read-Writable

恢复/(根)文件系统

文件系统/root有一点不同。 它之所以不同,是因为顶层文件系统包含了大多数 Linux 包、二进制文件和命令。 这意味着我们不能简单地卸载这个文件系统而不丢失重新安装它所需的工具。

由于这个原因,我们实际上会使用mount命令重新挂载/文件系统,而不需要先卸载它:

[nfs]# mount -o remount /

为了告诉mount命令卸载然后重新安装文件系统,我们只需要传递–o(选项)标志,然后传递选项remount–o标志允许您从命令行传递文件系统选项,如rwro。 当我们重新挂载/文件系统时,我们只是简单地传递了 remount 文件系统选项:

# mount | grep root
/dev/mapper/md0-root on / type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

如果使用mount命令显示已挂载的文件系统,则可以验证/文件系统是否已使用Read-Write访问权限重新挂载。 由于文件系统类型是xfs,重新挂载应该导致文件系统执行一致性检查和修复。 如果我们对/文件系统的完整性有任何怀疑,我们的下一步应该是简单地重新启动 NFS 服务器。

如果服务器无法挂载/文件系统,则将自动调用xfs_repair实用程序。

验证

此时,我们可以看到 NFS 服务器的文件系统问题已经恢复。 现在我们应该验证我们的 NFS 客户机是否能够写入 NFS 共享。 但是在我们这样做之前,我们还应该首先重新启动我们之前停止的 NFS 服务:

[nfs]# systemctl start nfs
[nfs]# systemctl status nfs
nfs-server.service - NFS server and services
 Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled)
 Active: active (exited) since Mon 2015-04-27 22:20:46 MST; 6s ago
 Process: 2278 ExecStopPost=/usr/sbin/exportfs -f (code=exited, status=0/SUCCESS)
 Process: 3098 ExecStopPost=/usr/sbin/exportfs -au (code=exited, status=1/FAILURE)
 Process: 3095 ExecStop=/usr/sbin/rpc.nfsd 0 (code=exited, status=0/SUCCESS)
 Process: 3265 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
 Process: 3264 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
 Main PID: 3265 (code=exited, status=0/SUCCESS)
 CGroup: /system.slice/nfs-server.service

一旦 NFS 服务启动,我们就可以在客户端使用touch命令进行测试:

[db]# touch /data/testfile.txt
[db]# ls -la /data/testfile.txt 
-rw-r--r--. 1 root root 0 Apr 28 05:24 /data/testfile.txt

看来我们已经成功地解决了这个问题。

作为边注,如果我们注意到对 NFS 共享的请求花费了很长时间,那么可能需要在客户端卸载并挂载 NFS 共享。 如果 NFS 客户端没有识别 NFS 服务器已经重新启动,这是一个常见问题。

总结

在本章中,我们深入探讨了如何挂载文件系统、如何配置 NFS 以及在文件系统进入Read-Only模式时应该做什么。 我们甚至更进一步,手动修复物理磁盘设备有问题的文件系统。

在下一章中,我们将通过排除硬件故障进一步讨论这个问题。 这意味着查看硬件消息的日志,对硬盘驱动器 RAID 集进行故障排除,以及许多其他与硬件相关的故障排除步骤。*