实时 KVM

jopen 9年前

实时虚拟化听起来有点矛盾,但是它确实是有用的(在某些条件下),并且为 Linux 内核的灵活性又提供了一个强有力的证明。KVM2015 论坛的前两个演讲就详细的讨论了实时虚拟化。第一个演讲者是 Rik van Riel,他讲解了实时虚拟化的内核部分的工作(油Tube视频幻灯片)。而第二个演讲者 Jan Kiszka 则解释了如何配置主机以及如何管理实时虚拟机(油Tube视频幻灯片)。我们这篇文章就采取了他们两人的意见,首先是 Van Riel 的想法。

PREEMPT_RT 内核

实时的重点是准确性,精确性,而不是速度。实时工作是那些没有及时处理工作就会致命的工作,例如电信设备里的坏音,股票交易中的机会错失,航空机械中的火箭爆炸。这些应用的特点是在一秒钟可能会有上千个这样的关键点,他们允许的最慢响应时间可能小到几毫秒,并且 99.999% 的这些关键点工作都要被即时处理,如果没有被及时处理的话... (想想上面的火箭爆炸吧),总之要全部及时的处理好。速度是重要的,但是要一直保证这种低延迟会带来另一个问题,那就是产出下降。

几乎每一个潜在的系统因素都来自于内核。比如,某个驱动程序可能会关闭中断而阻断高优先级的程序的调度。非实时内核中的自旋锁也是另外一个潜在的原因,因为 linux 在持有自旋锁的同时不能进行 schedule() 调度。这些问题可以通过运行 PREEMPT_RT(实时内核补丁集)构建的内核控制。除了临界区代码,一个 PREEMPT_RT 内核致力于使 linux 的每一部分都是可抢占的。

大多数的修改要求已经被并入 linus 的内核结构中:抢占式内核支持,优先级继承,高分辨率定时器,线程中断处置支持,自旋锁元注解和 NO_HZ_FULL 模式。虽然 PREEMPT_RT 补丁很大,但和过去相比已经优化了很多。目前应该解决的主要三件事是:把 non-raw 自旋锁转为优先级继承互斥锁,把中断处理真正放在线程中运行以便实时任务可以抢占他们,和支持抢占的 RCU(Read-Copy Update,读取-复制更新)实现。

遗留的主要问题在固件当中。x86的系统管理中断(SMIs)关心诸如风扇转速等事情。SMIs 不能够被操作系统阻塞,极端情况下会消耗数毫秒的时间。在此期间,操作系统完全被阻塞,除了购买良好的硬件之外,别无它法。内核模块 hwlatdetect 可以用来检测该问题,其阻塞一个 CPU 上的中断,寻找异常的延时峰值,并用特殊模块寄存器(MSRs)将该峰值关联到 SMIs 之上。

实时虚拟化,真的吗?

当前,实时虚拟化听起来可能难以置信,但确实可以。当然,仍有诸多问题存在:例如,虚拟机(VM)中任务的优先级和客户机中锁的持有者在主机中均不可见。这限制了调度器的灵活性,并阻止了优先级集成。因此,所有虚拟 CPU(VCPU)被置于非常高的优先级。仅仅内核软中断有更高的优先级,因此其向虚拟 CPU 传递中断指令。为了避免让主机处于饥饿状态,系统必须在运行系统任务的 CPU 和运行实时客户机的 CPU 间分区(使用 ioslcpus 和 nohz_fulkernel 命令行参数进行标记)。客户机必须以相同的方式在 VCPU 和运行普通任务的 CPU 间进行分区。后者可能会偶发性的退出到主机的用户态,这可能会比较长(非常像裸机状态的SMI),并且阻止客户机的调度。

因此,虚拟化的实时客户机比在裸机上的同样工作负载要使用更多的资源,而且这些资源必须专用于特定客户机。但是,这是可以接受的代价,以支持虚拟化提供的改善的隔离性,可管理性和硬件兼容性。此外,最近的每一代处理器在一个 CPU 插槽里有越来越多的内核可用;摩尔定律看上去在弥补这个问题,至少目前是这样。

