亚马逊AWS官方博客

深度解析 AWS Firecracker 原理篇 – 虚拟化与容器运行时技术

摘要

AWS Firecracker是一款由 AWS开源的轻量级虚拟化运行环境,客户可以在 AWS 云上和本地环境中使用它。Firecracker MicroVM 同时具备传统虚拟机的安全性和工作负载隔离能力以及容器的速度和资源利用率。运行在为 Firecracker 优化过的 Amazon 裸金属 EC2 服务器上的 MicroVM是多租户容器服务的理想选择,可以为最新的微服务或无服务器架构的现代应用提供方便、敏捷的开发和部署环境。

计算机虚拟化历史

为了便于读者理解,本文在正式介绍 Firecracker 前,先回顾一下虚拟化的发展,同时按照笔者的理解对涉及到的一些专业术语做一下翻译和解释:

  1. 轻量级虚拟机(以下简称 MicroVM,它没有模拟完整的计算机硬件设备,只实现了运行现代化工作负载的必要功能)
  2. 虚拟机管理监视器(Virtual Machine Monitor以下简称VMM,配合设备模型的模拟器共同实现虚拟机 Hypervisor)
  3. 设备模型(Device Model 指需要 Hypervisor 模拟仿真的硬件设备)
  4. 无服务器(Serverless 指开发者不用再考虑服务器运行环境了,代表服务有 AWS Lambda)

在计算机科学中,虚拟化技术(Virtualization) 是一种资源管理优化技术,将计算机的各种物理资源(如:CPU、内存、磁盘网卡等 I/O 设备)进行抽象、转换,呈现出一个可以供分割和组合的一个或多个虚拟机计算机环境。早在1974年,Gerald J. Popek 在论文中定义了虚拟化原型和准则。

而 X86平台虚拟化的转折点则发生在2001年,VMware 发布了 ESX,法国著名程序员Fabrice Bellard编写了开源设备模拟器软件QEMU(Quick EMUlator)的第一个版本,实现了全虚拟化。后来 QEMU 通过集成 KVM 与 XEN 技术,广泛用于虚拟机管理监视器,并提供接近原生的半虚拟化性能。现今已成为了开源领域 VMM 的事实标准(QEMU-KVM 在中小云厂商中被广泛使用)。作为通用设备模拟器,它在设计上做到了尽可能兼容任何硬件平台,同时各种高级特性(例如设备热插拔)也在一直不断丰富,并于2019年宣称可以模拟所有设备。

然而风光的背后是,一些统计显示2013至2018年间 QEMU 报告的 CVE 漏洞数量由49%来自它所模拟的设备,其中最为著名的 VENOM ”毒液”漏洞就是由它所模拟的软驱控制器导致。现代硬件设备与 KVM 的成熟使得需要 QEMU 模拟的设备越来越少,对大量其它体系结构(事实支持 Intel、AMD、ARM足以)和陈旧设备(例如软驱、打印机串口)的模拟,而此时的 QEMU 还依然在做加法,截止到2019年整个项目的代码量超过了三百万行(仅列出 C 代码、头文件与 Makefile,不包括注释),总文件数量超过一万。因此 QEMU 频繁爆出虚拟机逃逸漏洞相关安全事件也就不足为奇了。

读者看到这里一定会有一个疑问,有没有一款更精简、更安全、更现代化、Bug 更少易于维护的开源 VMM 用来替代 QEMU 这个庞然大物?

那么这个问题的其中一个答案,就是今天文中将要介绍的 Firecracker。

关于 AWS Firecracker 虚拟机

作为一款轻量级虚拟机,Firecracker 仅仅实现了以下功能:

  1. 基于 VirtIO 的网络,磁盘和套接字驱动(virtio-net,virtio-blk,virtio-vsock),分别用于 MicroVM 的网络与磁盘 IO 访问,以及 MicroVM 上的 AF_VSOCK 套接字与宿主机的 AF_UNIX 套接字通信,从而实现 MicroVM 内运行的应用传递 vhost 内核代码到宿主机的通信
  2. 可编程的间隔计时器
  3. KVM 时钟
  4. 串口终端控制台(例如 /dev/ttyS0)
  5. 仅包含关机键的键盘

