Fault-Tolerant Virtual Machine 论文笔记

Fault-Tolerant Virtual Machine

思维导图

Fault-Tolerant Virtual Machine 论文笔记

需求

实现一个容错的虚拟机,要求在虚拟机出问题的时候,可以有备份虚拟机上线,并且外界感受不到异常。

整体结构

primary/backup 策略有两种:

将 primary所有的状态,包括处理器、内存、IO 全部发送给backup;这种策略的困难在于,需要的带宽资源非常多,因为需要完全复制内存,内存上的数据都要发到 backup 上。

将主机视为确定性状态机,对于一个状态机,只有输入才能改变状态,没有输入状态是不会改变的。因此这种策略主要同步两台主机的输入,primary 和 backup 在同一个初始状态开始,让 primary 和 backup 获得相同的输入,这样两台主机就可以保持状态一致;这种策略的困难在于,处理同一个输入需要进行的操作具有不确定性,比如获取时钟,在 primary 上执行是一个结果,在 backup 上执行是另一个结果,这会导致两个主机的状态不一致。

结构图:

Fault-Tolerant Virtual Machine 论文笔记

结构设计

运行两台主机,各自运行虚拟机,保持两个虚拟机的状态同步,当 Primary 故障了, Backup 接手。Primary 将所有的输入和其他必要的信息通过 Logging channel 发送给 Backup,Backup 接收这些输入,执行和 Primary 一样的操作,Backup 上的输出会被丢弃掉。Primay 和 Backup 共享硬盘,但是 Backup 并不从硬盘获取输入,而是从 channel 获取输入。

实现细节

为了应对故障,我们需要检测故障,也需要为故障做好准备,我们需要让 Backup 保持和 Primary 一致,并在 Backup 接手 Primary 的时候,外界感受不到任何异常。

1, 如何检测故障?

检测方法:VMware FT 在主机之间发送 UDP 心跳包来检测是否故障,同时监视 Logging channel 是否出现异常。

Brain-Split Problem:Primary 和 Backup 之间如果只是断网了,按照上面描述的故障检测方式,Primary 和 Backup 都会认为对方出现了故障。两台主机都想要上线,这时的处理方法是使用 TEST-AND-SET 原子操作,操作共享硬盘上的一个值,谁 SET 了谁就上线。

故障处理:Primary 故障,Backup 需要执行完所有 Logging Channel 里的操作,然后 Backup 提升为新的 Primary,需要向局域网发送自己的 MAC 地址,这样交换机会将外界的输入发给新的 Primary。Backup 故障,Primary 切换到正常模式,不在发送 log entry。不管哪种故障,最后,Primary 需要启动新的 Backup。

2, 如何保持一致?(Determinstic Replay)

对于状态机,输入相同的字符串,最终将到达相同的状态。将主机视为状态机,要保持状态一致,那么就需要保证两台主机得到的输入是一样的。不过,计算机并不是那么理想的模型,计算机执行的时候,会有一些不确定的事件,比如中断;也会有不确定的的操作,比如读取时间。因此,Primary 不仅需要给 Backup 发送所有的输入,还需要发送必要的信息用以处理不确定的事件和操作。

对于不确定的事件,需要记录指令发生的准确位置,在 Replay 的过程中,这些事件将会在同样的位置触发。比如 IO 中断在 Primary 某个指令之后触发了,那么这个 IO 中断事件在 Replay 的时候,需要在同样的位置去触发。

对于不确定的操作,需要记录足够的信息来保证状态转移的一致。比如 Primary 获取了系统的时间,那么可以将这个时间发送给 Backup,保证 Backup 获得了同样的时间。

其实这个 Determinstic Replay 是很困难的事情。借助于虚拟机可以记录下 Primary 执行的过程,保证 Backup 也执行一样的过程,论文只是简单地提及了需要考虑哪些问题。

3, 如何让外界感受不到异常?FT Protocol!

保证系统的输出是一致的,这样外界就感受不到异常了。使用 FT Protocol 来处理 VMware FT 的输出事件,保证输出的一致性。