一旦实时 KVM 设计如上实现出来,剩下的部分就是修复 bug。许多修复要么是针对 KVM 的,要么是针对 PREEMPT_RT 的,所以它们将有利于所有的实时用户和所有虚拟化用户。例如,RCU 被改为有客户机运行时的扩展静默状态。扩展了 NOHZ_FULL 的支持,当运行 SCHED_FIFO(实时)任务时来完全禁用定时器时钟。在这种情况下,由于更高级别的任务已经抢先,此任务不会被重新调度,所以并不需要计时器时钟。加入了一些设置点来禁用能引入延迟(比如从宿主机到客户机的时间同步)的不必要 KVM 功能;这会占用几微秒时间,解决方法就是简单地在客户机运行 ntpd。

虚拟化的开支可以通过使用 PREEMPT_RT 的"简单等待队列"而不是全功能的 Linux 等待队列加以限制。这只会占用有限时间的锁,所以操作的长度同样是有限的(中断处理程序经常需要唤醒,所以它们的消耗会直接影响延迟)。将简单等待队列合并到主流内核的事正在被讨论

另一个技巧就是稍微提前一点调度 KVM 的定时器,这样就可以抵消注入虚拟中断时的消耗。虚拟层将中断传递给客户机需要几个微秒,KVM 核心模块中有一个参数允许基于客户机测得的延迟进行微调。

最后,新的处理器技术也有一些帮助。下面的案例是 Intel 的"CacheAllocation Technology" (CAT),这在一些 Haswell CPU 上可以使用。从 DRAM 和 TLB 中加载数据未命中结合起来的消耗可以导致一个单点未缓存环境,这将导致联合延迟高达 50 微秒。CAT 允许对指定的应用保留部分缓存,以防止一个工作负载将另一个工作负载驱逐出缓存,而且它使用一个基于控制组的接口完美进行控制。但是,这个补丁还没有纳入 Linux 中。

使用反复测试得出的结果出乎意料的好。纯物理延迟小于 2 微秒,尽管 KVM 测量的结果为 6 毫秒,但任然是一个很好的结果。为了达到这些数字,系统需要仔细地设置以避免所有高延迟的系统操作:没有 CPU 变频,没有 CPU 热插拔,不进行内核模块加载或卸载,同时也没有 swapping。除非实时辅助程序外,应用也进行了调整,以避免使用慢速的设备(如:硬盘或音响设备)。所以,部署实时 KVM 需要对系统和工作负载有深入的了解(例如,确保时间戳计数器的稳定,使系统不会回退到其它的时钟源)。随着人们更多地使用实时 KVM,一些新的瓶颈将会被发现,但是内核方面的工作大体上进行良好。

"我可以在我的云上使用它吗?"

在这一点上,Van Riel 将舞台留给了 Kiszka,Kiszka 将会对主机配置进行更多地讨论,包括怎样自动化,怎样使用 libvirt 和 OpenStack 管理系统。

Kiszka 是一个长期的 KVM 贡献者,他供职于西门子。在许多年前他开始使用 KVM,并解决了硬件兼容性问题这是一个遗留的软件问题 [PDF]。他已经研究实时KVM [油Tube] 许多年,人们现在会问:“我可以把它部署在我的云上么?”

答案是“可以”,但是也有一些限制。当然这不是公有云。为生产做实时控制不是很顺利,那是因为你需要从一些数据中心做 I/O 很远。“云”这里指的是私有云,里面的虚拟机和进程是通过快速以太网连接的。云环境下还有许多特性,因为他们不提供确定的延迟。举例来说,实时路径不能使用磁盘或动态迁移,但是这通常不是个问题。

Van Riel 解释说,基本的配置已经不满足需要,首先要看的就是网络。许多 QEUM 仍然是通过“大 QEMU 锁(big QEMU lock)”来保证的,设备透传已经有延迟问题。不过在一些方面正取得进展,比如,它已经可以能让准虚拟化设备(virtio-net)和 non-QEMU 后端在一起使用。

