Mesos架构探索之路 一切皆可模块化

本文是Mesos爱好者黄浩松在7月23日数人云对话Docker&Mesos沙龙活动上做的分享实录。黄浩松在Mesos社区非常活跃,他不远千里从新加坡而来,与大家分享了他对Mesos的深刻解读。

黄浩松
MESOS爱好者

很高兴与大家分享我在社区一些工作的进展,我在社区的ID叫haosdent,大家如果对Mesos有什么问题或者改进的建议都可以通过Mesos邮件列表或者Mesos的Slack来提交。

今天想跟大家分享的是我之前以及最近在Mesos做的一些工作——把Mesos里面一个重要的组件Containerizer模块化。首先向大家介绍一下为什么要把Containerizer模块化以及如何把它模块化。

Mesos的四种Containerizer

Mesos集群里面包括master、agent这两个重要的组件。当任务由Container启动的时候, Mesos agent会调用Containerizer这个组件去启动Docker或者是Mesos的Container。简而言之,Mesos Containerizer主要是管理容器的生命周期和任务的资源使用,以及通过隔离避免用户之间任务相互影响。比如在用Docker启动任务的时候,先pull镜像,把镜像pull下来之后需要启动、执行任务,最后销毁这个镜像。如果用Mesos来管理集群的话,这些都是由Containerizer来完成的。

现在Mesos里面有四种Containerizer,其中大家比较熟悉的是Docker Containerizer,Mesos用它来编排Docker镜像。另外Mesos也有它自己的Container—— Mesos Containerizer,管理自己的Container。

第三种是一种比较特殊的Containerizer,当用户有一些特定的需求,比如用户内部或者公司有一些不想公开出来的Container,但是又想使用Mesos,就可以通过Mesos提供的External Containerizer来完成。用户需要去写一个中间的代理层,实现Exteranl Containerizer定义的一些接口,定义之后就可以直接把用户特定的Container跑在Mesos上。

最后一种Composing Containerizer也是比较特殊的,Mesos启动的时候可以支持多种Container的类型,所以用户注册的所有Container都会注册到Composing Container里面。Composing Containerizer会负责决定用户的任务是用Docker启动还是用Mesos内部的Container启动。它相当于一个Proxy,把用户实际的任务代理到Docker Containerizer,Mesos Containerizer或者External Containerizer。

目前Docker Containerizer主要是由Tim完成的,它能够直接调用Docker daemon命令,与Docker daemon通信,然后让Docker完成一些事情。目前支持Docker的1.0.0版本,但是Docker1.0.0是很旧的版本,所以里面其实有很多tricky的代码要考虑到Docker版本的兼容性,这一块的代码维护成本非常高。

Mesos Container支持OS X,Linux以及目前最新的Windows。但OS X或者是Windows只是调用一些最基本的OS语义,并不像在Linux上支持那么完整, Linux可以直接调用cgroups、namespaces、chroot等一些东西,基本上Docker能做的事它都能做。

External Containerizer是为了用户的特殊需要,集成用户自定义的一些Container。但是用户编写的时候非常麻烦,必须要对整个Mesos里面通信的协议非常了解,才能按照它的定义去实现这些接口。模块化是一种更推荐的方式,所以External Containerizer目前已经废弃。根据之前用户列表的反馈,使用此类Containerizer的用户基本都已经迁移到其它的Containerizer。

应运而生的Unified Containerizer

Mesos维护各种不同的Containerizer是非常困难的,成本很高。想象一下所有不同类型的Container都要在同一份代码里面管理,而有时这些代码是大量重复的,假如一处有bug,那其它所有地方都要改一遍。再比如发现了Docker的一些问题,推动Docker去修改也是非常麻烦的。所以我们又有了Unified Containerizer,它拥有镜像管理的功能,之后还会支持AppC镜像和Docker镜像。

Mesos的模块化

Mesos的模块化是按照用户的需要去动态地加载一些模块,就像Linux一样有很多模块是动态加载的,像Chrome浏览器可以去应用市场下载一些第三方的插件。Mesos的模块也是基于这样的目的,比如GPU支持Nvidia显卡,那么以一种动态加载库的方式,按照用户自己的需要去加载这些模块会更好,而不是每一次编译的时候都要把所有的模块都跑一遍,包括每一次打包、编译Mesos的时候,可能会包含一些用户完全用不到功能。

把Mesos整个架构弄成模块化的优势是很多东西变成了可插拔。比如验证模块,每个公司可能都有自己的账号管理体系,并且这些账号管理体系是不想公开的,但又想把它集成到Mesos里,就需要把验证这部分模块化,用户可以编写自定义的验证模块,在用户启动Mesos的时候把这个模块的地址指定进来,而不需要再重新编译一次Mesos。

目前可以看到Mesos里面的大部分组件都已经实现模块化, Mesos在启动之前会去Hadoop集群或者FTP拉一些文件,这一部分也会模块化。Mesos模块化里有一个Module Manager,它会读一个配置文件——Module Spec JSON,在启动Mesos Agent的时候作为一个参数传进来,之后会解析这个JSON文件,按照用户JSON文件里面指定的一些参数,去load模块。这些模块可以做各种各样的事情,比如cgroup的Isolator,或者一些hook,用户在启动容器之前或者启动任务之后再做某一些事情,都是通过模块化的模式hook进来,启动的时候通过modules或者modules_dir来启动。

这是一个配置的例子, JSON文件指定模块路径以及给模块注册一个名字,命名主要是为了避免冲突。

Mesos把External Containerizer废弃之后,有些用户仍然想自定义自己的容器,怎么办呢?有一种方案是把Containerizer同样模块化。Containerizer模块化最初的目的是想支持更多的Container,比如Hyper的Container runtime就是把容器跑在虚拟环境中,包括CoreOS有一个叫rkt的Container runtime跟Docker也很类似,都是通过模块化的方式来支持更多这样的Container引擎。此外,模块化之后可以把自定义的Containerizer的实现从当前Mesos codebase里面拿出来,由些企业或者是第三方的组织自己维护,而不是每一次出了问题都要来修改Mesos。

Containerizer是一个早期的功能,因此模块化是比较繁琐的,最主要的原因是Containerizer采用了很多内部的变量包含了内部的方法,在整个代码里有一千多处引用,把这些弄出来是非常麻烦的。此外,在Mesos里面定义使用Containerizer,目前还只有Docker和Mesos,所以用户可以非常容易的指定,但是假设有更多的Container runtime,如何提供一个比较良好的接口去让用户定义Container的信息成了一个问题。另外一个要解决的问题是Containerizer的实现可能会破坏用户原有的东西。

这是我之前发在社区的文档有关接口的定义,其实就是把所有内部的变量用某一些方式把它移出去或者是替换掉,那些内部的方法会移到另外一个space里面。那么如何支持用户自定义的Container信息?就是加了一个单独的message,它包含了Containerizer的名字和一个parameter,有点类似hashmap的结构,由用户自定义参数。

目前我已经完成了这个工作,启动的时候把模块传递到modules参数里面,同时把要用的名字作为Containerizer的参数传递进来。依赖Containerizer模块化的实践有很多这样的tickets,其中一个是把Mesos里面的任务跑在KVM里面,另一个LKVM是一个Intel的轻量级的KVM,还有一个是CoreOS RTK的Container,包括Hyper的支持都是依赖这个。

以上就是黄浩松在数人云“对话Docker&Mesos”上的分享。

分享到:更多 ()