如果不用 VMware FT,在 Primary 需要输出的时候就输出,会发生什么呢?这个问题在讲义中提到了,里面举了个例子。假如 Primary 和 Backup 在开始的时候,变量 a 的取值为10。客户端让 a 自增,这条自增的请求将发送给 Primary。此时 Primary 执行了自增,并且将结果 11 输出给客户端,同时虚拟机需要将输入和必要的信息发送给 Backup。问题出在了这里:如果 Primary 回复了客户端请求,客户端看到了变量 a 的取值已经自增为 11,在 Primary 发送 log entry 给 Backup 之前,Primary 奔溃了。这时,Backup 接手 Primary,当客户端再次发送自增请求的时候,客户端期望得到 12,但是 Backup 将会返回 11,因为Backup 少执行了一条自增指令。这样,外界就感受到了异常,所以需要引入VMware FT来保证输出的一致性。

输出规则:在 Primary 想要输出之前,需要将会产生输出的 log entry 发送给 Backup,在 Primary 得到了 Backup 的回复之后,Primary 才能输出。

继续上面的例子。

在 Primary 没得到回复前奔溃了,Primary 不会回复客户端,Backup 是否在接手后回复客户端,取决于 Backup 是否接收到了 Primary 的 log entry。没接收到,客户端超时重发请求;接收到了, Backup 上线后回复客户端。

在 Primary 得到回复之后奔溃了,Backup 会接手 Primary 并回复客户端。Backup 不能确定 Primary 奔溃的准确时间点,不知道 Primary 究竟回复了客户端没有。因为有 TCP,所以 Backup 尽管回复客户端,最多客户端 TCP 认为这是一个重传的包,将它丢弃掉即可。

总结, FT Protocol 保证了在输出之前的全部操作可以得到重做。客户端得到系统的输出,可以认为状态发生了转移,FT Protocol 就是通过保证输出之后,两个主机的状态可以到达一致,虽然 Backup 有一定的延迟,但最终还是一致的。

4, 启动 Backup VMware FT

Vmotion 是 VMware 中用于迁移虚拟机的功能,这里修改 VMotion,暂停 Primary,将暂停下来这一刻的状态复制到 Backup 上。

5, Logging Channel 的管理

Fault-Tolerant Virtual Machine 论文笔记

由虚拟机的 hypervisor 来维护 Logging Channel,hypervisor 在主机上放一个缓冲区,用于发送和接收 log entry。对于 Primary,缓冲区满了,Primary 需要停止执行;对于 Backup,缓冲区空了,Backup 需要停止执行。Primary 缓冲区被填满一般是因为 Backup 的执行速度太慢了,这种时候需要调整 Primary 的执行速度。Backup 停止执行,问题不大,等到有新的 log entry 后,继续执行。
当 Primary 故障了,Backup 上线之前需要执行所有的 log entry,Primary 和 Backup之间 execution lag 越长,Backup 上线需要的时间越长。为了将 lag 保持在一个范围中,通过调整 Primary 的运行速度来保持 lag 在合理的范围。当 lag 大于一个阈值,就降低 Primary 的运行速度;当 lag 小于一个阈值,就增加 Primary 的运行速度。这是一个 lag 和运行速度的动态平衡。lag 如何计算呢?在 FT Protocol 中,让发送和回复携带额外的信息用于计算 Primary 和 Backup 之间的 lag。

6, VMware FT 上的操作

关机,Primary 关机了,要求 Backup 也关机,不要尝试上线;

资源管理,Primary 的虚拟机提高了 CPU 频率,Backup 也要应用同样的资源管理操作;

VMotion,主机可以使用 VMotion 将状态复制到另外的主机。要求 VMotion 不会将内容复制到正在运行的主机上,因为这样并不能容错。VMotion 在运行前要求主机暂停所有的 IO 操作,对于 Primary 他只需要暂停所有 IO 操作即可。对于 Backup 通过 Logging Channel 向 Primary 发送请求,要求暂停所有 IO 操作。

7, Disk IO

IO 会有几个问题:

硬盘上的 IO 操作是异步的,多个同时发生的 IO 操作给系统带来了不确定性。解决方法是,对于有竞争的硬盘操作,强制为顺序执行,Backup 上也按照同样的顺序执行。