KVM 提供了两个这样 virtio-net 的后端,vhost-net 和 vhost-user。Vhost-net 位于内核,在虚拟机上,它从 Linux 网络栈的 virtio-net 设备上连出一个 TAP 设备。他们都不接受延迟。Vhost-user 与之相反,允许任何用户空间进程提供网络,并可以与专业的网络库一起使用。

以具有实时能力网络库包括数据平台开发套件(DPDK)或者 SnabbSwitch 为例。这些栈是替代轮询策略的选择;这减少了大量的信号和事件,作为结果它也会导致延迟。Kiszka 设置使用 DPDK 作为 vhost-user 客户端;当然,其运行在一个优先级。为客户提供及时的 vcpu 中断,它必须比 VCPU 线程放置在一个更高的优先级上。

Kiszka 的应用程序没有高的包率,所以一个物理 CPU 足以运行所有网络接口系统的切换;更苛刻的应用程序可能需要为每个接口提供一个物理 CPU。

在实验室里,实时虚拟化成型后,转移它们到数据中心需要一些额外的工作。成百上千的虚拟机和大量的异构网络,它们中的一些是实时的,另外一些不是,这需要管理和灵活计费。实现这种需求需要一个云管理堆叠,诸如:OpenStack 就是一个被选择出来和可扩展的,具有实时能力的平台。它参考的架构包含(自底向上):PREEMPT_RT 内核,QEMU(在这里客户机不是实时的,这需要设置 vhost-user 开关),基于 DPDK 的开关,libvirt,和 OpenStack。每一台主机,或者说“计算节点”,被设定为独立的物理 CPU,这解释了上半部分的内容。IRQ 相关内容还需要显式地设置(通过失衡的守护进程),默认来说,这不涉及内核的 isolcpus 设置。但是,根据工作负载,可能需要调整,在任何情况下,如果有许多类似的主机,安装很容易被复制。有一个被叫做 partrt 的工具有助于建立隔离。

Libvirt 和 OpenStack

更高一层的 libvirt,不需要太多的规则,它仅仅会从高层上执行命令。在 libvirt 1.2.13 上,所有必需的可调参数是可用的:设置可调参数(规则,优先级,阻塞物理 CPU),用 mlock()查询 QEMU 所有的客户机内存(RAM),并启动虚拟机连接到 vhost-user 进程。这些参数是由 OpenStack 计算节点的 Nova 组件处理的。

Nova 已经可以被配置为虚拟 CPU(VCPU)阻塞并指定物理 CPU。其他的设置,在 OpenStack 中被错过,这在一个蓝图(a blueprint)中已被讨论。它还没有被完成(举例来说,它提供了联合无实时 CPU 到无实时 QEMU 的线程),蓝图将会使得剩余的 libvirt 功能生效。补丁正在被讨论,目标是在 OpenStack 的“Mitaka”版本发布,大概是在 2016 年的上半年左右。 Kiszka 的团队会集成补丁部署;团队将会提出扩展的补丁和蓝图。

OpenStack 是通过 Neutron 组件控制网络的。然而,实时网络趋向于特殊:他们根本不使用TCP/IP,Neutron 想要使用自己的方式管理网络。西门子(Siemens)说 Neutron 是“非托管型”网络(没有DHCP,甚至没有IP)。

总而言之,工作在更高层上的栈主要是为有实时能力的计算节点的基本设置进行标准化,许多工作是为了优化参数处理,诸如 partrt。在问答环节,如前所述,也被扩展以支持实时优化参数文件。然而, Kiszka 还计划着再看看更低的栈;最新的芯片有功能消除中断延迟,当直接分配设备给虚拟机的时候,不涉及到管理程序而直接路由中断,另外,Kiszka 过去的工作[PDF]让 QEMU 可以模拟实时设备并可以使其在未来恢复。