其中 VirtIO 驱动是一种主流的半虚拟化 IO 驱动方式, 它减少了用户态和内核态之前都切换次数从而提升效率,也是 QEMU-KVM 中最常用的一种 IO 驱动方式。

在介绍具体 Firecracker 之前,不妨一起分析下运行在云端的现代化 IT 工作负载,一类是曾经跑在 X86 物理机上的传统应用,客户会希望云端 EC2 虚拟机能够提供与本地  X86 同等,甚至更强的性能,更灵活丰富功能特性。另一类是通过容器、Serverless 等方式运行的云原生现代化应用,这类应用往往由更加轻量化、松耦合、服务和模块组成,运行方式也变得更加弹性和灵活。

传统虚拟机技术

承载第一类负载为代表的平台虚拟化技术经过了几十年的发展演进,从全虚拟化,半虚拟化,到硬件辅助虚拟化(例如Intel VT-x技术),再到如今以硬件卸载虚拟化(例如借助 Intel VT-d 通过 SR-IOV 方式卸载 IO 请求到硬件加速卡)。CPU 不需要预留计算资源给设备模型,而以前绝大部分设备都需要使用 QEMU 模拟(带来 CPU 额外开销),从而提升性能表现。

其中 AWS 的 Nitro 架构经过几代演进之后,现在的版本基于 KVM 虚拟化自研 VMM 以及配套专用硬件加速卡,这使得虚拟化环境下获得几乎与裸金属物理机一样的性能,大规模的这项技术用于新一代 EC2 实例,获得了极致的性能和安全可靠性。而关于 AWS Nitro 架构网上已经有大量的文章介绍,也可以参考 AWS re:INVENT 2017 CMP332 主题演讲,笔者在这就不做深度详细介绍了。

容器沙箱技术


而第二类负载的代表则是近年来大火的 Docker 容器,与传统虚拟机的捕获 – 模拟(Trap – and – Emulate)技术路线不同,容器最早的技术原型为 Unix 系统的 chroot 操作(诞生于1979年代),从而限制进程对指定根目录之外的文件访问。后来经历了 FreeBSD Jails、OpenVZ、LXC、libcontainer 等技术逐步演进,形成了完整的一套应用运行环境沙箱隔离技术。而 Docker 则通过一系列创新,将古老的容器沙箱技术上升到了一个新的高度。在笔者看来这其中主要包括但不局限于:

  1. OCI (Open Container Initiative) 规范了运行时标准
  2. 镜像分层设计与 OCI Image Spec 镜像规范
  3. 实现了一个轻量级容器运行时 RunC,它拥有简单运行容器所需要的全部组件
  4. 贡献给 CNCF 社区的 containerd 运行时生命周期管理程序

尽管这些优秀的设计使得 Docker 成功的大规模在生产环境中得到运用,并且后来的Kubernetes 等容器编排调度工具也选择了复用上面这些技术,但是容器的安全隐患从这项技术诞生的第一天起就一直存在。Docker容器技术采用共享宿主机内核方式运行,利用内核特性实现对不同用户的命名空间进行隔离(6种方式),使用 CGroups进行硬件资源分配与限制,POSIX Capabilities进行 root 权限分割等,Seccomp 限定系统调用。虽然有组合了很多种技术,但实现原理决定了容器的其隔离性、封闭性还是远远低于拥有独立 GuestOS 的虚拟机。由于操作系统内核漏洞,Docker 组件设计缺陷,以及不当的配置都会导致  Docker 容器发生逃逸,从而获取宿主机权限。由于频发的安全及逃逸漏洞,在公有云环境容器应用不得不也运行在虚拟机中,从而满足多租户安全隔离要求。而分配、管理、运维这些传统虚拟机与容器轻量、灵活、弹性的初衷背道而驰,同时在资源利用率、运行效率上也存浪费。

而AWS 同样面临着与 Docker 容器类似的技术困扰,2014年11月推出了 AWS Lambda 服务。为了提供无服务器计算下安全的体验,最初通过后台为每个租户置备独立的  EC2 实例从而实现与其它租户的强安全隔离。

