比容器更轻量、更安全的虚拟机


【编者的话】这篇文章介绍了一种轻量级的虚拟机,它既能够保证虚拟机的隔离性,又能够有接近Docker容器的性能。

我们是否能够拥有虚拟机的隔离性的同时,又拥有容器的高效性呢?在今天我们挑选的论文中,作者调研了基于Xen的虚拟机的性能瓶颈问题。他们在启动大量轻量级虚拟机(都是unikernels和minimal Linux虚拟机)时,发现了性能卡在何处,并且突破了它,创建了一个叫做LightVM的系统。在使用一个最小化的unikernel镜像前提下,这个系统能够在4ms内启动一个虚拟机。作为对比,fork/exec需要消耗将近1ms。在同一个系统中,Docker容器启动需要大概150ms。
01.jpeg

产生这些结果的前提是,LightVM的镜像是一个unikernel。在某些特殊情况下,你很可能只想创建一个unikernel(一个用来支持无服务功能执行< serverless function execution >的Micropython-basedunikernel就是一个有趣的案例)。论文作者还创建了一个自动化的构建系统,叫做TinyX,它能够用来创建仅运行单个应用的最小化Linux虚拟机镜像。如果我们观察TinyX虚拟机和Docker容器的启动时间,会发现两者的性能在启动250个虚拟机(容器)之前是非常接近的。但一旦过了这个点,Docker的启动时间会比TinyX虚拟机更短,这是因为即便是TinyX创建的最小的Linux发行版也会做一些额外的、临时的后台任务。
02.jpeg

随着虚拟机数量的增大,Xen的表现如何呢?如果表现不好,瓶颈又在哪里呢?

如下图所示,限制虚拟化的可扩展性和性能的最大因素就是虚拟机的大小。为了完成以下的这张图表,作者启动了一个unikernel虚拟机,重复多次,每次会将不同大小的二进制数据加入到未压缩的镜像文件中。可以看到实验结果,镜像大小就是最大影响因素。
03.jpeg

因此如果我们要加速启动,减小镜像大小是必不可少的。我们知道unikernel可以提供最小化的镜像。在这篇论文中,作者使用Mini-OS创建了一个unikernels的变种,它实现了一个TCP服务,这个服务会返回当前的时间。这是镜像在未压缩状态下只有480KB,并且只需要3.6MB的RAM。这个unikernel常被用来测试虚拟机所需要内存的最低值。

基于Mini-OS来创建自己的unikernel镜像可能比较复杂,因此作者创建了TinyX。


TinyX是一个自动的构建系统,它能够创建只允许单个应用的最小化的Linux虚拟机镜像。本质上,这个工具创建了一个虚拟机,它包含有一个最小化的Linux发行版,以及一个优化过的Linux内核。高度特殊化的unikernel具有最好的性能,但要求将应用移植到一个最小化的操作系统上,一个功能完备的普通操作系统的镜像能够支持大量的应用,但会带来性能的损耗,TinyX的出现为这两种情况提供了折中方案。
TinyX创建的内核镜像只有Debian镜像的一半大小,并且只需要非常小的运行时内存(TinyX需要1.6MB,而Debian需要8MB)。

在使用最小内存镜像的情况下,我们来继续观察启动大量虚拟机后,Xen的表现如何。当启动了1000台虚拟机,下图是在同一套硬件上,Debian、TinyX、MiniOS、Docker容器、线程创建的启动(boot)时间和创建(create)时间的对比。
04.jpeg



随着虚拟机数量的增加,创建时间急速增长(注意y轴是指数级增长的):在创建第1000个实例时,Debian、TinyX,unikernel的创建时间分别为42s、10s、700ms。
随着VM大小的减少,创建时间反而占据所有时间的更大部分。为了理解这些时间都耗费在哪里,作者监控了Xen的行为和时间,得到了以下这张图:
05.jpeg

