转载

DVM:Make VM run like Container

Docker的引爆和应用

之前的虚拟化,现在的容器,都是加一个间接层来解决问题的一个方式。David wheeler说计算机科学中的所有问题都可以通过插入另一个间接层来解决。目前只有一个问题解决不了,就是中间层太多的问题。所以中间层怎么加才能避免更多?

Docker是在2013年的时候提出的,同年就来到了中国,在2013年云计算大会上,就有来自dotCloud的Jerome Petazzoni来北京宣讲了Docker和Container。在Docker 1.0发布的一年之内,Google、亚马逊、微软都宣布了对Docker的支持。在整个开源界和技术界都是非常少见的,每个人都提到Container,亚马逊提供了EC2 Container Service (ECS),Google发布了Kubernets。

但是,实际上Container并不是非常新的技术,就是提到了容器技术的眼界。抛开最早的Uiunx的Chroot不提,从1998年的FreeBSD jail至今也是十几年的时间了,在Linux操作系统中,从2001年的Liunx-vserver,之后的OpenVZ,到2008年的LXC,都是发展了很多年的技术,为什么他们没有获得像Docker这样的爆炸性发展呢?

回顾一下操作系统的本质,暂且抛开桌面操作系统的复杂性,对于服务器操作系统来说,简单地说,它做了这么几件事情,其一是操作系统自身的安装;其二是包管理系统,用于应用和应用依赖环境的安装,从APT/dpkg,到Yum/RPM。接下来就是管系统的引导、应用的启动,最后,也是最本质的是管理硬件、调度软件Kernel。虽然操作系统做了这么多的工作,但kernel才是它最基本的事情。现在操作系统中的像packaging这样的功能,并不是我最终的想用的东西,我真的想用就是应用。

所以,Container实际既不是操作系统延伸,也不是虚拟机延伸,它是把应用往外延伸,变成一个原子的单元,直接用Container部署,这样Container完全地封装了,应用及其所有的依赖环境。这就让所有的包管理系统变得多余了。

就是在这样的场景下,Docker就诞生了,Docker实际上把两个技术搁在了一起,Container和layered images。这两个技术都不是新技术,分层镜像layered image实际在Live CD中已经有很久的应用历史了,Docker 所使用的也是同样的AUFS,但当这分层镜像和容器这两个技术遇到一起的时候,就达到了引爆点。

Docker给我们带来了什么?第一,使用Aufs(实际也支持其他的存储引擎),封装Dockerimages,这样保证了是开发测试与部署的高度一致性。第二是快速收敛,降低对配置管理系统的需求,之前使用配置管理系统,都是让系统逐渐地状态收敛到想要达到的配置。而使用Dockerimages之后,部署可以简单到甚至不需要配置,常常可以一步甚至零步就达到目标状态,这带来了整体运维成本的缩减。而使用分层镜像,不是非常大的单一镜像,在分发的时候可以只分发有变更的层,多个镜像可以共享基础层,可以降低部署的带宽和存储开销,甚至在运行的时候可以共享内存页面,降低内存开销。当然,开销对于很多人来说都不是最关心的部分,大家关心最本质的部分是对于节省流程改变。对很多人来说Container不是操作系统延伸,而是应用的延伸。

既然Container这么好,是不是有人会去想Iaas还是Paas重要吗?直接把Dockerimages丢在上面运行,现在的是Container Service,像Google的Container engige,亚马逊的ECS,尤其亚马逊ECS都是在Iaas基础上做的。但用户想用的只是让应用运转起来,他要想的实际是作为更好的Paas的Container Service,其实用户在大多数时候并不是真的想用虚拟机,但是如果抛开虚拟机,就需要面对容器安全的问题。即使是Docker上游,也警告说——不要把Docker容器当成可以完全替代虚拟机的东西,跑在Docker容器中的很长一段时间都是选择性的。也就是说需要选择那些善意的应用,或者说可信的应用来放进容器中——所有的服务对于可信的业务来说都是安全的。但是,对于公有云服务来说,所有用户都是有潜在恶意的,你需要在大部分情况下把他当作恶意用户去保证安全。做公有云的都了解,我们需要在各个方面防范用户可能入侵,任何一个接口都不应该被人能够利用。

