论文:《X-Containers: Breaking Down Barriers to Improve Performance and Isolation of Cloud-Native Containers》

背景介绍

什么是容器?

容器是一个标准的软件单元,它将代码及其所有的依赖打包,以便应用程序在不同的计算环境之间快速可靠的运行。容器的本质,用一句话来解释,就是一组受到资源限制,彼此间相互隔离的进程。形象一些来说,容器其实是一种沙盒技术,能够像集装箱一样,把我们的应用“装”起来,应用与应用之间就因为了有了边界而不会相互干扰,而被装进集装箱的应用可以被方便地部署到不同的环境中。

容器建立在两个关键技术上:Linux Namespace和Linux Cgroups.

  • Namespace创建一个近乎隔离的用户空间,并为应用程序提供隔离的系统资源(主机名、文件系统、网络、进程ID系统、IPC和用户);
  • Cgroups用来做资源限制(cpu、内存、网络、存储的使用限制);

当前比较流行的容器非docker莫属了,不过除了docker之外还有一些容器技术,包括由coreos开发的rkt以及较早的LXC。为什么需要容器呢?云原生应用的每个功能模块放在一个容器中,便于扩展、重用、升级。

image-20200729205428289

容器与虚拟机有什么区别?

image-20200729205739274

可以看出,容器是没有自己的OS的,直接共享宿主机的内核,也没有hypervisor这一层的硬件虚拟化以及基于hypervisor的资源隔离和限制,所有对于容器进程的限制都是基于宿主机的操作系统内核特性来进行的,比如之前提到的Linux Namespace和Cgroups,由此容器获得了一个很大的优势:轻量化,由于没有hypervisor这一层,也没有自己的操作系统,自然占用资源很小,产生的镜像文件也要比虚拟机小的多。

容器技术的不足有哪些?

  1. 运行在同一个宿主机上的容器直接共享宿主机的内核,如果某个容器发生了容器逃逸,那么其他容器也会存在安全风险;
  2. 容器是一组资源受限、彼此间相互隔离的进程,容器中的应用运行在自己的进程空间,无法在内核安装自己的功能模块,因此需要进行内核定制化的应用受到限制;
  3. 通用内核由于被所有容器所共享,因此无法为特定的容器进行裁剪和优化;

已有的支持容器隔离的安全容器方案有哪些?

基于轻量级虚拟机隔离的安全容器方案,与传统的虚拟机比较类似,这里以Kata Container为例简单说明。

Kata Container是通过虚拟机管理程序(图中的hypervisor)向上提供虚拟化硬件,hypervisor上层是经过裁剪的一个精简的gueset kernel,这个内核是经过高度优化的,只用于一个容器的运行,kernel上层的Agent为容器创建对应的namespace和资源。为什么说Kata Container是一个轻量级虚拟机隔离方案呢?因为它通过hypervisor(QEMU)虚拟出来的硬件设备和高度优化的kernel提供了一个完整的虚拟机环境。

image-20200730152105726

Kata Container的优缺点:

  • 优点:轻量级VM,拥有VM的隔离性;
  • 缺点:因为包含hypervisor的硬件虚拟化以及内核初始化,启动速度与传统的容器有差距;因为已经使用了虚拟化技术,无法在虚拟机内运行;

基于用户态内核的安全隔离方案,gVisor是比较具有代表性的实现方式,gVisor是用Golang编写的用户态内核,或者说是沙箱技术,它主要实现了大部分的system call,运行在应用程序和内核之间。gVisor运行时,是由多个沙箱组成的,这些沙箱进程共同覆盖一个或者多个容器,通过拦截从应用程序到主机内核的系统调用,并使用用户空间的Sentry处理;gVisor充当了一个用户态的操作系统内核,为应用进程提供强隔离

gVisor的优缺点:

  • 优点:使用Golang编写,强大的安全类型以及内存管理,比用C写的Linux内核更加安全;
  • 缺点:中断应用程序的system call,再使用sentry去和内核通信会产生开销,性能差;适配场景较少或者说需要定制化,因为它实现的系统调用有限,不支持所有的系统调用;

image-20200730154050418

基于Exokernel+LibOS的安全容器方案,Exokernel,顾名思义,是一种内核设计,它的设计思路是尽可能减少抽象层次,允许应用程序直接访问硬件,Exokernel只负责保护和分配系统资源,硬件的访问由应用程序进行。一些应用,典型的比如数据库软件MySQL,是希望直接跟硬盘进行交互来针对硬件进行优化,这就可以用到Exokernel。LibOS是什么呢?它的全称是Library Operating System,它提供对于硬件的抽象,与用户代码编译成一个在同一地址空间的二进制文件。LibOS可以修改定制,来适配上层用户对硬件的具体需求。

基于Exokernel+LibOS思想设计的容器方案的优劣:

  • 优点:打破了用户态下LibOS与应用进程安全隔离,带来性能提升;LibOS可针对应用定制化;Exokernel与LibOS更简洁,潜在安全威胁小;
  • 缺点:当前的LibOSes,比如MirageOS,Graphene等,对系统调用的兼容性差,不支持多进程;
image-20200730204646792

X-Container架构与实现

X-Container的架构是怎样的?

image-20200730210154931

上图是包括X-Container在内的不同容器架构的比较。最左边的是一个标准容器架构,比如Docker,所有的容器共享主机内核;后面的是一些安全容器方案,第二个是gVisor,它通过加了一个中间层对应用程序的系统调用进行拦截来提供容器的安全隔离;第三个和第四个是基于轻量级虚拟机隔离的安全容器方案,分别通过KVM和XEN-PV来提供一个完整的虚拟机环境供容器运行;最后就是这篇论文提出来的X-Container架构。

