首页,我的备份数据中心

新的 Linux 用户经常问我“学习 Linux 的最佳方式是什么?”我的建议总是归结为:安装并使用 Linux(任何发行版都可以,但稳定的版本效果更好),并随意摆弄它。不可避免地,你会弄坏一些东西,然后不要重新安装,而是强迫自己修复你弄坏的东西。这就是我的建议,因为我个人通过修复自己的问题学到的关于 Linux 的知识比其他任何方式都多。多年这样做之后,你开始对自己的 Linux 故障排除技能建立信心,这样无论遇到什么问题,你都会认为只要你努力足够长的时间,你就能解决它。
最近,当我的 KVM 主机出现问题时,这种信心受到了考验。断电后,它拒绝启动一个虚拟机,而这个虚拟机几乎是我所有主要个人服务器。在本文中,我将详细介绍一个几乎让我难倒的问题,并展示我如何在非传统的地方(至少对我而言)找到解决方案。
设置在我深入探讨我的问题之前,了解我的设置会有所帮助。虽然我在家里有服务器,但我的主服务器托管在数据中心。我与一位朋友共享服务器,因此物理服务器仅充当安全的 KVM 主机,我将服务器的 RAM 和 CPU 在两个虚拟机之间 50/50 分配。我最重要的所有服务,从我的主 DNS 服务器和我和我直系亲属的电子邮件,到许多不同的网站和博客,甚至我的主要 Irssi 会话都位于这两个虚拟机之一上。我最终从我家连接的服务器托管辅助 DNS 和电子邮件,但由于只有 1 兆比特的上传连接,我在家没有为外部世界托管太多其他内容。
有一天(当一位亲戚碰巧从外地来访时),我注意到我的主服务器和托管它的物理服务器都不可用。我通知了我在数据中心的联系人,结果是意外的停电影响了我的机柜。我正带着我的亲戚去海岸边玩一天,那里离像样的手机信号很远。所以,既然我无能为力,我就假设在我那天下午回到城里之前很久,电力就会恢复,除了损失一年多的正常运行时间外,我将恢复正常运行。
除了同步之外的一切当我回到城里,我的主服务器仍然宕机时,我第一次意识到存在真正的问题。但是,我可以登录到物理主机;所以起初我并不太担心。毕竟,我以前见过 KVM 实例无法从物理主机重启中恢复的情况。过去,要么是因为没有设置 VM 在启动时启动,要么有时甚至是任性的 libvirt apparmor 配置文件阻碍了启动。通常,一旦我登录到物理主机,我就可以更改任何错误的设置,禁用任何麻烦的 apparmor 模块,然后使用 virsh 手动启动我的 VM。这次不同了。
当我的 VM 无法手动启动时,我准备责怪 AppArmor。它过去曾阻止 VM 启动,但这次,无论是将 libvirtd AppArmor 模块设置为抱怨模式,禁用所有 AppArmor 模块,甚至强制停止 AppArmor 似乎都没有帮助。我甚至求助于重启物理主机,以听取 AppArmor 的警告,即在它运行后强制停止它可能会导致某些模块行为异常。没有任何帮助。当我连接控制台到正在启动的 VM 时,我开始看到初始内核错误,就好像它在挂载根文件系统时遇到问题一样。太棒了。停电损坏了我的数据吗?
故障排除过程的下一步是尝试从救援磁盘启动。使用 KVM,添加本地 ISO 映像相对容易,就像它是 CD-ROM 一样。因此,经过一番努力,我发现我实际上可以启动救援磁盘,并从救援磁盘确认我可以挂载我的 VM 驱动器,并且数据似乎没有损坏。那么为什么它无法启动呢?在我从救援磁盘运行手动 fsck 后,我尝试重新加载 GRUB,那时我得到了关于问题性质的第一个奇怪线索——即使从救援磁盘,我也无法可靠地写入文件系统。我会得到虚拟 ATA 重置,即使我似乎可以很好地读取。
所以,我假设我的那个特定 VM 有一定程度的损坏,但由于我的数据没有受到影响,我认为在最坏的情况下,我可以生成一个新的 VM 并将数据迁移过去。所以,这就是我接下来尝试的,使用了我之前用于构建 VM 的 ubuntu-vm-builder 包装脚本。VM 似乎生成得很好;然而,再一次,即使这个全新的 VM 也拒绝正确启动,并且有相同的奇怪磁盘错误。
正是在这个时候,我的故障排除步骤开始变得有点模糊,因为我开始尝试更绝望的事情。我在 GRUB 中启动了不同的内核版本(毕竟,在服务器启动的这一年中,内核已经更新了几次)。我审计了我 VM 磁盘映像上的所有文件系统权限,并且我尝试以 root 用户身份启动 VM,以防万一。我甚至尝试将一个 VM 的磁盘从 qcow2 转换为 raw,但没有结果。即使是网络搜索也一无所获。这台服务器宕机的时间比以往任何时候都长,我开始用尽选择。
同步我的第一个突破出现在我决定将我刚刚生成的新 VM 复制到我在家几乎相同的硬件上,使用相同的发行版,看看我是否可以在那里重现这个问题。我选择新主机仅仅是因为 qcow2 文件系统按需增长,它碰巧拥有最小的磁盘,并且同步速度最快。这个过程非常简单直接。首先,我使用 colocated 主机上的 virsh 导出该 KVM 实例的配置 XML 文件
$ virsh dumpxml test1.example.net > test1.example.net.xml
然后,我将该 XML 文件复制到我的家庭服务器,创建了一个以该 VM 命名的本地目录来存储其磁盘映像,并将它们从物理主机同步过来
$ mkdir test1.example.net
$ rsync -avx --progress remotehost:/var/lib/libvirt/
↪images/test1.example.nett/est1.example.net/
一旦磁盘映像被复制,我必须编辑 test1.example.net.xml 文件,因为磁盘映像现在存储在一个新的位置。在我这样做之后,我再次使用 virsh 导入这个 XML 配置文件并启动 VM
$ virsh define test1.example.net.xml
$ virsh start test1.example.net
VM 实际上启动了!虽然我仍然不知道 colocated 服务器上的问题是什么,但我非常有信心,如果我可以同步我的主服务器,它就会在这台家用机器上运行。当然,由于我家里的 12Mb 下载、1Mb 上传连接,复制这个 VM 的 45GB 磁盘映像将需要更长的时间。除了花费的时间之外,这个过程与测试机器基本相同,只是主机启动后,我必须更改其网络配置以反映其新的公共 IP。
在我的服务器恢复运行后,我只需要更改一些 DNS 条目和防火墙规则以反映新的 IP,即使我家里上传连接速度较慢,我至少也有一些喘息的空间来排除 colocated 服务器上的问题。
最后的手段现在我的 VM 及其数据是安全的,服务也已恢复(如果有点慢),我感到可以自由地在我的 colocated 服务器上执行更极端的步骤。第一步是尝试弄清楚它与我的家庭服务器相比有什么不同。它们都安装了相同的 Ubuntu 10.04 服务器,并且大多数软件包都相同。幸运的是,我的家庭服务器上缓存了一些旧的 libvirt 和 KVM 软件包,所以起初我迭代了所有这些软件包,看看问题是否是由于某些升级造成的。一旦我用尽了这些方法,我又尝试了物理主机上不同的内核版本,但仍然没有结果。
请相信我,在那一周里,我尝试了我能想到的每一种故障排除措施,然后才最终采取倒数第二种手段。我甚至在考虑这一点就应该告诉你我有多绝望。最后的手段是从头开始完全重新安装——这是我还不准备做的事情。但我已经足够绝望了,所以我选择了倒数第二种手段:从 10.04 到 12.04 的就地发行版升级。一旦尘埃落定,我尝试了我的小型测试映像,它实际上工作了。我们又恢复正常了。
同步返回好吧,我们几乎恢复正常了。你看,我已经在家里的服务器上使用了好几天了,在电子邮件、博客和其他服务之间,它上面有很多新数据。这意味着我不能仅仅启动 colocated 服务器上已有的映像。我必须同步我家服务器上的更改。
这里的真正技巧是我不能只是热同步服务器。首先,磁盘会一直变化,其次,我不想冒险让同一台服务器在两个不同的物理主机上以奇怪的状态运行。这意味着同步实际的磁盘映像。问题是,虽然 45GB 的磁盘映像通过我的 12Mb 下载速度相对较快地同步到我家(加上服务器当时已经宕机,所以停机时间不是考虑因素),但使用我的 1Mb 上传速度同步相同的数据将需要很长时间——对于纯冷同步来说太长了,因为我无法承受那么长的停机时间。
这里的解决方案将是双重的,它基于我可以做出的一些假设
-
虽然我的本地 VM 实例上更改了相当多的文件,但实际的更改大小与磁盘映像的大小相比相对较小。
-
rsync 有一种出色的机制,可以只同步大型文件中已更改的部分。
-
我的 qcow2 文件中的许多更改可能无论如何都会在这些文件的末尾。
-
如果我使用带有
--inplace
选项的 rsync,它将直接修改远程机器上的现有磁盘映像,并节省磁盘空间和时间。
所以我的第一阶段计划是从物理主机到物理主机运行 rsync,并在 VM 运行时热同步 qcow2 磁盘映像,并告诉 rsync 就地同步磁盘映像。因为我可以假设远程映像无论如何都会在某种程度上损坏(这是在磁盘正在使用时同步磁盘映像的缺点),所以我不需要关心 --inplace
是否会留下一个可能损坏的文件,如果它在中途停止同步。我可以在稍后清理它。
执行第一阶段热 rsync 的优势在于,我可以在服务器仍然在家运行时,整理出家庭和 colocated 映像之间的所有主要差异。我甚至可以潜在地在第二阶段之前多次运行该 rsync,以确保它尽可能最新。以下是我用于执行第一阶段热同步的 rsync 命令
$ rsync -avz --progress --inplace disk0.qcow2
↪remotehost:/var/lib/libvirt/images/www.example.net/disk0.qcow2
$ rsync -avz --progress --inplace disk1.qcow2
↪remotehost:/var/lib/libvirt/images/www.example.net/disk1.qcow2
在 rsync 只同步已更改的位以及我使用 -z
压缩数据然后再传输的事实之间,我能够比你想象的快得多地在 1Mb 连接上同步这些文件。当然,这些命令最终使我家里的带宽饱和,所以由于我没有完成热同步的时间压力,我最终为较大的 disk1.qcow2 映像设置了每秒 10 千字节的带宽限制
$ rsync -avz --progress --inplace --bwlimit=10 disk1.qcow2
↪remotehost:/var/lib/libvirt/images/www.example.net/disk1.qcow2
一旦第一阶段完成,我就可以开始第二阶段。我需要第二阶段 rsync 在 VM 关闭电源时运行,这样我可以确保在同步期间磁盘没有被写入。否则,我将冒着文件系统损坏的风险。由于这需要停机时间,我为我的服务器选择了适当的维护窗口,那时服务器会比较空闲,在几个小时前完成了最后的阶段 1 热同步,然后在执行最终同步之前干净地停止了 VM
$ rsync -avz --progress --inplace disk0.qcow2
↪remotehost:/var/lib/libvirt/images/www.example.net/disk0.qcow2
$ rsync -avz --progress --inplace disk1.qcow2
↪remotehost:/var/lib/libvirt/images/www.example.net/disk1.qcow2
由于之前同步磁盘映像的工作,最终的冷同步只花了一两个小时,大部分时间都花在 rsync 在本地和远程映像之间寻道以确认它们同步上。一旦命令完成,我就可以再次在我的 colocated 主机上启动服务器,将其 IP 更改回原样,我就又恢复正常运行了。
数据图 来自 Shutterstock.com。