隔离性的问题其实也是个哲学问题。这个实际上和军事问题很类似,这里可以引用孙子兵法第六篇《虚实》的一段话,“故备前则后寡,备后则前寡,备左则右寡,备右则左寡,无所不备,则无所不寡。”每个方面都需要防守的时候,即使有很多放手力量,在各个方面也都会变得相对薄弱。对于容器,容器里面的应用和容器外面的应用跑在同一个kernel上面,所有的系统调用都可以被用户访问,每一个系统调用都直接针对你的内核的进行访问,考验系统的安全行。相反,对于虚拟机来说,只有少数操作是可以穿透数据到外面的物理机,大部分情况下是完全隔离的,所以在近年应用中,虚拟机被广泛认为是安全的。

举一个例子,内核隔离性的一个进展是User Namespace,让一个普通用户可以在namespace里面得到一个root权限,但退出的时候重新成为普通用户,这样就可以让容器以普通用户的身份运行,从而降低安全风险了。但是这个特性的第一个版本出来的时候就被人注意到了一个潜在的问题,说一个普通用一旦建一个user namespace,UID就变成了零,只要能找到办法,带着root身份逃逸出namespace,就可以承袭系统的ROOT了,后来真的找到方法突破了user namespace。这个漏洞立刻被修复了,这个问题在Docker的使用场景中也基本不会发生。但是,类似这样的高风险漏洞真的不会再被发现吗?这是我们所面临的质疑,你可以去修正每一个发现的漏洞,但是挡不住用户那颗质疑的心。

我们或者说我们的用户们,想要享受Dockerimages带来的好处,但这个时候我们还需要对多租户用户进行隔离,既然做不到像虚机一样安全的容器得,反过来为什么不能把虚机变得像容器呢?

DVM

在多租户环境下我们需要做什么?首先需要可以和Docker用的一样的images,开发测试和部署具有一致性,部署和管理具有原子性,可以运行在虚拟里面进行用户隔离,但是开销不要太大,这个思路本身借鉴了Google的novm的项目。

我们把我们的产品称为DVM,简单地说,是KVM加上Docker Images。具体说,首先是基于KVM和qemu来提供天然隔离性,精心剪裁,保证性能和开销上不会太大。然后,我们提供了原生的同时运行多个相关应用的支持(POD),提供了rest接口,方便进行orchestartion。

如图1-1所示

DVM:Make VM run like Container

DVM的总体架构和Docker非常类似,有一个Daemon来做管理,每一个DVM虚机大概是这样的——Deamon会为每个POD启动一个VM,里面有一个裁剪过的内核,其上的initrd会把所有的docker images装载进去,启动到各自的Mount Namespace。而所有的这一套东西叫它DVM。dvm可以从第三方通过API,或者通过command line去管理,这会让docker用户感到比较熟悉。

POD的概念是直接源于Google的Kubernetes。对于Google来说他们可能不会单独运行一个Container,而是会运行一组相关的Container,可能其中有些用于跑服务,有些用于跑代理等辅助功能,有些是做一般性的日志服务等支持性服务,紧耦合在一起工作的方式。POD中的Container Image彼此独立,这样在开发、维护中可以更方便,在运行的时候,他们彼此间的文件系统是隔离的,但是同时POD内的image会分享network、pid等namespace,这样就做一些管理和操作。POD概念部分意义上和Docker的compose有些类似,只是compose没有那么直接和纯粹。在DVM中,我们将Container换成了VM,构成了POD的天然边界,在VM内部的应用间,不同Image跑出来的应用属于不同的 mount namespace,在文件系统方面彼此隔离,但共享pid 和 network namespace,这样保证他们之间既有隔离,又不失灵活方便。

DVM的基本使用流程和Docker非常接近,可以直接使用几乎所有的Docker Images,可以直接将Pod任务提交给DVM Deamon,或者是直接run一个docker images,daemon就会运行一个kvm虚拟机,并通过initrd将pod中所指定的docker image 运行在虚拟机中。

