实时 KVM —— 实时虚拟化可能吗?
实时虚拟化听起来有点矛盾,但是它确实是有用的(在某些条件下),并且为 Linux 内核的灵活性又提供了一个强有力的证明。KVM2015 论坛的前两个演讲就详细的讨论了实时虚拟化。第一个演讲者是 Rik van Riel,他讲解了实时虚拟化的内核部分的工作(Youtube视频和幻灯片)。而第二个演讲者 Jan Kiszka 则解释了如何配置主机以及如何管理实时虚拟机(Youtube视频和幻灯片)。我们这篇文章就采取了他们两人的意见,首先是 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 [YouTube] 许多年,人们现在会问:“我可以把它部署在我的云上么?”
答案是“可以”,但是也有一些限制。当然这不是公有云。为生产做实时控制不是很顺利,那是因为你需要从一些数据中心做 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)。