English日本語

文章

keelOS: 家用系统SLA达到99.99%很难么?

99.99% 的可用性,意思是一年里所有停机加起来不超过 52 分钟。不管是家里或小办公室里跑着路由器、NAS 和几台虚拟机的一台主机,还是一家跑着上百台虚拟机的小企业,这个数字难的不是硬件,而是宿主系统自己的每一次更新和重启。

keelOS 的答案是:宿主重启的时候,虚拟机不关机,只暂停二十秒左右,然后从原地接着跑,TCP 连接不断。这篇文章讲它是怎么做到的,测出来的数字是多少,以及还有哪些做不到。

可用性 一年最多停机
99.9% 8.8 小时
99.99% 52.6 分钟
99.999% 5.3 分钟

停机都花在哪

常见的家用虚拟化平台,宿主更新内核或系统以后都要重启,而一重启,它上面的虚拟机就得全部关机。原因很简单:虚拟机的内存、CPU 状态、虚拟设备都住在宿主的内核里,宿主内核一退出,它们就无处可去。

一次这样的重启,用户看到的停机大致是:

  1. 虚拟机逐台关机:几十秒到几分钟,Windows 往往最慢。
  2. 宿主重启:服务器主板的自检(POST)就要一到两分钟。我们的开发机经固件重启一次是 146 秒。
  3. 虚拟机重新开机,里面的服务重新启动:又是几分钟。

加起来,每次大约 5 到 10 分钟。按每月更新一次算,一年就是 1 到 2 小时,光计划内的更新就把 99.99% 的预算花掉了两倍,还没算停电和硬件故障。这也是很多人宁可不更新的原因。小企业更头疼:上百台虚拟机分在几台主机上,每台主机的更新都要先打招呼、安排停机窗口,几十个业务跟着停。

思路:把宿主拆成两层

从一个人民群众的朴素思维考虑:你一个 hypervisor,修正一下 SSL 漏洞,为啥要我们全体虚拟机 suspend、shutdown、restart?我们仅仅用了 CPU、内存、硬盘这些设备,和你的 SSL 漏洞没关系啊。你们的锅,我们虚拟机不背!

基于这个理念,我们把 hypervisor 分离再分离,直到剩下的东西,就是虚拟机所需的。这样,root 系统做任何改动,都不再影响虚拟机的运行。

具体做法是两层。下面是 hyp:一个我们自己写的很薄的 hypervisor,从 UEFI 启动,之后一直常驻内存,简单到几乎不需要更新。上面是 root:一个完整的 FreeBSD,负责驱动、ZFS、虚拟设备(bhyve)和管理界面。

关键在于,root 自己也跑在 hyp 之上,而虚拟机的内存不属于 root,属于 hyp 的内存池。这样 root 可以像一个普通程序一样退出、换新版、重新启动,而虚拟机的内存一个字节都不用动。

keelOS 的三层:hyp、root 和虚拟机,重启的只有 root
keelOS 的三层:hyp、root 和虚拟机,重启的只有 root

日常的升级、换内核都只重启上面的 root;hyp 和硬件不动,虚拟机只是暂停一下。

root 重启时 guest 怎么活下来

在 keelOS 上执行一次 shutdown -r,或者在网页上点“切换版本”,发生的事情是这样的:

  1. 挂起:root 关机时,每台 guest 的 vCPU 停下,bhyve 等正在进行的磁盘 I/O 做完,把虚拟设备的状态(网卡队列、中断控制器、定时器……)写成一个很小的检查点。内存不写盘:它本来就在 hyp 的池里,不用拷。
  2. 交接:root 把下一个内核和内存文件系统交给 hyp,然后像平常一样重启。
  3. 截下复位:hyp 拦住这次复位,不让它到达硬件,而是自己把新的 root 装进内存、跳进去(下一节)。
  4. 恢复:新的 root 启动,导入 ZFS 池,用检查点重建每台 guest 的虚拟设备,把 hyp 池里的内存重新映射给它们,vCPU 接着跑。
  5. 补时钟:暂停期间 guest 的时钟是停住的。恢复时 vmm 把停掉的时间补上,实测 guest 时钟误差在 ±0.4 秒以内。

对 guest 来说,这就像整台机器“卡”了二三十秒。它不重启,应用不重启,打开的文件和会话都在。TCP 对二三十秒的停顿有重传兜底,连接不会断;我们在测试中一直挂着一条 TCP 连接并持续写盘,跨过几十轮 reload,数据校验全对。

检查点本身带版本号:升级后新版本读不了旧格式时,它会在建虚拟机之前就干净地拒绝,guest 保持挂起,换回能读的版本就能恢复,而不是把它弄坏。

不经过固件的重启

普通的重启要走一遍固件:内存自检、初始化每个设备、选启动盘。服务器主板光这一步就要一两分钟,而且固件会把内存清空、把设备复位,hyp 和虚拟机的内存也就没了。

keelOS 的重启不走固件,我们叫它 reload:

  • 旧 root 关机前,keelload 把新内核、模块和内存文件系统交给 hyp。
  • FreeBSD 最后一步写复位寄存器时,hyp 截下这次复位,自己把新内核摆进内存,生成和 UEFI loader 一样的启动参数(内存图、EFI 系统表、显示用的 framebuffer),然后跳进新内核。
  • 硬件没有复位,hyp 和 guest 的内存原封不动。

在我们的开发机上,shutdown -r 到 ssh 能登录,reload 是 47 到 50 秒,经过固件是 146 秒。连续 100 次 reload 没有一次失败。

