DockOne技术分享(四):AppC和Docker的对比


【编者的话】 现在一说到“容器”,几乎所有人首先想到的就是Docker。Docker作为目前最主流的容器标准,掩盖了许多前辈和后续者的光辉。事实上,Docker既不是第一个容器类产品(OpenVZ、Lxc等都远远早于它),也不会是最后一个。今天我们来聊一个最近有点火的新容器标准:AppC。

AppC是 CoreOS 公司在2014年12月发起的社区项目,旨在设计一种新式的跨平台容器在镜像格式、运行方式和服务发现机制等方面的标准。

从官方的表态说,这个项目最初诞生的原因是,主流容器工具 Docker 正在从一个单纯的容器工具成为自成一体的生态圈。而 Docker 的中心式管理方式(由每个主机上的 Docker -d 后台进程统一控制)对于 Systemd 以及第三方的任务编排工具并不友好(具体原因稍后分析)。

因此 CoreOS 希望设计一种去中心化的、功能更纯粹、同时更关注性能和安全的应用容器,将诸如日志管理、容器调度、集群编排等工作都交给外部工具去完成。

Docker 的发起者是 DotCloud 公司,这个公司原本的业务就是做 PaaS 平台,这也决定了 Docker 的发展方向就是成为一个“大而全”的平台性质的产品。这种做法本身也符合占市场绝大多数的“大众用户”希望“一站式解决方案”的诉求。

可以这样说,两者本质的出发点和想解决的问题并不相同。因此,希望大家秉着开放和包容的心态来看待 Docker 和 AppC 的比较。而不是抱着选择“哪一个是最好的容器标准”的目的,因为这个问题可能和“哪个编程语言是最好的语言”一样,只能交给时间去定论。

现在符合 AppC 标准的容器已经有几款了(都十分的小众),不可能挨个对比。下面就只以 CoreOS 官方推出的 Rkt 容器作为与 Docker 的比较对象。以下内容都属个人测试的结果,不代表 Docker 或 CoreOS 任何一方的官方观点。

由浅入深的说。首先是最容易感觉到的一些差异。

操作命令的方式

1.png

可以看到,Docker的所有命令都包含在了 Docker 这一个命令行工具中。
而 Rkt 中与镜像相关的命令是由 AppC 统一的命令行工具 actool 提供的,而容器本身的操作由各个容器自己提供,例如 rkt。

Docker 的容器停止以后还会被后台的 Docker -d 进程记录,还能够再次 restart,而 Rkt 的容器里运行的应用则更像是一个普通的应用,结束了就真的结束了。

另外也能看出,Rkt 提供的功能只是 Docker 的子集,后者在日志、镜像、管理方面提供的支持完整很多。

然后,来看一下 Docker 和 Rkt 的一个十分重要的差异。

容器进程运行方式

Rkt 的每个容器进程由 rkt 命令直接 fork 出来。而 Docker 的容器进程是由用户执行的 Docker 命令行工具发送消息给后台的 Docker -d 进程,由 Docker -d 进程间接 fork 的。这咋看起来没什么区别,但是 Docker 的这种方式就给第三方的任务编排工具带来了一些困扰。
2.png

3.png

有些进程管理工具,比如 systemd 是通过 cgroup 来控制和跟踪进程的生命周期的,而 SysV 则是通过进程号和继承关系来跟踪进程生命周期。比如说当需要结束一个进程的时候,进程管理工具就会找到相应进程的所有子进程一起结束。

但 Docker 的这种进程运行模型,使得不论是通过进程树还是 cgroup 树都无法看出创建容器的 Docker 命令行与容器本身有任何关系。因此许多任务管理工具如果不对 Docker 进程进行特别对待,就无法正常的管理通过 Docker 启动的容器里的进程。

看镜像的结构

Docker 导出的镜像是一个 tar 文件,里面的内容是分层的。
4.png

5.png

解压以后的每一个目录其实就是镜像的一层。
6.png

Rkt 的镜像是一个 gzip 文件,里面没有分层,直接就是所有容器中的文件的打包。
7.png

8.png

个人觉得分层以后虽然复杂了一点,但能够复用空间,其实是比较有用的。

最后来看一下 AppC 比较强调的安全性和性能方面。

安全性

Docker 下载一个镜像,直接 Docker pull 了事,传输过程可加密可不加密,Docker 也不会问你是否了解这个镜像的来源。
9.png

而 Rkt 默认要求所有传输过程加密,并且下载镜像前用户必须首先添加对镜像来源签名的信任,除非用户明确指定取消验证,否则镜像即使下载了 Rkt 也会拒绝运行它。
10.png

11.png

容器性能

性能方面,可以从一个侧面来说明:构建一个最小的可运行镜像需要哪些东西。

下面的 hello 这个文件是一个静态编译(无任何非系统库依赖)的程序,运行的时候会打印一个“Hello World”。

把它放到一个空白的 Docker 镜像里面。
12.png

运行容器会发现 Docker 提示找不到 Bash。
13.png

将同样的程序放到一个空白的 AppC 镜像里面。
14.png

运行容器就能够正常工作。
15.png

这一点看出 Docker 在运行容器里的任何程序的时候,实际上都会先在容器里面启动一个 Shell,再由这个 Shell 去容器执行指定的命令。而 Rkt 则是纯纯的直接运行了指定的程序,相比之下在性能和镜像内容的“信噪比”上略占优。

