2011年3月29日星期二

如何避免 VPN 连接后会降低本地网络访问速度的问题

前言

上回说到了通过 VPN 进行翻墙,有不少朋友已经体会到了 VPN 的好处,许多网站从无法访问,变得可以流畅的访问了。但是,我们经常面临一个问题,VPN 拨通后,所有流量都会流经 VPN,导致本地的网络访问可能会很不顺畅,有的速度非常缓慢,有的甚至不能访问。特别是对于那些在使用 VPN 同时还在进行本地下载的用户,这个问题更加明显。对于那些收费的 VPN 而言,因为它们速度较快,所以感觉问题不大。但是,对于那些免费的、速度比较缓慢、甚至限制流量的 VPN 来说,这就是一个比较严重的问题了。很多人不得不同时只干一件事情,要使用 VPN 就停止本地网络的访问,要访问本地网络就需要断开 VPN。那么,可不可以只有访问镇外的流量走 VPN,而本地网络依旧使用本地连接呢?答案自然是可以的。
 

解决方案

其实这个问题在使用 VPN 之前就存在过。使用过教育网的朋友肯定有经验,国内流量是免费的,而国外流量是收费的。一般来说大家都会访问国内网络,但是毕竟偶尔也需要访问国外网络。有的学校做的比较一刀切,干脆禁止国外流量,而有的学校则收取国外流量高昂的费用。所以,在教育网生活的时间,大家都到处寻找国内代理服务器,一时间代理服务器搜索软件、代理服务器列表比比皆是。当时我们学校面临这个问题的时候,我们采用了另外一种办法,除了教育网光纤接入外,我们还申请了电信的包月 ADSL 作为第二链路。当时对于非服务器的网段使用的是 NAT,分别在教育网和ADSL接入上做NAT。然后,修改路由设置,凡是国内流量,都路由到教育网光纤接口;凡是国外流量,都路由到电信的 ADSL 接口。虽然,国外流量的带宽不大,但是对于当时的学生对国外网站的需求不高的情况下,这个问题还是得到了很好的解决。

再回过来看我们 VPN 的问题,会发现其实是一个问题。我们可以添加路由,将所有本地的网络(如上例的所有国内网络范围),路由到我们使用 VPN 前的默认网关。而剩余的,也就是国外的网络范围,自动流经 VPN。这就很好的解决了 VPN 降低本地网络访问速度的问题。

然后,我们需要寻找到本地的 IP 地址范围,在本例中,我使用的是国内的地址范围。因为这个数据库属于公开数据,我们可以从 CNNIC 或者 APNIC 获得相关数据。剩下的就是对地址库进行分析,然后生成路由添加和删除的脚本就可以了。

由于我的机器分别有 Ubuntu 和 Windows 7,因此,我将针对这两个系统写脚本。脚本语言不同,但是参数是一样的。
  • on    表明开启国内路由走默认网关的配置。也就是说添加国内网络的路由指向原默认网关。
  • off    表明关闭这个功能。也就是说删除之前操作所添加的路由。
  • update    是指下载更新 IP 地址库。
 

代码编写以及使用

Linux 对于 Linux 最通用的莫过于 Shell 了,因此,我使用 Bash 完成了这个脚本。虽然我是在 Ubuntu 系统上测试的,但是它应该支持大部分 Linux 系统。


下载脚本到本地,并启用路由配置
 
wget http://files.cnblogs.com/dancefire/vpnroute.zip
unzip vpnroute.zip
sudo bash vpnroute.sh on 

需要删除添加的路由,可以执行:
sudo bash vpnroute.sh off 

需要更新网络路由数据,可以执行:
bash vpnroute.sh update 

Windows

Windows 的批处理文件虽然也能写一些东西,但是总觉得它的功能太受限了。因此,这次我使用 PowerShell 来写这个脚本。PowerShell 是微软 2006 年推出的抗衡 *nix 中的 shell 的产品,通过紧密结合 .Net Framework 大幅提高命令行及脚本的能力。熟悉 linux shell 脚本的朋友会在使用中会发现有很多语法似曾相识。虽然有各种不适应,但是不得不说 PowerShell 还是很强大的。

下载脚本 http://files.cnblogs.com/dancefire/vpnroute.zip 到某个目录,比如 d:\tmp

修改 PowerShell 执行权限,默认只能执行签名脚本。:

开始->附件->Windows PowerShell->右键点击 Windows PowerShell->以管理员身份执行

在 PowerShell 命令行里执行:
Set-ExecutionPolicy -ExecutionPolicy Unrestricted

可能会提示如下信息:
执行策略更改
执行策略可以防止您执行不信任的脚本。更改执行策略可能会使您面临
about_Execution_Policies 
帮助主题中所述的安全风险。是否要更改执行策略?
[Y] 是(Y) [N] 否(N) [S] 挂起(S) [?] 帮助 (默认值为“Y”):
选择 Y,或者回车即可。

然后,进入下载文件所在目录,执行脚本
cd d:\tmp
.\vpnroute.ps1
 
如果脚本可以执行,它会返回如下帮助信息:

PS d:\tmp> .\vpnroute.ps1
Route networks of CN to the default gateway instead of VPN tunnel

usage: vpnroute.ps1 {on|off|update}

on Add routes of CN to default gateway
off Remove routes of CN
update Force download/update CN network data
 
如要添加路由,希望所有中国范围的IP使用默认的网关访问,就运行:
.\vpnroute.ps1 on

如果想要去掉添加的路由,就运行
.\vpnroute.ps1 off