DVM的开销分为几个方面,包含是性能开销、启动时间开销、还有内存开销等。首先对于CPU内存子系统的性能开销,大家知道现代的虚拟化系统,对不同的应用开销不太一样,但是对于大部分场景控制在10%以内是可以做到的。而对于IO来说,对于大规模的应用,调度系统会把Pod调度到不同的机器上面去运行,在这个时候为了应用能够在遇到故障的时候重新恢复,会倾向于使用共享存储作为持久化存储存储方案,不论是否有有虚拟机,网络开销都是更主要的。对于EBS这样的网络存储,相对于本机存储来说时延更高,在应用IO并行不充分的情况下,对任何用户都会有一些开销的,这些开销的主要部分并不是虚拟机带来的。

启动时间开销:因为VM引导时间是在一秒之内,VM启动之后大概在KVM支持情况下,可能比较简单的机器就能做到400到500毫秒的时间。既使是在测试环境中DVM,用的时间也只是800毫秒时间。所以对于一秒钟要启动几次应用,这个开销是有影响的。对于一些要跑一个小时再关掉,dvm的方式就是完全可行的了。同时,我们使用了用热插拔的技术作为一个优化,先启动虚拟机再把POD用到的那些images插进去、启动起来,这样,对于没有特殊kernel和配置要求的情况,可以预先启动好虚拟机,只要准备好image就可以插进去运行,达到毫秒级响应,这就和使用Docker没有什么区别了,一样是创建images和Container的消耗。

至于内存开销,作为KVM是qemu开销和内核开销,内核开销可以降低到十几兆程度。把那些给各种硬件进行支持的部分都被省掉了。但是尽管如此,作为Container来说还是会比DVM开销要小,它有很多内存可以分享,而且要想超卖内存的话,容器肯定是比虚机超卖内存方便的多。但是如果真的云服务商,不管目标是要打败亚马逊,去提供对于用户很棒的公有云服务,还是说只是要去做私有云,对于内存超卖的问题,都需要非常慎重。

对于dvm带来的额外的限制,和Docker相比,有些和Host共享的东西都是在DVM这里是做不到的,DVM强调的是用虚机来做到更好的隔离,所以破坏隔离性的特性都做不到。然后关联性的限制,容器关联,dvm POD内部的Container之间是很容易关联的,其自由度非常搞,共享pid 和network namespace,可以通过localhost 互相访问,但是dvm保证了两个POD之间的隔离和安全,一个Pod入侵不了其他的Pod,反过来想打破这个隔离来进行跨Pod的关联也很难不到,无法在Pod间共享任何namespace。

Dvm用一个json描述一个Pod,如图1-2所示。

DVM:Make VM run like Container

dvm支持你pod有一个共享的Hostname,支持指定多个Docker Images,可以通过file配置运行时添加到pod之中的文件——当用户大规模部署系统的时候,很多情况下不是把host的文件映射到容器内部,而是具体部署一个指定内容的文件到Image的哪个位置,这个文件可能是渲染过的,也可能是从一个URL下载的。启动之前把文件插到VM某一个应用的Image的位置,这是和Host的没有关系的。对于部署在任何位置都可以使用的。Dvm同样也支持Docker这种把host 的文件或路径映射到image内的方式。但是一般情况下考虑集群规模的部署情况下很少会这样去用的,因为如果有上百台机器,不太可能会去特别考虑被部署机器的特定的文件系统映射需求,在这个情况下,大多数部署都是部署指定的文件,而不是某个文件映射进来,那样只是破坏了一致性和封装性。我们提供这个可能性因为一能做到,二是帮你做开发的时候你也可以用这个功能。但是我们并不认为做大规模部署的时候这样的功能很常用。

Dvm是为了在Container Service的环境中使用的,我们做是生态环境中相容的一环。首先虽然DVM做的是剪裁过的kernel,用户也可以使用自己的kernel,自定义kernel。现在来看我们是基于kvm的,但是实际上我们对采用不同的 hypervisor没有什么障碍,增加支持更多的hypervisor有助于帮助用户的迁移。对于其他系统的集成,我们可以和Orchestration工具,也和调度系统,和网络服务,和存储服务等进行很开放的合作,既然我们都很看好Container Service,就让我们一起来推动技术和社区的发展。

欢迎大家和我们交流,我们很快也会开放源代码,与合作伙伴们共同发展。

作者简介

王旭:DVM创始人,CTO,前VisualOps CTO,多年的Debian,Kernel,分布式存储老兵

正文到此结束
Loading...