目标:通过主备复制手段设计一个可容错的VM,用于用户运行企业级程序。primary日常工作,一旦它宕机,和它保持lock-step的backup会立刻顶上,外界观察不到这些操作,我们制造了只有一台VM永远在正常运行的假象。
要考虑的点:
- 使用什么手段保持primary和backup严格同步
- 在虚拟化单核CPU时和多核CPU时手段有何不同
- 非确定性指令和外部中断如何处理
- primary和backup任意一个宕机之后如何处理,如何恢复到一主一备并保持同步的状态
- 脑裂问题如何处理,如何保证只有一个primary正在与外部交互
- backup顶上之后,新backup如何追上此前的全部操作
- ...
同步手段概述
- 状态迁移:将变更后的状态迁移到backup,包括CPU状态(寄存器)、内存、IO设备等
- 复制状态机:将产生变更的操作(operation)发送给backup
试想一个会操作大量内存的操作,如果使用状态迁移则要传输大量的数据,占用带宽并且难以保持同步;而复制状态机只会该操作发送给backup,节省数据传输量。
本篇论文中采用复制状态机的方式。
复制状态机及其限制
状态机是一个数学模型,它具有状态,外部的输入事件会让状态机的状态发生改变。
若有状态机A和B,它们的初始状态一致,后续状态机B以相同的顺序执行和A一样的输入事件,并且这些输入事件对状态产生的变更都是确定性的,那么B就会和A保持同步。下面两点是复制状态机的关键:
- 主备初始状态一致
- 备份以相同的顺序执行和主一样的输入事件
- 所有操作对状态的变更都是确定性的
可以把计算机看作是一个状态机,其持有的状态就是CPU寄存器、内存、缓存,而输入事件则是CPU执行的指令以及外部中断。
既然选择了复制状态机,就意味着VMWare团队必须处理所有非确定性的外部事件,包括:
- 非确定性指令:随机数、日期时间等
- 外部中断:中断由设备发出,对于计算机的执行来说是异步的,假设中断在A的第110条指令和第111条指令之间发生,那么对于B,若想与A保持同步,必须也在110条和111条之间插入这个中断
- 只支持单核:对于多核CPU的每一个内存读写都是非确定性的操作
一般来说采用复制状态机的容错系统处理非确定性操作的方式是将非确定性操作的结果连同操作一起发送给备份,但是对于多核CPU的内存读写,这相当于将内存操作的全部结果都发送,所以复制状态机的优势已经没了。
VMWare FT貌似后期支持多核CPU的时候采用了状态迁移
利用Hypervisor
VMWare FT是如何捕获primary的每一个指令执行以及外部输入的呢?
在运行的虚拟机以及实际硬件之间有一个Hypervisor
层,它对于一个VM的执行有完全的控制权,包括向VM递送所有的输入。它能够捕获所有primary上的非确定性操作的必要信息,并通过logging channel将这些信息发送给backup以在backup上重放。
举个例子——接收外部事件:
- primary的hypervisor通过外部中断接收到网卡包,它通过logging channel将其发送给backup的logging channel,并向VM层递送该中断
- backup的hypervisor也向它的VM层递送该中断
举个例子——发送外部事件:
- primary执行到了一个发送外部事件的指令,hypervisor识别到它是primary,外部事件将正常发送
- backup也执行到了这个指令,hypervisor识别到它是backup,不发送本次事件。所以不会有两个网卡包被发送到网络中
共享磁盘
上面提到backup会丢弃所有外部事件的发送,那磁盘写入怎么解决?
primary和backup会共享一个底层的网络磁盘,当primary宕机backup顶上后可以看到和primary一样的磁盘。