内存在异步读取硬盘,于此同时,CPU 在操作那块内存。第一个问题是由不同硬盘 IO 带来的问题,第二个问题是由硬盘 IO 和访问内存带来的问题。解决方法:一,给硬盘读取的那段内存加保护,当 VM 要读取的时候,暂停 VM,直到硬盘读取完毕,在 MMU 中改变页的保护;二,bounce buffer,硬盘读写要先经过一块缓存。硬盘读,读到缓存再复制到内存上;硬盘写,写到缓存,再写入硬盘。

IO 进行时,Primary 炸了,Backup 不知道是否发出了 IO 请求。Backup 收不到硬盘的 IO 请求回复, 也不知道是否完成了 IO 操作,是否 IO 报错了。解决办法:如果 log 中没有 IO 已完成的中断,那么Backup 重做 IO 操作,因为它是幂等的,重新再做一次不影响结果。

8, 网络

VMware 对虚拟机的网络做出了优化,hypervisor 会异步地更新虚拟机的网络设备状态,比如异步更新网络设备的接收缓存。我们需要保证这些更新在 Primary 和 Backup 上发生的时间是一致的,否则就出现了不一致。

解决办法:关闭异步更新,hypervisor 中记录并更新网络设备的变化,将这些变化应用到 Backup 上。

优化办法:一,减少 trap 和 interrupt,虚拟机发送的数据积累到了一定的量,hypervisor 就 trap 一次,将这些数据发送出去,顺便把接收的数据传给虚拟机。简单来说,一堆包一个中断,而不是一个包一个中断。二,减少传包的延时,TCP 数据一到即开始处理,省去了 context 切换的开销。

Alternatives

1, 独立硬盘

Fault-Tolerant Virtual Machine 论文笔记

两台主机距离远,共享硬盘的方案贵,两台主机不能都访问到共享硬盘,这些原因促使人们选择使用独立硬盘。开始的时候,保持硬盘的一致,在 Backup 上执行的硬盘写入操作都要真正的去执行。

这带来一些问题,比如VMotion需要保持硬盘的同步,这是非常耗时的;比如不能用共享硬盘,无法解决 brain-split problem,可以考虑使用一个共同能访问的服务器来替代。

2, Backup 读取硬盘

Backup 的所有输入都通过 Logging Channel 获得的,包括硬盘读取的数据。为了减少网络开销,可以考虑使用 Backup 读取硬盘的方案。
这带来了几个问题:一,减慢了 Backup 虚拟机执行的速度。二,读取硬盘失败需要处理。Backup 失败,需要不断重试直到成功;Primary 失败,需要将读取失败的内容发给 Backup,保持内容的一致,鬼知道 Primary 读取了什么内容。三,Primary 先读后写同一个区域。Backup 接收到 log entry,可能读取的内容是 Primary 后来写入的内容,这导致了不一致。因此要将 Primary 的写操作延后,直到 Backup 的读操作完成。

Q&A

Q: 共享硬盘的 vm-ft,Primary 先读取后写入硬盘同一个位置,Backup会读取到什么样的内容?如何保证和 Primary 是一致的?

A: 看 Alternatives 这一节,如果在 Backup 是通过读取硬盘来获取输入的,那么会出现这个问题,需要将输出操作延后才行;论文中没有提及这个问题,我猜处理方法可以这样:在 FT Protocol 中进行处理,在 Backup 接收到写入磁盘的操作之后,需要等到之前所有的读取磁盘操作执行完毕之后,才回复 Primary ACK,Primary 收到回复之后才会进行输出。

不过这是 Alternatives,正常来说,所有的输入是通过 Logging Channel 来发送到 Backup 的,Backup 不接收任何输入。

Q: 讲义提到,在 Backup 上线之前,Replay 过程中的所有输出都会被丢弃。这不就发生了一个问题吗?假如 Primary 在接收到 Backup 的回复后,给客户端发送回复前故障了。这时 Backup 上线了,Replay 的输出被丢弃,客户端就得不到结果。客户端认为系统的状态是没改变的,而 Backup 的状态确实已经发生了改变。因此,我这里还是觉得 Backup 接手之后,Replay 的输出是不会丢弃的,TCP 会认为是重复的包,硬盘重复写是幂等的。

{{uploading-image-265260.png(uploading...)}}

上一篇:mysql数据库开发重点知识总结


下一篇:Mongodb备份恢复到任意时间点