如果对AppC和Rkt容器感兴趣,可以参考我在InfoQ的这篇文章:CoreOS那些事之Rkt容器尝鲜(上)

Q&A

问:关于镜像的问题是否可以基于本地源来创建?我个人觉得本地源对于企业来说很重要。
答:AppC的镜像创建方式不太一样,现在还不支持用编写Dockerfile这样的方式创建,而是把文件放到指定结构的目录,然后直接用actool工具打包创建。

问:能支持不同的发行版?
答:AppC是跨系统的,只是一个标准。Rkt可以在任意的Linux发型版使用。

问:Rkt的最小镜像是?
答:Rkt的最小镜像可以小到里面只有一个可执行程序。

问:“信噪比”的概念不是很明白。
答: 内容“信噪比”就是说实际用户要的文件和其他辅助文件的比例。

问:Rkt容器可以起多进程吗?
答: 和Docker一样的,入口命令只能一个,里面可以后台运行多个进程。

问:“但 Docker 的这种进程运行模型,使得不论是通过进程树还是 cgroup 树都无法看出创建容器的 Docker 命令行与容器本身有任何关系。因此许多任务管理工具如果不对 Docker 进程进行特别对待,就无法正常的管理通过 Docker 启动的容器里的进程。”这里提到的任务工具对Docker进程特别对待,通常需要怎么处理,才可以方便用户的任务工具对Docker启动容器里的进程进行管理?
答: 就是说在比如结束容器进程的时候,不能通过启动容器的命令所在的pid或者cgroup来跟踪容器内的进程,而要和Docker后台服务进程去取。

问:那Rkt的日志网络等是怎么处理的呢?
答: 这个Rkt就撒手不管,让用户自己选择其他工具,比如Kubernetes或者Mesos这些框架都可以用,AppC目的就是做纯粹关注容器本身功能(环境隔离)的容器。

问:Rkt运行自己的镜像和Docker的镜像有什么区别吗?
答:镜像格式上有些区别,不过其实两者应该是很容易转换的,Docker容器到AppC容器转换的工具已经有了,反转的还没有,估计Docker现在自己是主流,也没打算做这种事情。

问:Rkt有提供Restful的API吗?
答:没有,因为要实现Restful API就必需要有一个常驻后台的进程,这样就违背了Rkt做纯粹的容器工具,并尽可能的减少对系统入侵性的设计意图了。

问:感觉 Rkt+CoreOS 和 Docker+Machine/Swarm/Compose 这两种组合都在做集群的事情?
答:是的,其实是存在一定竞争关系的。只不过CoreOS觉得Docker越做越复杂,不符合他们的应用场景,就新建了一套,顺便改进一些Docker的小毛病。但CoreOS依然会对Docker解决方案继续提供长期支持。

问:Rkt能使用Docker的镜像吗?
答:能,Rkt支持直接下载DockerHub或其他Docker Repo的镜像,只是下载后会自动转换成AppC格式存储到本地。

===========================

以上内容根据2015年5月19日晚微信群分享内容整理。分享人林帆,ThoughtWorks 成都 Cloud&DevOps 小组成员,目前主要研究内容是应用容器化和CoreOS系统相关领域。近期主要文章有CSDN的《CoreOS实践指南》系列,InfoQ的《CoreOS那些事》系列,和程序员杂志2015年5月刊的《Linux容器:Docker vs Rkt》等。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加我微信(liyingjiesx)参与。

3 个评论

有两个问题:
1 在容器进程运行方式的部分中, rkt容器进程属于运行rkt的sshd进程的cgroup,是不是意味着,一旦我关闭了ssh的shell,容器就自动被关闭了, 但是由于docker容器是由docker daemon在直接维护,反而没有这样的问题?

2 rkt的容器应该是systemd和systemd-nspawn创建,我对systemd不熟悉,有可能systemd或者system-nspawn一旦crush或者restart,rkt容器随之关闭吗?如果是这样的话,这和coreOS所诟病docker daemon一旦重启就会导致容器全部关闭的问题又有什么区别呢?
1. 是的。如果是从Shell启动的rkt容器,Shell关闭了,rkt容器也就跟着结束了。这样就使得通过容器隔离运行的程序和普通的应用程序行为上是一样的。因此在管理容器封装过的服务就可以用管理普通服务一样的办法来处理,比如做成Daemon Service或者交由其他管理工具调度,而不需要特别的处理对待。
2. 不会的哈。rkt目前默认的隔离实现是使用的现成systemd-nspawn命令,而systemd-nspawn实际上只是一个[操作namespace的工具](http://linux.cn/article-4678-1.html)而已。就是说通过这个工具让进程运行在独立的namespace(也就是我们常说的容器)里面,至于启动以后就基本没systemd-nspawn什么事了,namespace本身是内核提供的功能。
另外,systemd是系统的PID=1的,是所有其他进程的父进程,如果systemd发生crush,整个系统也就crush了,可以不必纠缠于这种情况。system-nspawn是一个命令工具,不是后台服务。因此rkt的容器本质上确实是没有后端daemon的。
谢谢你的回复

要回复文章请先登录注册