17

X-Containers:打开Linux as LibOS的潘多拉魔盒

 3 years ago
source link: https://kernel.taobao.org/2019/05/x-containers/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

May 28, 2019 • 启翾

X-Containers:打开Linux as LibOS的潘多拉魔盒

本文基于对今年ASPLOS 19的一篇论文《X-Containers: Breaking Down Barriers to Improve Performance and Isolation of Cloud-Native》的理解整理而成。
PPT地址:http://www.csl.cornell.edu/~delimitrou/papers/2019.asplos.xcontainer.pdf
视频地址:https://www.youtube.com/watch?v=OxAFxS-NgyE&feature=youtu.be

该论文针对现在云原生Single-Concerned(即一个容器只做一个事情)的普通容器平台的安全隔离问题,而提出的一种全新解决方案。X-Containers想要达到3个目标,1) 容器之间具备VM级别的安全隔离能力,2)保持对现有容器的二进制100%ABI兼容(包括binary不需要重新编译,多进程并发实现)及OCI容器标准兼容,3) 性能相对于普通runC容器来说没有太大的下降。

现有方案的问题

现有的安全沙箱方案(如Clear Containers、Kata Containers、Hyper Containers、Hyper-V Containers等)可以在兼容OCI标准、100%二进制ABI兼容的前提下,解决上述安全隔离问题。但是论文作者提出这些方案均依赖于硬件辅助虚拟化技术,无法部署在所有的云环境下(比如Amazon EC2),有些公共云或私有云(比如Google Compute Engine)通过支持嵌套虚拟化技术来运行这些安全沙箱方案,但是这会极大地影响性能。X-Containers试图提出了一个能够运行在所有云环境的一种不同的方案。

Google的gVisor方案(ptrace模式),可以在用户态实现容器间的安全隔离,但是性能和兼容性与标准runC容器有一些差距。

一些非Linux Kernel的Unikernel或LibOS也是试图通过硬件辅助虚拟化的机制来启动容器,但是依然存在云基础设施的限制,而且均存在Linux Kernel的100%兼容性问题。另外一类是user mode LibOS,比如Drawbridge或者LKL(Linux Kernel Library),他们都缺乏多进程管理的能力,因为它们的底层平台是Linux Kernel,Linux Kernel没有给用户态进程开放页表管理(多进程管理)的能力。Graphene通过Syscall fork出新的LibOS,并借助Linux Kernel来实现复杂的进程间通信功能,这样进程间通信的效率和安全性就不是最优的。另外Graphene只实现了1/3的Syscall功能,达不到二进制100%ABI兼容。兼容性(包括多进程管理)的天然缺陷目前已经成为Unikernel/LibOS推广和应用的最大阻碍。

SOPS17的LightVM《My VM is Lighter (and Safer)than your Container》以及ATC 2018的KylinX《KylinX: A Dynamic Library Operating System for Simplified and Efficient Cloud Virtualization》均是通过Xen的PV架构进行容器的隔离,可以在任意的云环境下面部署,但均没有解决Xen的PV架构(X64模式)的性能缺陷。同时两者均基于一个叫MiniOS的LibOS进行扩展,MiniOS并未达到二进制100%ABI兼容的能力。

X-Containers

1.png#align=left&display=inline&height=257&originHeight=364&originWidth=774&size=0&status=done&width=547
通过架构图可以看到,X-Containers架构,它有两个核心组件。一个是很薄的位于ring0特权级的Hypervisor(即X-Kernel),另一个组件是位于ring3特权级的X-LibOS,它和Application运行在同一个特权级,因此可以实现相互高效地函数调用。

1) X-Kernel:由Xen 4.2修改而来(近800行代码)。它位于ring0 Level。这里可以是Root模式的ring0,也可以是Non-Root模式的ring0,因此整个方案可以部署在一个标准的公共云的VM内(通过一个叫Xen-Blanket的Linux驱动)。

2) X-LibOS:由Linux Kernel 4.4.44修改而来(近1800行代码,其中1500行来自arch和半虚拟化的汇编语言),这里采用Linux作为LibOS的最主要的原因是为了达到100%的ABI兼容性。因为作为一款通用的云产品,你永远无法预料千奇百怪的应用对内核功能的依赖程度,要做到Linux 100%ABI兼容最好的方法就是直接使用Linux。其实Linux内核代码虽然庞大,但并不代表它的性能就差。相反它是一个高度可定制化(内核参数、proc参数等)、可裁剪、可调试性强、潜在高性能的内核。之前,在多租、多容器环境下,可能由于顾此失彼而导致无法做到性能最优,但是服务于Single-Concerned的云原生应用的Kernel,就有可能将系统裁剪、调优到最佳状态(比如为了单vcpu而关闭SMP,可以提升系统spinlock性能)。