升级同样走 reload:新版本写进另一个槽,切换时就是一次 reload,所以升级 keelOS 也不用让虚拟机关机。

不只是 Linux

跨 reload 活下来不靠 guest 配合:guest 里不用装代理,也不知道自己被暂停过。所以它对各种系统都一样,在开发机上测过的有:

  • FreeBSD、Debian、NetBSD:带着 TCP 连接和持续写盘跨几十轮 reload。
  • Windows 10、Windows Server 2022、Windows XP、Server 2003:和其他 guest 一起跨 reload,桌面、网络照常。
  • 直通整块 NVMe 的 NAS guest:这是最难的一种。直通设备的 DMA 映射(VT-d)由 hyp 管,不在 root 里;新的 root 启动时也不去复位这块盘。guest 一直在 fsync 写盘,跨 reload 没有一次出错。

有的 guest 更适合在 reload 时正常关机再开机(比如带 SR-IOV VF 的路由器),这是每台 guest 自己的一个设置:on_host_reload 是 suspend 还是 poweroff。

升级:两个槽,只试一次

reload 解决了“重启不关虚拟机”,升级还要解决“新版本出问题怎么办”。keelOS 的启动分区里有 A、B 两个槽:

  1. 新版本只能写进没在运行的那个槽,写完不会自动重启。
  2. 你选“现在切换”,或者定一个时间(比如凌晨 3 点)。切换就是一次 reload,虚拟机暂停二三十秒。
  3. 新槽只试一次。起来了才算确认;起不来,下一次启动自动回到原来的槽。
  4. 回滚就是再切回另一个槽。

配置和数据都不在槽里:主机设置在启动分区的单独目录,虚拟机在 ZFS 池里。新版本读到缺的配置项用默认值,读到废弃的配置项就在升级时转换或删掉,不会因为一行旧配置起不来。

实测数字

一次 root reload,虚拟机实际暂停 17 到 35 秒,取决于机器和 guest 的数量。下面都是在真机上测的(双路 Xeon 的 Supermicro 开发机、一台 16 GB 的 Supermicro,和一台联想 P340 Tiny)。

场景 结果
一台 guest,带 TCP 连接和持续写盘,FreeBSD 20 轮、Debian 35 轮 全部通过,暂停 24–26 秒,时钟误差 ±0.4 秒,数据校验全对
17 台 guest 一起(Windows、NAS 等) 全部恢复,挂起的 15 台暂停 33.9–34.9 秒
装好的 keelOS,16 GB 机器 guest 暂停 16.5 秒,root 从关机到能登录 31 秒
连续 reload 100 次无失败,每次 47–50 秒
联想 P340 Tiny(第一台非 Supermicro) 升级时 Windows 7 挂起后原样恢复,暂停 55.7 秒,慢在哪里还在查

从网络上看中断会比暂停长一些:我们开发机接的交换机口开着 STP,网卡重新连上后要等 30 秒才转发,所以测到的是 50 多秒。家用交换机一般没有这 30 秒。

还做不到的

  • hyp 自己的更新还要经固件重启一次,这时虚拟机要关机。hyp 很小、很少改,但不是永远不改。
  • root 崩溃(kernel panic)现在会带着整台机器复位,虚拟机断电。计划中的做法是:hyp 发现一次没有经过正常关机流程的复位时,先停住虚拟机,只重启 root。
  • 硬件和停电:一台机器就是一台机器。硬盘用 ZFS 镜像、接 UPS 是必须的;主板坏了、断电时间超过 UPS,谁也救不了。
  • 规模:一台主机上目前测到的是 17 台虚拟机一起跨 reload,暂停 34 秒;虚拟机越多,挂起和恢复的时间越长,单机几十台以上还没测。上百台虚拟机要分在几台主机上,多台主机的统一管理和虚拟机在主机之间迁移还在计划里。
  • 带 SR-IOV VF 的 guest 在 reload 时暂时是关机再开机;让 VF 也跨 reload 的代码写好了,还没测。
  • 已知 bug:在装好的 keelOS 上,一台已经从 reload 里恢复过的 guest,下一次 reload 时会挂不起来,被强制关机。正在查。
  • 暂停不是零:二三十秒对网页、文件共享、远程桌面是“卡了一下”,对实时语音这类应用就是一次掉线。

结论:算一笔账

把计划内的停机从“重启宿主、重启所有虚拟机”变成“虚拟机暂停半分钟”,99.99% 就不再难了:

更新频率 重启宿主(每次约 5–10 分钟) keelOS reload(每次约 30 秒)
每月一次 1–2 小时/年 6 分钟/年
每周一次 4–9 小时/年 26 分钟/年

每月更新一次,keelOS 一年只用掉 52 分钟预算里的 6 分钟,剩下的留给停电和硬件。问题从“不敢更新”变成了“有没有 UPS 和镜像盘”,这是家用用户花小钱就能解决的。

要到 99.999%(一年 5.3 分钟),一年就只能 reload 十来次,而且不能有任何意外,这就不是一台机器能做到的了。下一步是几台主机一起管,再加上热迁移(计划中):虚拟机开着就迁到另一台主机,连 hyp 更新、换内存、换主板这类硬件维护也不用让它们停。但对家里的一台虚拟化主机,或者小企业几台主机上的上百台虚拟机,安心更新、虚拟机不重启,已经是最大的差别。

← 文章 · 为什么选 keelOS