随着 AWS Lambda 服务的增长,开发团队意识到需要一个新技术提供高安全性、灵活性和效率的运行时环境给类似 AWS Lambda 和 Fargate 这样的服务。此时开发团队利用打造硬件卸载虚拟化所积攒的经验,重新开始构建一款全新的 VMM 并且整合容器生态系统,也就是 AWS Firecracker。

 

AWS Firecracker 开源项目详解

从 AWS 官方公布Firecracker至今已经有一年时间了,在这短短的一年时间里,作为一个开源项目,不仅引起了社区的广泛关注同时也孵化出了多个基于 Firecracker 运行时的容器项目。开源也大大拓宽了 Firecracker 这款通用轻量级虚拟机的使用场景,除了现在 AWS 的无服务计算产品,还能运行在 AWS 以外的本地环境中运行容器,未来还有可能运行在边缘设备硬件上运行各种应用。

以社区的 firecracker-containerd 项目为例,通过该项目使得标准的 RunC 容器能够运行在 MicroVM 内部,而且通过 HostOS 上的 containerd 就能够管理 GeustOS 的容器。从而无缝兼容 OCI (Open Container Initiative) 标准规范,镜像格式以及管理工具,这使得以 Docker 和 Kubernetes 的主流容器运行调度框架只需要少量修改,就能够适配基于 Firecracker 虚拟化技术隔离的安全容器方案。这样提现了容器领域以开源文化主导的社区生态魅力。

除此之外与 firecracker-containerd 功能类似的另一个项目,则是由 OpenStack 社区主导的 Kata Containers,该项目在1.5版本后,新增加了对 Firecracker VMM 的支持(之前仅支持 QEMU和 NEMU作为 VMM),Kata 自己的运行时和 RunC 一样也符合 OCI 及 CRI 标准规范,能够同时被 Docker、Kubernetes 及 OpenStack 直接支持。

那么基于 Firecracker 的容器运行时有哪些优势呢?

首先是虚拟化提升了应用负载的隔离性,每个 MicroVM 内部仅运行一个应用容器,或者仅运行一个应用 Pod(Kubernetes 情况下,同一 Pod 内的多个容器还是采用 Docker 沙箱方式隔离构成单个应用)。而不同租户的不同应用之间,实现了虚拟化级别的隔离,每个应用不再共享 HostOS 内核,而是拥有独立的 GeustOS Linux 内核。与此同时 Firecracker  运行在普通 Linux 上,每个 MicroVM 都是以 KVM 进程的方式运行,由操作系统负责进程调度。对于不同 MicroVM 在宿主机上运行的 Firecracker 进程,Firecracker 通过静态链接以 jailer 方式启动,并通过 CGroups 和 Seccomp BPF 进行进程间安全隔离,全方位提供隔离性保障。

其次,安全始终是 AWS 关注的头等大事。虚拟化作为一项最基础的服务,其本身的安全性格外重要,在 Amazon EC2 的主页是这样描述产品的:”Amazon EC2 安全并且可以调整大小的云计算容量。在需要时启动应用程序,无需预先承诺。”,安全是描述 EC2 的一个词汇。而前文中也讲到了 QEMU 作为设备模型开源实现,它的安全方面的声誉却相当糟糕,这也成了 AWS 决定重新做一款 VMM 的原因。从主要两个维度降低风险:

  1. 抛弃 QEMU 使用的 C 语言,选择内存安全的 Rust 作为开发语言
  2. 基于 crosvm 使用极简设备模型,模拟尽可能少的必要设备,减小暴露的攻击面

在 Rust 语言的定义中,凡是可能会导致程序内存使用出错的特性,都被认为是不安全的(Unsafe),反之是安全的(Safe)。除非通过”  Unsafe “关键字显示使用, 如果您的代码有某种方式可能会导致内存不安全(例如缓冲区溢出),则会出现编译错误。生命周期和所有权也可以确保安全的并发性。 例如,编译器强制在任何时候,给定的内存区域只能由一个线程写,也可以由任意数量的线程读取。而与 Rust 并列的底层开发语言 C 和 C++,则是属于不安全的语言。

再次是高性能和低开销:得益于极简的设备模型, Firecracker 取消了 SeaBIOS (开源的 X86 BIOS),移除了 PCI 总线,取消了 VGA 显示等等硬件模拟,严格的说它甚至不是一台完整的虚拟计算机。而 Firecracker 运行的 GuestOS 使用的也是 AWS 定制过的精简  Linux 内核,同样裁剪掉了对应的设备驱动程序、子系统等等。因此叫它 MicroVM,其启动步骤和加载项要远远少于传统虚拟机。因此 Firecracker 目前已经能提供小于125ms 的 MircroVM 启动速度,每秒150台的启动能力,小于5MiB 的内存开销,并发运行4000台的极限承载容量(AWS i3.metal EC2 作为宿主机),以及热升级能力等。这些都是传统虚拟机所遥不可及,但现代化弹性工作负载又有强烈需求的性能指标。

其实为了解决容器安全隔离性问题,开源社区也涌现了大量同类项目,而 Firecracker  是跟它们相比有哪些不同,又如何脱颖而出的呢?在笔者看来这些其它方案大致分为两类:

一类是基于在已有的成熟 VMM,进行编译选项裁剪和 GeustOS 精简,同样使用虚拟化技术解决问题。这类方案包括基于 QEMU-KVM 的 Hyper.sh RunV,Intel Clear Container,Pouch Container 等,基于 VMware ESXi 的 vSphere Integrated Containers,以及 Microsoft 的 Hyper-V Container。这类方案的主要不足源于这些 Hypervisor 过去用在传统虚拟机,并不是面向无服务器计算这类的现代化工作负载而设计开发,因此在安全、性能、开销方面可能会存在先天不足。

而另一类则完全不使用虚拟机,以 Google 的 gVisor 以及一众 Unikernel 技术为代表gVisor 引入了进程虚拟化的思想,去掉了设备模型,将虚拟化的边界移到了系统调用层面,从而减少传统设备虚拟化的开销。而代价却是有大量的 Linux 系统调用需要沙箱处理,暴露了更多的攻击面(目前已实现了数百个系统调用)。而 Unikernel 则源于90年代的操作系统微内核理念,随着近年来容器技术的走红,这项技术也越来越被人关注(例如 Nabla container)。其核心思想是将 Linux 的宏内核拆分成为多个库,只打包应用依赖的库到微内核镜像中。每个应用拥有独立的微内核,从而提升性能,降低开销,减少攻击面。不过这些 Unikernel 项目大部分处于早期阶段,作为容器技术的竞争对手,其部署、管理以及与 OCI 的兼容性都会是一大考验。

小结

虚拟化技术从诞生到如今已经历经了近五十年,芯片硬件,虚拟化软件,操作系统厂商都在不断迭代自己技术,从而共同推动虚拟化技术的发展。近年来随着云计算的崛起,云厂商充分整合优化硬件、软件、系统三条线上独立发展的技术,在此基础上又结合业务需求进行了优化和剪裁,让大家已经看到了虚拟化的老树又开出了新花,AWS 从不同需求出发,通过 Nitro 与 Firecracker 两款虚拟机技术,将的虚拟化演进到了一个全新的高度。

引用

https://firecracker-microvm.github.io/

https://github.com/firecracker-microvm/

https://thinkwithwp.com/cn/blogs/china/kata-containers-1-5-firecracker-support/

https://thinkwithwp.com/cn/blogs/china/firecracker-lightweight-virtualization-for-serverless-computing/

https://www.slideshare.net/AmazonWebServices/c5-instances-and-the-evolution-of-amazon-ec2-virtualization-cmp332-reinvent-2017

本篇作者

莫梓元

AWS 解决方案架构师,有着超过5年的云计算领域从业经验,工作中担任过解决方案、售前、研发、实施、运维等多种角色。加入 AWS 之前,作为技术骨干负责中国联通基于 OpenStack 的沃云平台自研工作。