从上图可以看到X-Containers 架构和LightVM一样,也是利用了成熟Xen的PV架构(包括成熟VCPU管理、中断管理及页表管理等体系),再结合Linux Kernel自身的进程管理,就可以在Guest实现一套完整的SMP多进程、多线程并发执行环境。存储和网络能力也还是原先的Guest VM视角看到的虚拟设备,因此管理各个Linux VM的方法,还是与之前的接口类似,但可以说是基于原先Xen的PV虚拟化架构和接口,为Single-Concerned云原生容器做的一个架构升级。

特权级与地址空间

X-Kernel位于ring0,不同于标准Xen下的Linux Kernel,X-LibOS与Application同时运行于ring3的Guest环境。X-Kernel修改了Xen的ABI,消除Kernel(X-LibOS)和Application之间的隔离。Kernel(X-LibOS)映射到了Application的地址空间,所以就算直接访问内核数据也不会引起一般情况下的内存访问异常。和正常的Linux一样,X-LibOS被映射进程了每一个进程地址空间的高地址部分(所有进程共享),低地址部分是进程的部分。

一般情况下,为了进程间的安全隔离,Xen下的Guest Kernel的页表的Global bit不会置上,进程切换会全部刷新。但是X-Containers内会置G bit,只有在切容器时才会刷G的TLB entry,性能有所提升。另外,标准Xen的PV架构为了支持X64模式,只能在ring3同时运行Kernel和Application(因为X64模式取消了段保护),为了保护Kernel和App的隔离,分别用不同的页表(类似于gVisor KVM模式的sentry),那么普通的Syscall都会通过Xen来中转并且有页表的切换,性能不是太好,这也是为什么Xen后来用硬件辅助全虚拟化来支持X64平台的原因。而X-Container为了增强性能,牺牲了Kernel与Application之间的安全隔离能力,采用同一个页表。这两点变化弥补了Xen的标准PV架构(X64模式)的性能缺陷。

从Syscall到Function Call

X-Containers提供3种syscall调用方式。

方式1:Trap模式。应用程序通过Syscall陷入ring0(X-Kernel),X-Kernel判断syscall num以及参数,然后让应用程序返回到同一地址空间的Kernel(X-LibOS)中的系统调用入口(从syscall entry table中获取)去执行真正的系统调用。但是这种情况不是LibOS的函数调用模式,性能和一般的Guest Kernel内核访问没有太大差别,存在着两次特权级地切换。

方式2:Function call模式。X-Containers在每个应用的地址空间中透明的映射了一个vsyscall page,里面包含了syscall entry table,一些可以修改代码的应用程序可以直接通过这个table找到syscall entry来加速syscall的调用。

方式3:透明Function call模式。X-Containers 实现了一个运行于X-Kernel中的叫做ABOM(Automatic Binary Optimization Module)的组件。这个模块可以在X-Kernel识别到syscall指令之后,根据RIP的上下文指令识别该次syscall指令的模式(包括syscall指令的前后指令,包括syscall num和参数),然后将进程地址空间中(仅内存)的该RIP及上下文替换为function call(即使是只读映射)的指令(有点类似热补丁技术)。这样,下次再次从该RIP地址触发syscall指令时就总是一次高效的function call了。

2.png#align=left&display=inline&height=494&originHeight=756&originWidth=766&size=0&status=done&width=501
从图中可以看到,两条指令(mov和syscall)直接替换为一条callq指令,rax这个寄存器也不需要赋值了,因为该RIP已经替换为了syscall entey table中function call的绝对地址的callq指令调用。目前只支持跟随着mov指令的syscall指令,同时X-Containers也提供工具离线修改application或library的二进制,以注入代码去将更为复杂的syscall指令修改为function call。

这个地方笔者认为是项目的画龙点睛之笔。既要实现LibOS所带来的function call的性能好处,又实现了二进制(binary不用重新编译)兼容,还很有可能巧妙地避免了GPL Licence的感染。

再回过头来看,X-Containers的确是通过直接利用Xen的PV架构继承了Guest VM间的安全隔离能力。虽然X-Containers的Kernel与Application的隔离很弱,但是Xen作为Hypervisor与Guest的原语非常简单、攻击面比较小,再加上Xen作为TCB代码非常少,因此Hypervisor与Guest之间的安全性也可以得到保障。这里通过一个简单地ring3就可以做到隔离,与平时见到的其它的依赖于Linux Kernel的user mode LibOS的区别在于,其它user mode LibOS是基于Linux Kernel,而Linux Kernel没有类似VCPU、页表管理等VM的原语定义。Linux Kernel只有进程原语,所以user mode LibOS只能依赖进程的线程管理、内存管理等Syscall原语来模拟一个VM的行为,这个接口与攻击面是比Xen的VM原语要大一些的。因此可以看到像gVisor、Graphene等user mode LibOS基本上都是需要通过seccomp来做一些syscall上面的限制。