XenStore交互和设备创建占据了绝大部分时间。其中设备创建的时间相对不变,而XenStore的负载则迅速增长。

LightVM的设计

> 我们的目的是使得虚拟机的启动时间能够接近进程启动时间。Xen从没有考虑过这方面的改进,如上一节所示,这个问题并不仅仅是不高效的代码造成的。比如说,XenStore的根本问题在于它的中心化、类文件系统的API,在虚拟机创建和启动过程中,由于这个API需要数十次的中断和特权域交叉(privilege domain crossing),导致这个API运行缓慢。

我打赌,在这个设计在最初被创建时,没有人会想象到创建1000台虚拟机。

LightVM重新设计了Xen的控制面板,使用一个轻量的驱动(叫做noxs,也就是no XenStore)来代替原有的XenStore,并且允许前端和后端驱动通过共享内存直接通信。

LightVM还维持了一个预先准备好的虚拟机shell池,通过这个shell池,所有虚拟机的通用处理部分都会在后台进行完成。当一个虚拟机创建命令被发布,一个满足该虚拟机要求的shell会从shell池中被挑选出来,因此只有最终的初始化步骤需要被完成,比如加载内核镜像到内存中,完成设备的初始化。
06.jpeg

在标准版的Xen中设备创建会以调用bash脚本来结束,这是一个非常缓慢的过程。LightVM使用了一个二进制后台程序来替换它,这个后台程序会执行一个预定义好的启动程序,这个过程不需要任何forking或者bash脚本。

性能

我们已经在文章的开头看到了LightVM在不同镜像下的启动时间。除此之外,LightVM还能够在30ms左右保存(save)一个虚拟机,并且在20ms左右还原(restore)一个虚拟机。标准版的Xen分别需要128ms和550ms。

Unikernel的内存使用非常接近于Docker镜像,TinyX则需要更多,但1000台虚拟机也只需要22GB内存。这对于当前服务的所有内存来说只是九牛一毛。

只要虚拟机只运行必要的任务,那么虚拟机的CPU占用率也能够接近容器。
07.jpeg

案例

作者提供了4个不同的使用案例。


在以下的所有场景中,使用容器能够帮助提升性能但会带来脆弱的隔离性,使用功能齐全的虚拟机则能够提供和轻量级虚拟机同样的隔离性,但会带来性能的损耗。
  • 针对移动用户的个人防火墙,它会运行在接近蜂窝基站的移动网关中(移动边缘计算——MEC)。在这个场景下,一个ClickOS的unikernel镜像被使用,8000个防火墙能够运行在一个64核的AMD机器上,并且达到10ms左右的启动时间。一个运行有LightVM的单台机器能够为一个节点内的所有用户提供个人防火墙,并且不会成为一个性能瓶颈。
  • Just-in-time服务安装(类似于JITSU)。
  • CDN中的High-density TLS termination。这个要求内容提供者的long term secret key,因此在不同内容提供商之间加强隔离性是必要。
  • 创建一个轻量级的计算服务,比如AWS Lambda。在这种情况下,我们可以使用一个基于Micropython的unikernel来运行Python脚本编写的计算任务。启动和执行这样一个任务只需要1.3ms。下图是压力测试的情况,服务的响应时间随着虚拟机数量的增加会线性增长,直到800台虚拟机。
08.jpeg



我们展示的案例表明,轻量级的虚拟化是一个真是的需求,并且我们能够同时获得高隔离性和高性能。
最近Google云平台的一篇博客“Demystifying container vs VM-based security: security in plaintext”提供了一个关于容器安全和隔离的有趣观点,有兴趣的可以看看。

论文链接:My VM is lighter (and safer) than your container Manco et al., SOSP’17

原文链接:My VM is lighter (and safer) than your container(翻译:杨润青)

===========================
译者介绍
杨润青,90后博士僧,研究方向是网络和信息安全。

0 个评论

要回复文章请先登录注册