可以看到X-Container包含两个核心组件,一个是X-Kernel,一个是X-LibOS。

  1. X-Kernel:由Xen 4.2修改而来,直接利用Xen的PV架构提供了虚拟机级的安全隔离能力,并且Xen作为虚拟机管理器(hypervisor)提供的原语比较简单、攻击面更小,通过一个叫Xen-Blanket的Linux驱动整个方案可以部署在一个标准的公有云的虚拟机内;
  2. X-LibOS:针对之前提到的LibOS安全容器方案的缺点包括系统调用兼容性差且不支持多进程,这里的X-LibOS采用Linux内核,版本为4.4.4,这样能做到完全的系统调用兼容,并且结合Linux内核自身的进程管理可以支持多进程、多线程的并发执行环境;由于Linux内核可以定制化,比如指定内核参数、编译选项等,所以可以针对特定的容器应用进行裁剪优化,减少攻击面,提高性能。

X-Container利用成熟的Xen的半虚拟化架构提供了容器间的安全隔离能力,容器内通过优化的Linux内核提供多进程支持,但不提供容器内进程的安全隔离。X-Kernel位于ring0,X-LibOS与应用程序同时运行在ring3,实现时修改了Xen的代码消除了用户态Linux内核和应用程序间的隔离,将Linux内核映射到应用进程的高地址空间。

总结一下X-Container的优缺点:

  • 优点:打破了用户态下LibOS与应用进程安全隔离,带来性能提升;LibOS可针对应用定制化;Exokernel与LibOS更简洁,潜在安全威胁小(继承了LibOS+Exokernel架构容器方案的优点);成熟的Xen-PV架构提供VM级的隔离性;定制化Linux内核支持多进程且兼容性好;
  • 缺点:受限的容器内安全隔离,如OpenSSH、NGINX等需要保证进程隔离的应用无法在X-Container上运行;由于需要运行LibOS,内存占用相对更大;由于包含Xen的硬件虚拟化,启动时间相对更长;

下面这张图给出的是几种容器架构的优缺点比较:

image-20200730222840793

X-Container有哪些实现细节?

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

在Xen中增加模块实现从系统调用到函数调用的转换

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

  • 方式1:Trap方式。应用程序系统调用时首先进入到运行在内核态的X-Kernel,X-Kernel判断系统调用类型以及参数,然后让应用程序返回到同一地址空间的Linux Kernel(X-LibOS)中的系统调用入口去执行真正的系统调用。因为消除了客户环境下Linux内核和应用进程的隔离,Linux内核被直接映射到了进程的高地址空间,相应的系统调用入口地址记录在位于进程地址空间固定位置的系统调用入口表(Syscall Entry Table)中,X-Kernel做的事情其实就是查表,找到系统调用在进程同一地址空间中的地址,然后返回给应用程序。但是在这种情况下,性能和一般虚拟机环境下的系统调用没有太大差别,因为X-Kernel和应用程序分别运行在内核态和用户态,存在着两次特权级地切换。
  • 方式2:Function call方式。因为消除了Guest环境下Linux内核与应用进程间的安全隔离,它们都运行在用户态,一些可以修改代码的应用程序可以直接通过这个系统调用入口表找到系统调用入口通过函数调用的方式来加速系统调用,无需通过运行在内核态的X-Kernel来中转,往往需要修改应用代码。
  • 方式3:透明Function call方式。X-Containers 实现了一个运行于X-Kernel中的叫做==ABOM(Automatic Binary Optimization Module)==的组件。这个组件可以在X-Kernel识别到系统调用指令之后,根据当前上下文指令识别系统调用的模式(包括syscall指令的前后指令,syscall num和参数),然后将进程地址空间中的系统调用指令替换为function call的指令(仅内存)。这样,下次再次从该地址触发syscall指令时就可以通过函数调用的形式进行。下图就是ABOM组件将代码中正常的系统调用指令替换成函数调用指令的例子。

image-20200730224420740

X-Container性能评估

下面简单看一下X-Container的性能评估,第一个图是X-Container以标准化容器Docker为参照,在不同的云环境下的系统调用的吞吐量的一个比较,灰色柱状图的是Docker,蓝色柱状图是X-Container,X-Containers的性能是docker容器syscall吞吐的27倍。

image-20200730225328174

image-20200730225404061

同时针对Google’s gVisor and Intel’s Clear Containers等安全容器方案,在不同的benchmark的数据也有较大的性能提升(具体参考论文数据)。与标准Docker的比较,在不同场景各有优劣势。

image-20200730225507294

上图可以看到,随着容器数量的增加,X-Container的吞吐量仍然能够保持较高的值,其可扩展性较好;

image-20200730225626878

从这个图也能看到X-Container的一些不足,比如启动时间较长,且占用内存比较大。

总结

X-Containers整体效果上来看是非常好的:

  • 基于Xen-PV提供了VM级别的安全隔离能力;
  • 基于Linux内核,可以做到应用良好的兼容性与多进程支持,可以无缝对接现在绝大多数容器;
  • 对Guest环境的地址空间与函数访问做了优化,性能也做到了架构上的最优;

参考

https://tech.meituan.com/2020/03/12/cloud-native-security.html

http://gaocegege.com/Blog/%E5%AE%89%E5%88%A9/unikernel-book

http://gaocegege.com/Blog/csp/unikernel

https://remimin.github.io/2018/09/12/kata-container-01/

http://dockone.io/article/2442

https://note.youdao.com/ynoteshare1/index.html?id=27c128c6e81c2800def7c25d5eb11ce9&type=note

https://tech.meituan.com/2018/11/15/docker-architecture-and-evolution-practice.html

https://kernel.taobao.org/2019/05/x-containers/

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