X-Containers的性能是docker容器syscall 吞吐的27倍。同时针对Google’s gVisor and Intel’s Clear Containers,甚至和Unikernel and Graphene等,在不同的benchmark的数据也有较大的提升(具体参考论文数据)。与标准Docker的比较,在不同场景各有优劣势。

3.png#align=left&display=inline&height=384&originHeight=384&originWidth=1520&size=0&status=done&width=1520

1) 安全模型:X-Containers的安全模型重点是强调容器间、以及容器与Hypervisor的隔离,为了降低性能损失,X-Containers修改了标准的PV架构API,打破了容器内进程间、以及User与Kernel间的隔离性,Application与Guest Linux采用同一个页表,同一个特权级且通过function call进行调用。因此不适合那种通过进程来做隔离的应用(比如OpenSSH server通过不同进程来隔离用户,或者区分root进程和普通用户worker的Nginx服务),另外也不适合容器内进程间强容错的应用。

2) GPL感染:Linux作为LibOS与Application进行函数调用,是否会让Application受到GPL感染的风险,这个问题值得再次深入探讨。因为只是几条指令地替换,并未有链接过程,标准的binary(在普通Linux环境是不受感染的)是未经重新编译就是直接运行的,这个可能会降低GPL感染的风险。但是论文作者的确并没有证明100%没有问题。也姑且把它作为一个架构约束留作读者的家庭作业吧。

在整个环境中,Docker引擎运行在HostOS的Domain-0上,通过runV的runtime来启动X-Container容器。在启动过程中,通过一个简单的”boot loader”来启动X-LibOS,附带个X-LibOS传递一些参数,如虚拟设备,ip地址等。这样X-Containers容器就无缝地融入了现在标准的Docker容器平台中。到目前位置,到底是通过Linux as LibOS来弥补Xen PV架构(X64模式)的性能缺陷,亦或是先确定了Linux as LibOS,然后再选择了不少Unikernel/LibOS(比如LightVM、KylinX、ClickOS的MiniOS或者是MirageOS)均喜爱的VMM试验田Xen,我们不得而知。X-Containers整体效果上来看是非常好的,首先不依赖于硬件辅助虚拟化就让容器间、容器与Hypervisor间具备了VM级别的安全隔离能力;第二与原生Guest Linux Kernel的对接,可以做到应用的二进制以及100%ABI兼容(也意味着多进程支持),可以无缝对接现在绝大多数容器;最后一点,通过LibOS function call的方式对Guest 环境的地址空间与函数访问做了优化,性能也做到了架构上的最优。基本上达到了项目最初的3个目标。而且即便是作为声称为约束的容器内的弱安全模型,也“幸运”地符合Single-Concerned云原生容器的特点。或许,这里的约束是一种不得已而为之的无奈。下图是X-Containers与各个安全容器方案的综合比较。

4.png#align=left&display=inline&height=329&originHeight=524&originWidth=784&size=0&status=done&width=492

借鉴与影响

从X-Containers 论文的情况来看(源码暂时未开放),目前某些细节以及功能只是一个原型,仍然很不完善,同时Xen的社区热度和技术生态也不如KVM那么丰富和完整。但它的技术特点还是有非常值得借鉴的地方,一是巧妙地“疑似”解决了使用Linux Kernel来作为LibOS所面临地GPL感染问题。这个idea的提出,就算目前ABOM的方案解决不了GPL问题,相信也会有人朝着这个方向继续探索。因为我们知道,现在绝大部分的LibOS的基础都不是Linux,原因之一就是GPL问题。Linux as LibOS的魔力得到释放,就像打开了潘多拉魔盒一样,或许就在若干个月之后,我们就会看到一些基于该idea的Linux或KVM平台下的新的安全沙箱的创新方案。二是通过尝试PV虚拟化机制去除基础设施约束,打开了一条不同的容器安全之路。这有助于打破现有的多租云原生PaaS服务的市场格局,催生出不同的更丰富的PaaS产品形态。目前该论文的3位作者已经根据这篇论文的idea在NSF(National Science Foundation)”的资助下成立了一个初创公司(Exotanium Inc,一作沈之明任CTO),足以看出作者对于该项目、方案前景的信心。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK