云计算帮助用户降低成本的方法有很多,不过有一点可能被很多人忽略了,那就是不停机实时迁移(Live Migration)。
延伸阅读,点击链接了解 Akamai Cloud Computing
实时迁移如何帮助我们降低成本?那花样可就多了:
- 资源动态调整:借助实时迁移功能,在不中断服务的情况下将工作负载从一个实例迁移到另一个实例,从而根据需求动态调整资源。例如,在低峰期间,可以将工作负载从高性能实例迁移到低成本实例,以节省成本;在高峰期间则可以迁移回高性能实例以应对需求。
- 故障恢复和容灾:实时迁移功能可以帮我们快速将工作负载迁移到备用实例,以应对主机故障或区域性故障。通过实时迁移,可以确保服务的持续可用性,减少停机时间和业务影响,同时降低成本,因为备用实例通常会配置为低成本的冷备份。
- 定期维护:云服务提供商会定期进行基础设施维护和更新,这可能会导致一些短暂的服务中断。通过实时迁移功能,我们可以在维护期间将工作负载迁移到其他实例,以确保服务的连续性,并避免因维护活动而导致的潜在损失。
- 按需资源使用:实时迁移功能使我们能更灵活地管理资源使用情况,根据需要随时调整实例规格和数量。根据实际需求动态调整资源,可以避免过度配置实例,从而降低不必要的成本。
总的来说,通过云实例的实时迁移功能,用户可以更灵活地管理和优化资源使用,根据需求动态调整实例配置,从而降低成本并提高服务的可用性和性能。
那么正在使用Linode云服务的你,是否考虑过:这种听起来很方便的实时迁移功能,从底层的技术上来看,到底是怎么实现的?本文我们将深入了解这项技术背后的细节。
实时迁移的工作原理
在开发这项功能时,我们的第一步是调查QEMU如何处理实时迁移。QEMU是Linode使用的一种虚拟化技术,而实时迁移也是QEUM的一项功能。因此我们的重点是将这项技术引入Linode,而非重新发明一个类似的技术。
那么实时迁移技术到底是如何以QEMU的方式实现的?整个过程分为以下四步:
- 启动目标QEUM实例,该实例的各项参数与需要迁移的源QEUM实例完全相同。
- 对磁盘进行实时迁移。数据传输过程中,对磁盘内容进行的任何更改也会提交至目标磁盘。
- 对内存数据进行实时迁移。迁移过程中,内存内容的任何变化也会提交至目标内存。如果这一过程中磁盘内容也出现了变化,相关变化同样会被提交至目标QEUM实例的磁盘中。
- 执行割接点。当QEMU确认有足够多的内存页可以放心进行割接后,源和目标QEMU实例将会暂停。QEMU会复制最后几页内存数据和机器状态,机器状态包括CPU缓存和下一条CPU指令。随后,QEMU会让目标开始运行,这样目标实例就可以从源实例停止时的状态恢复运行了。
这些步骤概括介绍了QEMU实时迁移的执行过程。然而依然需要通过包含很多手工操作的方式来精确指定目标QEMU实例的启动方式。此外,上述过程中的每个操作都必须在正确的时间执行。
Linode实现实时迁移的方式
在分析过QEMU已经实现的技术后,我们该考虑具体用怎样的方式将其实施到Linode平台上。
在实时迁移工作流程的第1步,需要启动目标QEMU实例以接受传入的实施迁移连接。在实现这一步时,我们最初的想法是拿到当前Linode实例的配置文件,随后将其应用到目标计算机。理论上这应该很简单,但进一步思考就会发现,实际情况要复杂很多。尤其是,配置文件虽然可以告诉我们Linode实例是如何启动的,但并不一定可以完整描述启动后的Linode实例的完整状态。例如,用户可以在Linode实例启动完毕后通过热插拔的方式连接块存储设备,但这种情况并不会记录到配置文件中。
为了在目标主机上创建QEMU实例,必须对当前运行的QEMU实例进行剖析。我们通过检查QMP接口的方式对运行中的QEMU实例进行剖析,该接口为我们提供了与QEMU实例布局情况有关的丰富信息,但它无法帮助我们从来宾系统的视角了解实例内部正在发生的事情。例如,对于本地SSD和块存储,它只能告诉我们磁盘链接到哪里,以及虚拟磁盘连接到哪个虚拟化PCI插槽上。在查询QMP以及检查并分析了QEMU接口后,可以构建一个Profile来描述如何在目标位置创建一个完全相同的实例。
在目标计算机上,我们将收到完整的描述信息,借此了解源实例到底是什么样,随后就可以在目标位置忠实重建这个实例,但此时还有一个差异。这个差别主要在于,目标QEMU实例在启动时使用了一个选项,该选项可以让QEMU接受传入的迁移。
至此,实时迁移的记录过程已经基本结束,接下来需要看看QEMU是如何实现这些操作的。QEMU进程树由一个控制进程和多个工作进程组成,其中一个工作进程负责返回QMP调用或处理实时迁移等任务,其他进程需要一对一映射至来宾CPU。来宾环境与QEMU端的功能相互隔离,具体行为类似于独立的系统。
从这个意义来看,我们需要处理三层内容:
- 第1层是管理层;
- 第2层是QEMU进程的一部分,负责处理所有操作;
- 第3层是实际的来宾层,负责与Linode用户进行交互。
目标实例启动并准备好接受传入的迁移后,目标硬件会告知源硬件开始发送数据。源端会在收到这个信号后开始进行处理,并会在软件中告知QEMU开始传输磁盘内容。软件会自主监控磁盘传输进度,借此检查传输操作是否完成,并会在磁盘传输完成后自动开始迁移内存内容。此时软件依然会自主监控内存迁移进度,并在内存迁移完毕后自动切换至割接模式。上述全过程都是通过Linode的40Gbps网络进行的,因此网络方面的操作都可以快速完成。
割接:关键环节
割接操作是实时迁移过程中最重要的一环,只有理解了它,才能完全理解实时迁移操作。
在割接点状态下,QEMU已经确认做好了所有准备,可以进行割接并在目标计算机上运行。源QEMU实例会让两端暂停运行,这意味着:
- 来宾系统被“时停”。如果来宾系统正在运行时间同步服务(如NTP),NTP会在迁移完成后自动重新同步时间。这是因为系统时钟会产生几秒钟的落后。
- 网络请求停止。如果网络请求是TCP请求(如SSH或HTTP),基本上不会产生可感知的连接中断;如果网络请求是UDP请求(如流媒体视频),可能会导致少量丢帧。
由于时间和网络请求均已停止,我们希望割接能尽量快速完成。然而为保证成功割接,还需要进行一些检查:
- 确保实时迁移顺利完成不出错。如果出错则要进行回滚,解除源Linode实例的暂停状态,不再进一步执行其他操作。开发过程中,我们在这方面进行了大量实验并解决了很多错误,虽然这为我们造成了很多头疼的问题,但最终都顺利解决了。
- 确保关闭源实例的网络,并在目标实例上正确连接。
- 让我们的其余基础设施清楚得知迁移后的Linode实例是通过哪台物理计算机运行的。
由于割接过程时间有限,我们希望能尽快完成上述操作。解决了这些问题后,即可继续进行割接了。源Linode实例会自动会自动收到“割接完成”信号并让目标实例运行起来。目标Linode实例会从源实例暂停时的状态恢复运行。源和目标实例上的其余内容则会被清理。如果目标Linode实例在未来某个时间需要再次进行实时迁移,则会重复执行上述步骤。
CPU标记
在向来宾操作系统呈现CPU方面,QEMU有不同的选项。其中一个选项可将主机CPU的型号和功能(即CPU标记)直接传递给来宾系统。通过使用该选项,来宾即可不受约束地使用KVM虚拟化系统所支持的全部能力。当Linode首次采用KVM时(当时还没有实时迁移功能),为了实现最大化性能,我们就使用了该选项。然而在开发实时迁移功能的过程中,该选项为我们造成了很多挑战。
在实时迁移的测试环境中,源和目标主机是两台完全相同的计算机。但在现实世界中,我们的硬件集群并非100%完全相同的,计算机之间的某些配置差异可能导致产生不同的CPU标记。这很重要,因为当一个程序被载入Linode的操作系统后,Linode会向该程序呈现CPU标记,为了充分利用这些标记,程序可以将软件中的特定部分载入内存。如果一个Linode实例被实时迁移到不支持该CPU标记的目标计算机,程序将会崩溃。这可能导致来宾操作系统崩溃,甚至导致Linode重启动。
因此在实现实时迁移时,我们必须设法防止程序因为CPU标记的不匹配而崩溃。可行的选项有两个:
让QEMU模拟CPU标记。但这可能导致原本快速运行的软件运行速度变慢,并且完全无法调查原因。
收集源计算机的CPU标记列表,确保目标计算机具备完全相同的标记,随后再进行迁移。这种方式更复杂,但不会影响用户程序的运行速度。我们最终选择了这种方式。
在决定对源和目标的CPU标记进行匹配后,我们使用下列两种方法的组合最终实现了目标:
- 第一种方法更简单。将源硬件的所有CPU标记发送给目标硬件,当目标硬件设置新的QEMU实例时,会通过检查来确保自己至少拥有和源Linode实例相同的标记。如果不匹配,将不进行实时迁移。
- 第二种方法更复杂,但可以避免因为CPU标记不匹配导致的迁移失败。在发起实施迁移前,我们会对具备可兼容CPU标记的硬件创建一个列表,随后从该列表中选择硬件来创建目标计算机。
第二种方法必须能快速执行,并且让我们的工作变得更复杂。某些情况下,我们需要针对超过900台计算机检查最多226个CPU 标记。为所有这226个CPU标记编写检查代码本就很困难,而这些代码还需要不断进行维护。但Linode的创始人Chris Aker提出的一个惊人想法最终解决了这个问题。
方法的关键在于为所有CPU标记创建一个列表,并将其表示为一个二进制字符串。随后,可以使用Bitwise and(“按位与”)运算来对比字符串。
对于实时迁移,CPU标记完整列表会表示为一个二进制字符串,其中每一位都代表一个标记。如果一个位为“0”,代表对应的标记不存在;如果某个位为“1”,则代表标记存在。例如,一个位可以代表AES标记,另一个位可以代表MMX标记。这些标记在二进制字符串中的位置会维护并记录在案,随后用于我们数据中心内的所有计算机。
相比维护一组if语句来检查某个CPU标记是否存在,这种列表的维护工作无疑更简单也更高效。例如,假设总共需要追踪并检查7个CPU标记,这些标记可以存储在一个8位数字中(多出的一位供未来进行扩展)。例如这样的字符串可能类似于00111011,最右侧的一位代表AES已启用,右数第二位代表MMX已启用,右数第三位代表其他标记已启用,以此类推。
实时迁移操作会在源和目标计算机上针对CPU标记字符串执行“按位与”操作。如果两个计算机的CPU标记字符串运算结果相等,意味着目标计算机是兼容的。我们的内部工具可以使用上述算法得到的结果为可兼容的硬件构建一个列表。该列表会展示给我们的客户支持和硬件运维团队,这些团队可以使用我们的内部工具来编排不同的运维任务。
紧跟技术变化的步伐
随着时间推移,Linode会增加新的功能,我们也许要继续努力保证实时迁移可以兼容这些功能。引入某些新功能时,可能无需围绕实时迁移执行新的开发工作,但我们可能依然需要测试该功能是否可以按照预期正常工作。对于某些功能,则可能需要在开发的早期阶段,针对实时迁移进行必要的兼容性测试和相关工作。
和其他几乎所有软件类似,对于同一件事,通过不断研究,总能发现更好的实现方法。例如,从长远来看,为实时迁移功能开发更多模块化的集成方法,无疑可以降低维护负担。或者我们甚至可能将实时迁移的相关功能纳入到底层代码中,从而使其成为Linode一项拆箱即用的功能。
—————————————————————————————————————————————————
如您所在的企业也在考虑采购云服务或进行云迁移,
点击链接了解Akamai Linode的解决方案