想更新中国IP路由数据,就运行
.\vpnroute.ps1 update

我注意到 Windows 的 PowerShell 脚本执行速度不是很高,在 Linux 上使用 Bash 添加路由大约只需要10-20秒,在 Windows 上使用 PowerShell 需要 70-80 秒。大家如果看到有段时间没响应,不要着急,给他一些时间就会好的。

连接后,分别访问 http://www.ip.cnhttp://whatismyipaddress.com 以测试路由设置是否正常。

如果路由添加正确,访问国内网站 http://www.ip.cn 应该看到的是你国内的 ip。而访问国外网站 http://whatismyipaddress.com ,应该看到的是你 vpn 的 ip。

2011年3月28日星期一

如何使用 StrongVPN 翻墙

引言

翻墙很容易,身体矫健的,助跑两步,腾空、扒墙头、一撑就过去了;身体差点的,搬个梯子过来,悠哉游哉过去。换句话说,有把子力气的自己暴力解决,没力气的,用钱买个工具解决。三千法门,条条皆为大道。凡事皆是如此,众所周知的“翻墙”,自然亦是如此。

介绍

在这里我想不多说免费的办法,大家心照不宣,说多了可能就用不了了。我这里说一下使用 VPN 的解决办法。VPN 有免费的,收费的。无论哪种都有很多种选择,具体哪个好,根据各地的网络环境差异不同。相对而言,免费的 VPN 比较慢,而且可能有连接时间和每日次数限制;收费的比较快,没有连接时间和次数限制。比较了一下看 Youtube HD 的速度后,我最终了选择了收费的 VPN。
需要声明的是,我只是阐述我所了解到的情况,所以不要认为我是在做广告。我所选择的是 StrongVPN。它支持 OpenVPN,目前解决了一些地方 ISP 把 PPTP 给禁掉的问题。我看到有每年 $85 的选择,可以用这个。需要注意的是,StrongVPN 中文网页上说错了,并不是只有每年 $155 的一种,而且也不必交什么 $25 的开户费。大家看英文网站(http://strongvpn.com/packages_china.shtml) 就自然明白了。网站推荐给中国用户的有两个,一个是 $85 全年的VPN:"4 City Special Open 1 Year",另一个是每月 $10,最少3个月的VPN:"Lite Open 3 months"。后者可以作为尝试使用。协议上支持 OpenVPN, L2TP, PPTP等,国内推荐使用 OpenVPN 协议。
账户开通后,还可以添加第二个PPTP账户,给手机用,每月 $2。不过目前 Android 手机使用 VPN 上有不少问题。Google 上面的 Issue list 相关问题已经有一年多没有Google的开发人员理会了,不知道Google怎么搞的。Android 上目前我发现 L2TP/IPSec PSK 还是比较好用的。使用 CyanogenMod 的用户应该不用太担心,CM 好像支持 OpenVPN。
需要说明的是,一个 VPN 账户只能一个客户端用。要是有第二台电脑就只能错开时间登录,不能同时使用。我想这也是为什么它有一个第二个 PPTP 账户的选项,因为开通这个可以专门给手机一个账户。

申请

申请的时候也很简单。支付方式如果填写 Paypal,基本上可以立刻开通,而且Paypal比较安全,也不会泄露你的银行或者信用卡信息。电话号码需要填写真实的电话号码,因为 StrongVPN 很可能会电话联系你,以确认身份。默认的地址一般选择旧金山,物理上离亚洲近。
开通后,在首页右上角上登录,在左边选择 VPN Accounts Summary。这会列出所有的开通的账户。对于 PC 而言,更多关注的是 "Your Open accounts"。如果 Status 是 active,就说明已经可以使用了。
点击 view greeting,这会显示配置所需要的信息。在 "STEP 1. DOWNLOAD" 里面,有一个链接
"Your OpenVPN Configuration Files"。把它下载下来,解压缩并且保存到合适的位置。一会儿配置的时候需要用。

具体配置

Windows 7

配置可以参考 StrongVPN 的帮助:
http://strongvpn.com/openvpn_win7.shtml

首先下载 OpenVPN Windows 客户端:
http://openvpn.net/index.php/open-source/downloads.html

目前的版本是 2.1.4:
http://swupdate.openvpn.net/community/releases/openvpn-2.1.4-install.exe

安装就是一路下一步就行。
在执行 OpenVPN 前,我们需要先把 配置文件复制到位。将前面下载并解压缩的文件,全部复制到安装目录的 config 目录下,如:C:\Program Files\OpenVPN\config。
然后,设置 OpenVPN 的执行权限。由于 OpenVPN 执行需要修改一些系统配置,比如路由配置,所以必须以管理员身份执行程序。点击:开始->程序->OpenVPN->[注意]右键点击 “OpenVPN GUI”->属性->兼容性->选中“以管理员身份运行此程序”,确定退出。
设置好了就可以运行了,点击:开始->程序->OpenVPN->OpenVPN GUI
程序正确运行后会在右下角有一个图标,右键点击这个图片,会看到和你 StrongVPN 账户同名的配置名称,点击里面的 Connect,等待一些时间,如果一切正常,链接成功后,右下角的图标颜色就从红色变到了绿色。此时你的所有流量都是通过 VPN 访问的了。如果碰到了问题,你需要检查配置过程是否有错,比如是否是以管理员身份执行的 OpenVPN 等。

Linux / Ubuntu

sudo apt-get install network-manager-openvpn-gnome
安装完成后,建议重新启动,可能会避免一些错误。

方法一

设置可以参考:http://strongvpn.com/ubuntu_open.shtml

点击右上角的网络连接图标:VPN连接->配置 VPN(C)...
这会弹出一个配置窗口,点击“导入”,然后选择前面下载并解压缩好的配置文件中的ovpnXXX.ovpn文件。配置文件会被导入。点击“高级”->TLS 认证,注意“密钥方向”, 确保其结果为 1。或者确保内部的值和 ovpnXXX.ovpn 的内容一致。之所以需要确认这个配置的原因,是因为 network-manager-openvpn 有一个 bug 可能会错误的忽略这个“密钥方向”,从而配成 0,导致 VPN 无法连接。还需要注意一点,不要勾选自动连接,毕竟我们只在需要的时候访问 VPN。确定无误后,点击确定、应用、关闭。
这个时候点击右上角网络连接图标->VPN连接,里面会显示出刚刚导入的ovpnXXX的连接,点击它就开始连接了。点击后,注意右上角的网络连接图标,如果 VPN 连接成功,会在图标的右下角出现一个锁头的标志,如果没有的话,可能哪里出了问题。
如果碰到错误,“VPN连接"ovpnxxx"失败,因为没有有效的VPN机密。”,那很可能是因为你忘记重启了。重启一下,再试试看。

方法二

将所有配置文件复制到 /etc/openvpn 目录下。并且执行:
cd /etc/openvpn sudo ln -s ovpnxxx.ovpn ovpnxxx.conf 
将 ovpnXXX 替换成你真实的文件名。
然后启动服务:
sudo /etc/init.d/openvpn start 
如果一切正确,openvpn会连接配置好的 VPN。这么配置的好处是每次启动 openvpn 都会自动连接,不需要用户登录。对于服务器类的环境,可能需要这么配置。

Android 手机

如果开通了 2nd PPTP 账户,或者把服务器设定到 PPTP 服务器上了。那么Android 手机也可以使用VPN服务加速网络访问。
VPN服务器一般支持 PPTP 和 L2TP 两种协议,某些互联网提供商的设置有误,可能会导致PPTP协议无法使用,因此,推荐使用L2TP。而且,我感觉 Android 对 L2TP 的支持要比 PPTP 好些。
设置的办法可以参考: http://vpnblog.info/strongvpn-l2tp-android.html

首先是找到配置参数,在 StrongVPN 登录后,点击 “VPN Accounts Summary”,点击 “Your PPTP accounts” 下面账户的 “View greeting”。这里面是配置信息,记录下来。如果大家浏览器上装了 FoxToPhone 或者ChromeToPhone 插件,可以复制相关信息到剪贴板,然后直接发送到手机上,省得在手机小键盘上打错了。
然后在 Android 手机上配置:
设置->无线和网络->虚拟专用网设置->添加虚拟专用网->添加VPN “L2TP/IPSec PSK”:
参考之前记录下来的信息配置。
名称自己随便定义;服务器填写配置中的”Server Address”的内容,也可以填写”Server IP”的内容;
然后,注意,点击“设置 IPSec 预共享密钥”,填入账户信息中的 “L2TP key (shared secret)”。下面的“启用L2TP密钥一定不要选中
配置好后,点击菜单按钮,然后选择“保存”。配置就结束了。
然后是连接,点击新建的VPN后,会询问你用户名、密码,这个填入账户信息中的”Login”和”Password”就没问题了。我一般选择记住用 户名。其实要是密码也能记住,我还希望记住密码,毕竟手机打密码比较困难。可以没有。我是把密码保存到即时贴里,然后需要输入的时候,复制粘贴就行了。

验证

连接成功后,访问一下 http://www.ip.cn,或者 http://whatismyipaddress.com 来确认你自己的 IP 地址是否已经变到了VPN服务器所在的地址。如果还是原始的地址,那说明 VPN 没有链接成功。

2011年3月9日星期三

记一次 superblock 损坏导致服务器无法启动的故障修复

前言

前几天接到朋友联系,说他的服务器坏了,启动不起来了。这是一个RHEL 4的服务器,而且是那种盗版RHEL 4,也就是说没有售后服务的,联系我问问能不能帮帮忙。我也很久没有弄过Linux系统上的东西了。只好尝试一下,庆幸的是,修好了,并帮朋友维护了一段时间,在此记录一些修复和维护中碰到的问题。修复 superblock 本身并不复杂,我觉得值得记录的是修复过程中的思考过程,和修复所需要注意的问题。

一、启动故障


系统无法启动,启动时内核panic:
Uncompressing Linux Ok, booting the kernel.
audit(1297269214.612:0): initialized
ide2: I/O resource 0x3F6-0x3F6 not free.
ide2: ports already in use, skipping probe
Red Hat nash version 4.1.18 starting
File descriptor 3 left open
Reading all physical volumes.  This may take a while
/dev/hda: open failed: No medium found
Found volume group "VolGroup_ID_17253" using metadata type lvm2
File descriptor 3 left open
8 logical volume(s) in volume group "VolGroup_ID_17253" now active
File descriptor 3 left open
VFS: Can't find ext3 filesystem on dev dm-0.
mount: error 22 mounting ext3
mount: error 2 mounting none
switchroot: mount failed: 22
umount /initrd/dev failed: 2
Kernel panic - not syncing: Attempted to kill init!
_

看到这个报错,我Google了一下,很快就发现,这很有可能是硬盘的superblock [1] 坏了,因此感觉需要修复superblock。

询问了一下,瘫痪之前都发生了些什么。朋友提到了一个情况,在瘫痪前,他发现有一个目录存储了太多的文件了(确实非常的多,大约是几百万到上千万个文件,程序设计上有问题),这是他写的一个php做缓存的脚本生成的后缀为.php的文件,绝大多数都已经是垃圾文件了。他觉得太占磁盘空间了,想清理一下,于是用下面的命令删除这些生成的文件:
find . -name "*.php" -exec rm -f {} \;

可是执行命令后,等了几分钟,发现系统没有反应。于是就Ctrl-C了,后来发现系统还是很慢,于是就执行reboot了。接下来,系统就启动不起来了。

可以推断,其实并不是系统没有反应,而是删除如此大量的文件,需要相当的时间,当Ctrl-C后,磁盘写入行为并没有因此而立即停止,在如此密集磁盘写入时执行reboot,确实比较容易引起磁盘上的故障。再加上这块硬盘虽然是ext3,但是日志使用的是默认的ordered [2],出问题的几率就更大了,所以怀疑是 superblock 的可能性就更大了。

二、修复环境



当时朋友有些慌张了,因为他认为这是他操作失误导致服务器瘫痪,有些不知道该怎么办了,那天我有事情比较忙,打算晚些时候再回来帮他。随后的操作中可以看到他做了很多危险的操作,我会一一提出来,大家有类似情况的时候,一定要注意。

首先是他把硬盘直接拆下来来了,打算拿回公司备份。备份的想法是好的,如果有合适的服务器,拆下来接过去备份也是对的。但是问题就在于,这是一块SCSI接口的硬盘,是一堆Linux分区,使用的还是LVM [3],而他公司没有一个SCSI接口的机器,他可能还需要去市场上买个SCSI卡,而且,他也没有一台Linux的机器,全是Windows,他甚至打算使用explore2fs之类的不成熟的软件来挂载这块硬盘。

这问题就大了。买的SCSI卡的稳定性如何?别是杂牌的,卡再造成硬盘的二次数据损失。用Windows备份可能损坏的硬盘的数据是非常危险的,explorer2fs这类Windows挂载ext2/3分区的软件从来就没成熟过,至今为止还有大量的特性不支持,只是试验产品。使用他们挂载分区,很可能会造成数据损失。另外,之前说过,问题很可能是superblock出问题了,这种情况下,不修复 superblock ,谁也别想挂载成功,而Windows上显然没有这类软件。

碰到这类问题,比较好的办法是使用另一台具有SCSI接口的Linux,进行修复、挂载、备份。当然,对于朋友而言,这不现实。那就折中,还在原服务器上修复,但是使用 Linux LiveCD 进行修复,将数据备份到外置 USB 硬盘上。

这里需要注意的是,即使硬盘有几个分区,只坏了一个,备份到其它貌似还好的分区似乎也不是什么大问题。但是,在修复、备份的时候,一定要尽可能的避免被修复磁盘的写操作,无论是哪个分区。因为在问题确认修复前,你并不能肯定只有那个报错的分区有故障,其它一切正常。

那天朋友在机房的时候,他什么都没有带,只能眼睁睁的一次次的重启,看错误日志,试着能不能进系统。这是很不好的,如果怀疑硬盘出了故障,那么这么一次次重启很容易加重问题,因此一定要避免。

没有任何修复环境是没有办法工作的。我让朋友回去准备几张光盘,都是可引导的修复盘。朋友对 Linux 有一些经验但不是很熟悉,折中起见,我让朋友准备了 Fedora 14、Ubuntu 10.10、UBCD等光盘备用,并且打算使用 Fedora 14 的 Live CD 进行修复。并且第二天带一个大容量的,最好是空的USB硬盘来,备份数据。并且,在家用 Fedora 14 启动一下,看看怎么进入命令行模式,第二天启动的时候,不要进入图形界面。在命令行模式下修复。

第二天约好时间后,看了他的gtalk留言,感觉是一身冷汗啊。

首先是告诉我,在服务器上 Fedora 启动后直接进桌面了,没有选项,点了点不知道怎么修复,甚至打开了故障硬盘的几个分区,没找到需要备份的文件。于是乎,又启动了Ubuntu选择了修复坏系统,结果发现要安装文件,然后报错说没有硬盘,于是退出了。

敢情这哥们直接把我说的话当耳旁风,在故障服务器上直接启动图形界面,更甚的是,他还尝试让 Ubuntu 覆盖安装 RHEL 4。幸好分区的 superblock 是坏的,不然全毁了。

这里面有三个错误。首先是不应该启动图形界面,其次是错误的理解了 Ubuntu 中修复系统选项的含义,然后是在菜单上点击服务器分区从而激发了绑定操作,很可能直接造成写入操作。
这些都应该是前一天晚上回家琢磨的,他显然偷懒了,直接拿故障环境练手。如果不是这哥们命大,而且系统出的问题没有那么严重,那么这些连续的错误,很可能造成不可挽回的结果。
虽然对于 Windows 用户来说,看着纯命令行觉得无从下手,但是,得到了美丽的界面往往意味着你得付出些什么。在以前的某些 LiveCD 中,加载图形界面的时候,由于各种驱动和程序的加载,错误的进行了硬盘的写操作,从而导致有些人抱怨过启动 LiveCD 导致硬盘数据二次损坏,最终使得修复无望。虽然,最近这些版本的 LiveCD 可能没有这类问题,但是为了避免万一,应当尽量减少对硬盘写入操作的可能性。既然所有修复行为都会在命令行模式下进行,那就没必要启动图形界面冒风险。

我让他带着 Ubuntu 的盘就是以防万一,万一 Fedora 启动出现奇怪的问题,我们可以用 Ubuntu 启动然后修复系统。而不是进入 Ubuntu的修复系统选项。那个选项是给 Ubuntu 系统准备的,是以光盘上的系统文件及配置覆盖硬盘上的系统文件及配置,从而达到修复系统的目的。这显然和我们要修复的 RHEL 驴唇不对马嘴。修复系统所需要的只是一个 Linux 环境而已。

至于最后在菜单上点击硬盘分区,甚至还打开里面的文件看看,这就是纯属找死了。系统已经报错了,即使能够挂载成功,文件系统也很可能是有问题的,必须在访问前先fsck,否则很可能会引发更大的问题。毕竟默认 Fedora 挂载可不是只读,而是可读写,混乱后,谁也无法预测会把硬盘写成啥样。

三、确认问题



该准备的光盘准备了,不该操作的操作也做了,这让我很无语,虽然怀疑仅仅是逻辑错误导致superblock坏掉,应该不会有大问题,但还是让我对这次修复的可能性感到怀疑。至少这朋友完全不按照我说的办,经常的做些自己觉得没什么的危险操作,哎,前景黯淡啊。

既然他已经改点的都点了,该造成的损坏基本上也造成了。那就在 Fedora LiveCD 的图形界面下修复吧,至少他还可以更方便的把命令返回结果通过 firefox 给我发过来。比用 android 手机通讯好多了。

首先我需要知道,都有哪些文件系统被他挂载了,另外,系统上有哪些分区。
[root@localhost liveuser]# mount |grep LogVol
/dev/mapper/VolGroup_ID_17253-LogVol4 on /media/0edef924-567f-45fc-9609-51722cad6e9e type ext3 (rw,nosuid,nodev,uhelper=udisks)
/dev/mapper/VolGroup_ID_17253-LogVol7 on /media/ee0c40c6-d9d1-4a81-9806-9991621db1dd type ext3 (rw,nosuid,nodev,uhelper=udisks)
/dev/mapper/VolGroup_ID_17253-LogVolHome on /media/f524534e-3d24-4a22-b475-9e4b7dac0d35 type ext3 (rw,nosuid,nodev,uhelper=udisks)
/dev/mapper/VolGroup_ID_17253-LogVol6 on /media/12953c57-baba-4358-baeb-cdd17d6513a2 type ext3 (rw,nosuid,nodev,uhelper=udisks) 

好嘛,据我所知,服务器上好像有5个绑定目录的分区,LogVol{3,4,6,7,Home},LogVol3 好像就是那个坏的分区,想绑也绑不上,除了它,他全绑上了。

我需要确定 LVM 总共有哪些分区,lvscan 命令可以告诉我们LVM下面的分区情况。
[root@localhost liveuser]# lvscan
ACTIVE '/dev/VolGroup_ID_17253/LogVol3' [10.00 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol4' [1.06 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol7' [53.56 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol6' [5.38 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol1' [2.00 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol0' [2.00 GiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVol2' [64.00 MiB] inherit
ACTIVE '/dev/VolGroup_ID_17253/LogVolHome' [29.44 GiB] inherit 

嗯,LogVol{0,1}是交换分区,LogVol2肯定不是我们的目标,那么缺失的是LogVol3,这个分区出问题了。如此,我们手动绑定一下 LogVol3 看一下报错信息。
[root@localhost liveuser]# mkdir /media/myroot
[root@localhost liveuser]# mount -t ext3 /dev/mapper/VolGroup_ID_17253-LogVol3 /media/myroot
mount: wrong fs type, bad option, bad superblock on /dev/mapper/VolGroup_ID_17253-LogVol3,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so 

这里我们可以直接看到报错说 'bad superblock' 信息。
然后我们再看一下内核报错。 

[root@localhost liveuser]# dmesg | tail -n 20
[ 343.583694] EXT3-fs (dm-3): mounted filesystem with ordered data mode
[ 343.585926] SELinux: initialized (dev dm-3, type ext3), uses xattr
[ 346.179128] EXT3-fs: barriers not enabled
[ 346.183702] kjournald starting. Commit interval 5 seconds
[ 346.189688] EXT3-fs (dm-4): using internal journal
[ 346.189694] EXT3-fs (dm-4): mounted filesystem with ordered data mode
[ 346.193216] SELinux: initialized (dev dm-4, type ext3), uses xattr
[ 348.911539] EXT3-fs: barriers not enabled
[ 348.918113] kjournald starting. Commit interval 5 seconds
[ 348.918151] EXT3-fs (dm-9): warning: mounting fs with errors, running e2fsck is recommended
[ 348.922722] EXT3-fs (dm-9): using internal journal
[ 348.922728] EXT3-fs (dm-9): mounted filesystem with ordered data mode
[ 348.922738] SELinux: initialized (dev dm-9, type ext3), uses xattr
[ 350.225535] EXT3-fs: barriers not enabled
[ 350.230730] kjournald starting. Commit interval 5 seconds
[ 350.236075] EXT3-fs (dm-5): using internal journal
[ 350.236081] EXT3-fs (dm-5): mounted filesystem with ordered data mode
[ 350.241386] SELinux: initialized (dev dm-5, type ext3), uses xattr
[ 1957.796112] EXT3-fs (dm-2): error: can't find ext3 filesystem on dev dm-2.
[ 2688.247855] EXT3-fs (dm-2): error: can't find ext3 filesystem on dev dm-2. 


这里我们看到,内核报错说无法在 dm-2 上找到 ext3 文件系统。这很有可能就是 superblock 损坏造成的问题。

另外,我们看到,正如我所担心的那样,不仅仅是 dm-2 (LogVol3) 有故障, dm-9 分区也有故障。很可能其它分区也有问题,都需要在使用前,进行磁盘检查 fsck。冒失的访问,很可能会造成数据损坏或丢失。

如此,我们基本上可以确定是 superblock 的损坏,至于是否还有其它故障,以及是否有数据损失,需要在 fsck 之后才能知道了。

四、镜像备份损坏的硬盘



执行 fsck 会对磁盘进行写操作,我们需要在此之前对磁盘进行镜像备份。这样万一 fsck 的修复造成了更大的损失,我们还可以恢复原始状态。

我让朋友插上 USB 硬盘,桌面上会自动出现这个硬盘的图标,如果没有菜单上也会有,点击菜单项打开这个 USB 硬盘,会触发 Fedora 自动绑定该硬盘。这么操作省的朋友敲命令了 (心里想,反正之前不该点的也都点了,破罐破摔吧~~)。

通过 mount 命令找到新绑定的磁盘路径:
/dev/sdb1 on /media/BACKUP type fuseblk (rw,nosuid,nodev,allow_other,blksize=4096,default_permissions) 

然后,开始镜像 LogVol3:
[root@localhost ~]# dd if=/dev/VolGroup_ID_17253/LogVol3 | gzip > /media/BACKUP/server_root_image.gz
20971520+0 records in
20971520+0 records out
10737418240 bytes (11 GB) copied, 666.429 s, 16.1 MB/s 

确认一下文件确实存在:
[root@localhost ~]# ls -l /media/BACKUP/*.gz
-rwxrwxrwx. 1 liveuser liveuser 5943229016 Feb 10 17:29 /media/BACKUP/server_root_image.gz 

五、修复


先进行第一次修复尝试。
[root@localhost liveuser]# fsck.ext3 -B 1024 /dev/mapper/VolGroup_ID_17253-LogVol3
e2fsck 1.41.12 (17-May-2010)
fsck.ext3: Superblock invalid, trying backup blocks
fsck.ext3: Bad magic number in super-block while trying to open /dev/mapper/VolGroup_ID_17253-LogVol3
The superblock could not be read or does not describe a correct ext2
filesystem. If the device is valid and it really contains an ext2
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
e2fsck -b 8193 <device> 

这里面说无法修复,原因是 superblock 损坏了,所以 fsck 无法定位相关分区数据。建议使用备份的 superblok。

我们知道,superblock 对于分区而言非常重要,因此 ext2/3 文件系统将 superblock 备份到了磁盘的各个位置,如此多的备份,降低了所有 superblock 备份都损坏的概率。

可是问题是,这些备份在哪里呢?superblock 的备份是和 block size 相关的。询问后得知,这个服务器上的分区的参数都是默认设置,只是调整了一下大小和个数。既然如此,那么所有的分区都应该是同样的 block size,那么它们备份 superblock 的相对位置也都一样。

鉴于此,我们打算通过 LogVol7 分区给出一个superblock 备份相对位置的列表:
[root@localhost liveuser]# dumpe2fs /dev/VolGroup_ID_17253/LogVol7 | grep -i superblock
dumpe2fs 1.41.12 (17-May-2010)
Primary superblock at 0, Group descriptors at 1-4
Backup superblock at 32768, Group descriptors at 32769-32772
Backup superblock at 98304, Group descriptors at 98305-98308
Backup superblock at 163840, Group descriptors at 163841-163844
Backup superblock at 229376, Group descriptors at 229377-229380
Backup superblock at 294912, Group descriptors at 294913-294916
Backup superblock at 819200, Group descriptors at 819201-819204
Backup superblock at 884736, Group descriptors at 884737-884740
Backup superblock at 1605632, Group descriptors at 1605633-1605636
Backup superblock at 2654208, Group descriptors at 2654209-2654212
Backup superblock at 4096000, Group descriptors at 4096001-4096004
Backup superblock at 7962624, Group descriptors at 7962625-7962628
Backup superblock at 11239424, Group descriptors at 11239425-11239428 

尝试使用 32768 的备份:
[root@localhost liveuser]# fsck.ext3 -B 1024 -b 32768 /dev/mapper/VolGroup_ID_17253-LogVol3
e2fsck 1.41.12 (17-May-2010)
fsck.ext3: Bad magic number in super-block while trying to open /dev/mapper/VolGroup_ID_17253-LogVol3
The superblock could not be read or does not describe a correct ext2
filesystem. If the device is valid and it really contains an ext2
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
e2fsck -b 8193 <device> 

这个备份还是不行,再换一个:
[root@localhost liveuser]# fsck.ext3 -b 98304 /dev/VolGroup_ID_17253/LogVol3
e2fsck 1.41.12 (17-May-2010)
Superblock needs_recovery flag is clear, but journal has data.
Recovery flag not set in backup superblock, so running journal anyway.
/dev/VolGroup_ID_17253/LogVol3: recovering journal
Adding dirhash hint to filesystem.
Pass 1: Checking inodes, blocks, and sizes
Inode 81, i_blocks is 8, should be 0. Fix<y>? 

不错,98304 这个备份是好的。已经修复了一些内容了,只要继续就很有可能修复系统。不过,当我让朋友点击 y 确认的时候,悲剧又发生了。他在复制粘贴返回信息的时候,习惯了 Windows 的 Ctrl-C 和 Ctrl-V,呃,我们要知道,Ctrl-C 在 命令行下有另外的含义,就是终止程序运行。结果,他按 Ctrl-C 了……
[1]+ Stopped fsck.ext3 -b 98304 /dev/VolGroup_ID_17253/LogVol3 

磁盘修复一半的时候强行终止退出?我现在非常不确定系统当前的状态和磁盘的状态,我只好让朋友重新启动,重来。重启前跟他说,千万不要再做任何异常的操作了,否则可能系统会无法恢复的。

很不幸,我的话又变成了耳旁风。重启后,他兴高采烈的告诉我说,可以看见 LogVol3 啦,不过有些文件夹还是打不开啊。我晕倒~~

我非常怀疑他是不是关心硬盘的数据。这个硬盘仅仅是恢复了 superblock,但是必然还有很多其它的问题,冒然以可写形式绑定,磁盘很可能会丢数据的。我给了他严重警告,再这么做我就不帮忙了,我替你担心半天,你反而不听我劝,混不在乎。

重新开始修复硬盘:
[root@localhost liveuser]# fsck.ext3 -y -b 98304 /dev/VolGroup_ID_17253/LogVol3

Free blocks count wrong for group #78 (32254, counted=5049).
Fix? yes
Free blocks count wrong for group #79 (32254, counted=4724).
Fix? yes
Free blocks count wrong (2566343, counted=1869026).
Fix? yes
Free inodes count wrong for group #0 (16373, counted=16288).
Fix? yes

/dev/VolGroup_ID_17253/LogVol3: ***** FILE SYSTEM WAS MODIFIED *****
/dev/VolGroup_ID_17253/LogVol3: 229199/1310720 files (1.6% non-contiguous), 752414/2621440 blocks 

经过了一段时间的等待,LogVol3 修复终于完成了。由于得知当时LogVolHome进行了大量的读写,因此,虽然可以挂载,但是非常怀疑其中也有故障,因此也许进行磁盘检查和修复:
[root@localhost /]# fsck.ext3 /dev/VolGroup_ID_17253/LogVolHome
e2fsck 1.41.12 (17-May-2010)
/dev/VolGroup_ID_17253/LogVolHome contains a file system with errors, check forced.
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/VolGroup_ID_17253/LogVolHome: 2301889/3859072 files (9.9% non-contiguous), 2554717/7716864 blocks 

果然,确实有错误,并且修复了。
然后,尝试挂载 LogVol3,看这次是否没有问题了:
[root@localhost /]# mkdir /media/myroot
[root@localhost /]# mount -t ext3 /dev/VolGroup_ID_17253/LogVol3 /media/myroot 

一切正常,没有任何报错。磁盘修复基本可以宣告完成,接下来就是备份和重新启动了。

六、备份重要数据


重新启动系统,如果一切正常,系统会正常加载所有的服务器,并且开始提供服务,那时数据就会发生改变了,在还不知道服务器是否正常的情况下贸然启动服务器,而没有备份,这是危险的。因此,我们先备份重要数据。
[root@localhost /]# cd /media/myroot
[root@localhost myroot]# tar -czvf /media/BACKUP/www.tgz www
[root@localhost myroot]# tar -czvf /media/BACKUP/server_lampp.tgz opt/lampp
[root@localhost myroot]# tar -czvf /media/BACKUP/server_mysql.tgz opt/lampp/var/mysql
...

最后确认一下数据是否已经备份好了。
[root@localhost myroot]# ls l /media/BACKUP
total 6287380
-rwxrwxrwx. 1 liveuser liveuser 92690926 Feb 10 19:35 server_lampp.tgz
-rwxrwxrwx. 1 liveuser liveuser 28670158 Feb 10 19:30 server_mysql.tgz
-rwxrwxrwx. 1 liveuser liveuser 5943229016 Feb 10 17:29 server_root_image.gz
-rwxrwxrwx. 1 liveuser liveuser 373677732 Feb 10 19:34 www.tgz
...

七、重新启动



分区修好了,fsck检查各个分区也都没问题了,该备份的都备份了。可以尝试重新启动系统了,祈祷吧……

很不幸,没起来。:(

启动的过程,系统报错:
Remounting root filesystem in read-write mode:
Setting up Logical Volume Management:
Checking filesystems
/boot: clean, 38/26208 files, 15754/104420 blocks
/dev/VolGroup_ID_17253/LogVol4: clean, 22/139392 files, 12950/278528 blocks
/dev/VolGroup_ID_17253/LogVol7: clean, 132904/7028736 files, 882601/14041088 blocks
/dev/VolGroup_ID_17253/LogVol6: clean, 22314/704512 files, 168941/1409024 blocks
/dev/VolGroup_ID_17253/LogVolHome contains a file system with errors, check forced.
/dev/VolGroup_ID_17253/LogVolHome:
Inode 1340876 is too big.
/dev/VolGroup_ID_17253/LogVolHome: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.
(i.e., without -a or -p options)
[FAILED]
*** An error occurred during the file system check.
*** Dropping you to a shell; the system will reboot
*** when you leave the shell.
*** Warning -- SELinux is active
*** Disabling security enforcement for system recovery.
*** Run 'setenforce 1' to reenable.
Give root password for maintenance
(or type Control-D to continue): _ 

经过Google,发现这是 e2fsprogs,也就是 fsck.ext3 所在包,早期版本的一个bug。如果inode节点很大,会触发这个bug。1.40以后就没有这个问题了,这也看出来我一直坚持要升级的原因之一。越早的版本的软件包含的bug就越多,每年新的版本都会修复大量的bug。而那些坚持版本越老越稳定的人,实际上是非常错误的。太新和太老都是不合理的,要取一个合适的折中。像 RHEL 4,甚至5,就太老了。其内很多版本包含了大量的bug,可是由于没有升级到新的版本,RedHat不得不花大力气去将修复bug的补丁打到老的软件上,而有相当数量的补丁是完全基于新版本API的,那些补丁就没有办法打在老软件上。RHEL/CentOS所谓的长期稳定,也就是因为RedHat有大量的人力在做这种将新软件的补丁打到老软件上的事情。可是,有些时候,我们直接用新的版本会更好。

我没心思在LiveCD里面升级e2fsprogs,这是个危险的操作。触发这个bug的条件是一个目录下的文件太多,从而导致inode过大。了解了一下,那个目录正好是服务器故障前正在清理的目录。那么我们就先把这个目录清理掉再说。

由于这个目录包含有太多的文件,计算了一下清理这个目录会花1-2个小时。已经是凌晨了,写了个脚本,开始执行,清理完文件后,会自动重启。基本上该做的都做了,应该没有什么问题了。脚本执行后,就让朋友回家好了。

很幸运,朋友到家后,给我发来消息,服务器正常启动了,Web应用也都开始正常提供服务了。一切似乎都恢复正常了。很庆幸,没有机会用上备份(那就意味着要重装系统之类的大操作了)。

八、后记



这次系统故障导致我朋友很紧张的原因之一就是系统没有备份。最近一次备份也是几个月前,因此如果硬盘无法恢复,那么直接造成的结果就是这几个月的工作全部丢失。其实使用Linux备份还是比较容易的。最简单的办法是用crontab,定义的压缩一份重要数据,传到别的服务器上去,或者复制到别的物理硬盘上。可惜由于他们公司对 Linux 熟悉的人不多,因此没有人去做而已。

这也反映了一个问题。我们经常看到类似于,“什么服务器最安全快速?”、“什么编程语言效率最高?”的问题。这类问题实际上问题本身就有问题。没有最安全的服务器,没有最好的编程语言,只有最合适你的。

很多人都说 Linux 如何如何安全,Windows 如何如何脆弱。真不知道这些人从哪得到的数据?还是大脑进水了?如果你说的是精心配置下的服务器,那么很难说谁更安全,Windows 的内核非常健壮,相比 Linux 而言,更稳定和安全。你只要查一下 Linux 内核在过去几年内所出现的安全漏洞的数量,对比一下 Windows 2000/2003内核的漏洞的数量,你就会发现这个问题。而 IIS 出现的安全漏洞,和 Apache 比起来,谁也不敢说谁更安全。而就配置而言,我们都可以对每个服务器配置执行账户,限定用户权限。相比而言,Windows 的 ACL 比 Linux 的要更复杂和完善。当然,Linux 也有 Linux 的优势。它们是处于一个安全等级上的。无论哪个系统,都可以配置出安全、高效的服务器。

但是,你得精心配置啊。我想大多数人对二者的安全配置并不熟悉,也似乎没有花大量的经历和试验去熟悉。那么对大多数人而言,似乎默认配置就是最好的。就像我这个朋友,问他各个配置为什么这样,为什么那样,他几乎都是一个答案,不知道和默认就是这样,没改。这让我很无语。

安全界上,凡是默认配置的,基本都是有隐患的。大多数入侵啊、蠕虫啊,也都是针对默认配置的机器做的。不管你是 Linux 也好,Windows 也罢。只要是默认配置,你就不要提什么谁更安全,那是扯淡。

还有一个问题就是升级。这个服务器用的是 RHEL,貌似国内好像比较喜欢用这个。虽然其内的软件很老,但是这个发布确实比较稳定,你要是用正版的,有售后支持,我也觉得没什么。但是问题是,国人似乎用盗版成瘾。觉得既然能买 RHEL的盗版盘(甚至是下载,都不用买),为什么还花钱?这个服务器就是如此。可是,你用盗版,如何升级?而且,盗版的售后找谁?国外用RHEL的人多,那是冲着售后去的,人家有24小时应急响应,服务器出了问题很快就能解决。国内人用 RHEL,还不用带售后,这是为啥?百思不得其解。哪怕你用CentOS呢,虽然没有售后,但起码可以升级啊。呵呵,也许人性使然。

不论如何,服务器修好了。一切似乎又恢复了正常。但是,真的如此么?造成这次故障的,致使那个累积了太多文件的php缓存代码还在按照以往工作着,今天出现的问题,说不定什么时候就又会出现。问题需要进一步的挖掘,才能真正的让服务器,健康的运行。当然,今天那个朋友可以好好睡个觉了,下一次出问题,起码也是半个月后了,还有时间来分析和解决深层次的问题。

参考


[1] http://www.cyberciti.biz/tips/understanding-unixlinux-filesystem-superblock.html
[2] http://en.wikipedia.org/wiki/Ext3#Journaling_levels
[3] http://www.ibm.com/developerworks/cn/linux/filesystem/lvm/lvm-1/index.html


注:从 http://www.cppblog.com/dancefire/archive/2011/03/09/fix-bad-superblock-in-linux.html 合并过来。