从容器规范看Docker和Rocket
CoreOS在发布Rocket时曾指出,有些人需要更“纯净”的容器。 换句话说,Rocket是“App Container Specification”的标准实现。本文从“App Container Specification”入手,分析了Rocket和Docker在技术实现上的不同。
【编者按】在“选择Docker还是Rocket做容器?为何不选择两个?” 一文中,曾提到CoreOS的创始人Polvi和Docker的创始人Sonomon都认为,Rocket和Docker没有竞争性。Docker平台是 一个产品,Rocket是一个组件。企业可以选择Docker替代Cloud Foundry,也可以使用Rocket构建Cloud Foundry。CoreOS在发布Rocket时就指出,Rocket的出现是因为有些人需要一个更“纯净”的容器。换句话说,Rocket算是 “App Container Specification”的标准实现。本文作者从“App Container Specification”入手,分析了Rocket和Docker在技术实现上的不同。以下为原文:
Docker和Rocket,殊途同归
首先,对于那些把Docker与Rocket放 在一起比较的人,我想劝你们首先适当地调查一下。如果有这样两个软件:一个已经有1年多的历史,作为一个开源项目由全世界上千的开发者共同参与,并且已经 或即将在实际的生产环境中被部署、测试;而另一个软件则是“新鲜出炉”的。如果这个较成熟的软件不是特别烂的话,那么很有可能它就是赢家。
所以,将Docker0.1.1与最近预发行的Rocket拿来比较会更公平一些。此外, CoreOS的CTO Brandon Philips也多次强调,Docker的一些功能将不计划加入到Rocket中——不只是现在,也许永远不会。为什么?因为Rocket项目的重点,至少目前的情况——不是重新实现Docker。Rocket是 “App Container Specification”的实现,因此,想比较两者,最好比较“App Container Specification”和最初的Docker清单或者Docker实现的其它规范。
以下的比较都将基于以上共识:
Systemd
通常情况关于Rocket的讨论最多的话题是systemd-spawn或者是 systemd 。 CoreOS已将systemd作为他们Linux发行版的init系统。他们可以接着使用Init 脚本或者upstar,但是他们选择使用了将来会成为所有主流Linux版本标准的init系统。实话说我并没有关于systemd独到的见解,只是读过 Lennart等人的一些文章。对于CoreOS来说,将systemd作为新的Linux发行版的init系统的是不明智的。另外,CoreOS已经获 得了我的信任,我对此深信不疑。你可能会抛出 btrfs与我争论,但是, 那个会很快消失,我希望它会被修复,或者被其他可靠的方案替换。毕竟稳定性比功能更重要。
好吧,让我们回到问题上来。其中systemd做的或者说能够做的事情多是进程管理。你可能会认为,systemd解决这 个问题的方式,多少有些重叠了服务、进程管理的概念,并且将一些原本能在PID1之外很简单的任务复杂化了,但因为CoreOS是systemd的忠实用 户,如果使用其它工具来实现反而显得奇怪。
此外,据Brandon介绍,Rocket 的最初实现使用systemd-nspawn的原因是他们想用systemd,Systemd-naspawn 已经实现,而且正在做他们想做的东西,所以它有助于项目的开始。坦率地说,在0.1.1版本,我并不关心他们使用的技术,没准以后都会变的,因为 Rocket的设计是可插拔的。如果你不喜欢systemd-nspawn,可以用你自己的stage1实现,Rocket已经提供了详细的命令行参数。
另外需要指出的是,那些认为想使用Rocket,就必须运行systemd的人就完全错了。Rocket根本不需要 systemd。它也能与其它init系统,如SysV或upstart配合工作。我在upstart上测试了Rocket,没有遇到任何问题。 Rocket仅仅是“重用”了systemd和systemd-nspawn,处理stage1和stage2。
App Container
有些人可能还没有准确地理解“App Container Specification”。他们一直在说systemd, 并说Docker是如何好,不需要在容器中用systemd运行进程。Docker通过后台进程管理单进程的容器。Docker后台进程未被写成一个进程 管理工具,当你的容器停止时,这种设计的弊端就会显现出来。你最终要通过主机或者容器中的进程管理工具来应付它。如果你还没有经历过这样的事,要么你可能 是幸运的,又或者没有在环境中运行大量的容器。
我们需要进程管理,或进程管理提供的某些功能。如果读过“App Container Specification”,你会理解这点的。我将其中一部分摘录如下:
容器执行一个或多个应用程序,共享PID namespace、network namespace、mount namespace、IPC namespace和UTS namespace。执行之前,每个应用程序将开始转为(比如chroot)自己特有的读写根文件系统。容器的定义是,包含一系列应该在一起启动的应用,以及应用与整个容器的隔离器。
上面明确提到了几个进程共享Linux的命名空间。换言之,Kubernetes的Pod与以上对容器的定义很像,我不知道这是否与CoreOS正在积极参与Kubernetes的开发有什么关系。但Kubernetes Pod是一组容器,而不是容器中的一组进程。这让你能从多个Docker镜像组成一个Pod中获益。这是App Container Specification定义了 依赖关系的地方,所以你的容器可以依赖于其他容器,因此通过 Stage0创造的最终容器runtime可能是 这样的。
现在,你已经在容器中运行多个进程,其中一些可能是后台进程,并且可能需要被监督,你需要一个进程管理器。至今我用过最好的管理器是runit,但话说回来,当你已经积累了大量关于systemd的经验时,你为什么会想要写runit脚本或使用其他管理器?如果我是CoreOS,也会做出一样的决定——systemd。
Docker和命名空间
现在,让我们回到Docker。Docker一直倡导一个容器运行一个进程。总的来说,我同意这一点,因为Linux容器 主要是为了实现主机的进程隔离,而每个容器运行一个进程,会给你带来更多的灵活性和复合型,独立的更新、回滚等,操作更加简单。然而,当你创建一个新的 Docker容器或事实上LXC容器,容器被重新分配一套由libcontainer提供的全新的Linux命名空间。实话说,我觉得这有点浪费。于Docker还挺有意义,因为你要在一个容器中运行一个单独的进程,并且如果你想提供更通用的进程环境,来覆盖大多数的用例,你可能需要libcontainer高效提供的大量可用命名空间。
只为一个进程创建一组命名空间,增加了内核大量的额外管理工作。如果你的主机上运行了很多的容器,可能会出现内核某些方面的瓶颈。你可能会说开销很小,但是为什么非要过度占用内核呢?
因此,为了节省开销,可以共享进程之间的命名空间。但是有个问题,如果你开始共享命名空间,并且创建它的容器已经终止,它会删除共享命名空间的所有的进程。Kubernetesde在设计Pod时,就考虑到这个问题了。 Kubernetes Pod中第一个被创建的容器,会被从internal/24 VLAN中分配一个IP地址,该IP地址也被Pod内共享一个network namespaces的容器共享。
你可以看到这里微妙之处。每当“网络”创建的容器停止时,他会撤销Pod中所有的其它容器,因为没有可以共享的命名空间 了,所以需要从头创建新的Pod,重新分配IP。更糟糕的是,你的连接也没了。所以,当你的容器停止后,不能自动重启它们。我相信通过观察Docker销 毁容器的过程,可以找到解决办法,但是,那有些绕。所以,如果你想要在Docker容器中运行多个进程,你会需要一个进程管理工具。
监管日志
最后,讲一下日志。日志由管理进程负责。通常这个进程管理器通过捕获进程的stdout/stderr,并将其记录到一个日志。“App Container Specification”上有关日志的说明如下:
应用程序应登录到stdout和stderr。容器执行程序负责捕捉和不断输出。
对于CoreOS来说,systemd似乎是一个显而易见的选择。Runit在这方面做得很好,但这又回到我已经在前面提到的,专业上来讲,这是不必要的额外工作。
Docker的做法是,把近六个月的日志记录到日志插件prosposal文档中。虽然还未实现,但考虑到这是由Michael Crosby提出的,我对它有信心。
用户体验和使用经验
大家对Rocket的另一个不满在于用户体验。但请记住,Rocket最新发布的版本是0.1.1。你知道第一辆车的样子吗?它看起来是这样的。这当然不是最好的用户体验,然而这是朝着法拉利和保时捷的第一步。
但同样也是最重要的是,Rocket是App Container Specification的实现,不会强加给你任何东西。同样,Dockerfiles、Docker daemon以及其它你想要的实现,都只能靠你自己了。甚至是Stage1!你想让Docker作为管理器?破解它,并把它作为参数传递给rkt run,成为一个Stage1进程。我都能想象在未来,CoreOS很可能会实现一些很棒的工具,甚至提升Ops 和Devs的用户体验。但那些工具可能会作为单独的项目,而不是Rocket的核心部分。在这一点上我认为最重要的是项目的稳定。
结束语
我要十分明确地说,我喜欢Docker,并且一直在关注Docker。这篇博文并不是在讨论Docker或Rocket好与坏。大多数人都是没有读过“App Container Specification”,才会总是要比较这两者的好坏。
最后,强烈建议你去了解一下Docker和Rocket。如果你喜欢用C编码,你也应该试试LXC或其它类似的技术。还有很多事情可以做,很多机会,让你成为目前这个行业巨变的一部分!