在 Swarm 被纳入 Docker 1.12后,Swarm 与 K8S 之争日趋白热化。本文作者 Adriaan de Jonge 身为 Xebia CTO ,专精 DevOps 及持续交付,曾为 Docker 摇旗呐喊三年的老司机,如今开始易帜。
现在即使是最酷的产品也绑定于某个特定供应商。不管在过去的三年里,我曾多么热衷 Docker,但是现在,绑定的供应商开始动摇 Docker 在我心中的地位了。而目前他们两者之间的竞争还在持续。又或许在这个过程中会出现一个更好的选择也说不定。这篇文章就是带领大家来了解一下 Core OS 的 rkt(读音按照“rock-it”来),以及告诉你为什么从现在开始要了解 rkt。
Docker 怎么了?
如果你跟我经历相似,你可能因为对 Docker 充满热情而被它的一些缺点蒙蔽了眼睛。不要误解我,我并不是说 Docker 不好。我的意思是它并没有那么完美。所以接下来让我们来看一下它的一些不足之处吧。
Docker 越来越像以前的 Microsoft
一旦一家公司意识到它有着自己的垄断权,他们就会开始做出垄断行为。一如微软曾经试图通过在 windows 中安装 Internet Explorer 来排挤 Netscape,现在 Docker 正在尝试将 Swarm 融入到 DockerCore,以此来打击 Kubernetes,Mesos,Marathon 和 Nomad。
Docker 确实有简化 Swarm,使它轻松被广大 Docker 用户接受的优点。如同微软确确实实提升了 Internet Explorer6.0的性能。MSIE6浏览器突出了微软的优势,所以他们在5年内都没有继续开发了。
想象一下 Docker 跟 Swarm 结合的结果是什么……想象一下你的下一个任务就是移动到 Docker:当需要选择一个最好的调整程序时,在你选择 Kubernetes,Mesos/Marathon,Nomad 之前,你首先要理清楚的就是 Swarm 的不足之处是在哪里。已经有了 Swarm,那么为什么在这个基础上还要安装其他的调度程序呢?
我希望 Docker 能够提升 Swarm 的性能,在修改这一点上,希望 Docker 做得比微软(修改 MISE)要好。但是即使是现在,Kubernetes 在很多方面也都是领先于 Swarm 的。随着 Kubernetes 的持续开发,Swarm 已经远远落在后面。虽然 Docker 能够让人轻松使用调度程序,但是在我看来,他们还是应该跟 Docker 核心分开。
“Docker 的架构从基础上来看就是有瑕疵的”
Docker 的核心就是 Daemon 程序,这个程序是 Docker 开始运行的起点。Docker 可执行文件仅仅只是一个 REST 代理,这个代理要求发请求给 Docker daemon 来完成它的工作。Docker 的评论家指出,这很不符合 Linux 的方式。
它的痛点在于你是否使用类似 systemd 这样的初始化系统。因为 systemd 并不是针对 Docker 设计的,所以当你尝试开启一个 Docker 程序的时候,你其实也就是开启了一个 Docker 代理程序,而这个代理程序向 Docker daemon 请求开启真正的 Docker 容器。在这里还是有一个风险的,就是当 Docker 容器还在运行的时候,Docker 代理运行失败了。在这样的情况下,systemd 得出结论,程序已经停止,并且重启 Docker 代理——于是创建第二个容器(是的,你还是可以继续处理工作,但是已经是无关紧要的了)。
大概一年之前,我把 systemd 当成是一个简单的 Docker 调整程序来调查。我遇到了同样的危机,或者说将 Docker 和 systemd 结合的问题。那时候,我觉得这些是 systemd 的原因,而不是 Docker 的缺点。我那时候还想,会不会 systemd 并不是针对“Docker 本地”来设计的。
现在,我才了解到,可能 Docker 才是那些问题的症结所在,而不是 systemd 的问题。就像上文中提到的,Docker 并不符合 Linux 的风格,而也正因为如此,Linux 工具跟 Docker 也协作得不是很好。所以是谁错了呢?是新来的 Docker,还是已经存在了多年的 Linux 工具呢?CoreOS 的 CEO,Alex Polvi说:“Docker 的架构从基础上就有瑕疵。”
Docker 创建程序,陷在了第二个阶段
Docker 最好的功能之一就是 Dockerfiles 的引入,你可以在构建 Docker 镜像时保持版本控制。这里唯一的问题是在 Docker 的路标中 Dockerfile 语法冻结很长时间了。这就意味着在过去的一年半以来 Dockerfile 格式并没有依据社区的良好建议而进化。
当然,在某种程度上,我们需要一个稳定的格式,但是只有当格式完全成熟的时候。在这里举个例子,就是 Dockerfile 缺乏的地方,考虑到 Kelsey Hightower 在他博客《12 Fractured Apps》中提到的:
“记住,我们要运送artifact,而不是编译环境。”
事实就是,Dockerfile 格式并不能很好地支持这个区别。考虑到要构建一个可执行的 Golang:可执行的结果可能只有两百万字节大小,但是创建的环境有几百兆字节。当然,你也可以在本地系统上构建镜像。你无法利用 Docker Hub 中的自动化构建环境通过一个简单的 Dockerfile 优雅的构建出一个小的 Golang 镜像。社区有个关于扩展 Dockerfile 格式的提议就能很好的支持上述原理,但是由于 Dockerfile 格式的冻结该提议也被搁置了好几年。
那么,rkt 是如何缓解这个状况的?
简而言之,rkt 现在给 Docker 提供了更好的解决办法。它拥有一套更加 Linux 的架构。这个强劲的对手会保持自己的垄断作风。
具体一点的答案就是:2014年年底,Core OS 宣布,这个有竞争力的容器平台 Rocket(后来简写并改名成 rkt)的诞生。虽然 rkt 在发布之后最初几周里得到了很高的关注度,但是后来却销声匿迹了。Core OS 还是将 rkt 作为 Docker 的可替代工具来开发,后来在2016年2月 rkt1.0版本发布的时候才回归大众视野。
现在 Docker 宣布在 Docker Engine1.12中已经包含了 Swarm,那么是时候正视 rkt,让它作为 Docker 的可执行方案了。那在未来它会替代 Docker 吗?从 Docker 切换到 rkt 难度大吗?让我们来看一看 rkt 寻找答案吧……
rkt 能够运行 Docker 镜像
假设现在你想要用 rkt 来替代你的 staging 以及产品系统,同时又想让你所有的开发系统都继续运行……在这个案例中,你可以只在你的运行系统上将 Docker 替换成 rkt。但严格来说,这并不是一个随随便便的替换。毕竟,架构是不一样的——但是我们会努力往一样的方向发展的。另一方面,为了在 rkt 上运行 Docker 容器而去学习新的命令行指令也不会太难。在你最爱的云提供商上打开 Core OS 实例,并打出以下代码:
或者用你最爱的 Docker 镜像来替代 nginx。在底层,rkt 将 Docker 转换到ApplicationContainer(appc)格式。
这也就意味着你不需要 Docker 来运行 Docker 镜像了。而且也意味着你可以重新使用 Docker 创建的东西,在这个过程中不需要忍受迁移带来的伤痛——而不是学习 rkt 的命令行语法。
让我们来一睹命令行的单个部分:
如果你没有忽略这部分,那么rkt就会拒绝启动你的镜像,因为它找不到签名(.asc file)来确认 Docker 镜像的完整性。rkt 默认设置下是安全创建的。在这个例子中,这也就意味着你可以通过提供合适的签名来省去一些命令的输入。
就比如在 Docker 中,你需要明确地暴露端口到外部。不像 Docker,rkt 端口已经被命名,而不是标记数字。如果这是一个原生的 rkt 镜像(或者需要精确的appc),那么它读起来很可能是这样的:
然而,既然 Docker 没有命名的端口,那么被暴露的端口就会被自动命名为-。这也就意味着,如果你只能显示暴露端口的话,就使用 Dockerfile 中的 EXPOSE 关键字:
镜像期望存储在默认的 Docker 仓库中(Docker Hub)。除了这个例子,还有个更长的 URL 指向另一个 Docker 仓库(docker://)或者包含镜像的通常的 web 网站(https://),但是之后你就不再运行 Docker 镜像了。
Rkt 有着更加简单的架构
在 rkt 中是没有 daemon 进程的。Rkt 命令行工具做了所有的工作。来看从CoreOS 网页借鉴的图片:
跟 Docker 不同,如果你使用 systemd 来启动 rkt容器,你实际上就是在监控容器进程,而不是监控代理程序,然后由代理程序连接到 daemon,并由 daemon(直接或者间接——依赖于你的 Docker 版本)来启动容器。
另一方面,你不能这么写:
像 Docker 代理一样 daemon 化进程。而且必须要运行一个初始系统来 daemonize 进程。比如,你可以这样运行:
同样,你无法从远程机器(比如 Docker 代理)运行 rkt 命令行。再有,你也可以将它作为安全功能考虑。
Rkt 为镜像遵从开放标准
现在,rkt 允许你使用 Application Container(appc)或者 Docker 镜像。在不远的将来,Open Container Initiative(OCI)格式将会被加入,我们会朝那个方向走的。
容器镜像拥有开放标准的好处就是,它允许开源社区提供多种创建镜像的方法,不仅仅局限于 Dockerfile 格式。
构建 appc 镜像的默认方式就是使用命令行工具,叫做 acbuild。说实话,喜欢 acbuild,还是喜欢 Dockerfile 格式,这真的只是个人口味问题。好处就在于它天生就跟 Linux 原则距离更近。而最大的好处就是开放格式允许可替代的构建机制。比如,考虑到全能创建工具 dgr 或者 Golang 指定创建工具 goaci。一旦 OCI 格式可用,就会有更多可能,但是现在,我们还是需要等待。
Rkt:我们到达目的地了吗?
如果你现在就想要开始使用 rkt,那么你在使用道路上会有一些磕绊。虽说你可以在磕磕绊绊之中找到方向,但是不得不承认的是,在我写这篇文章的时候(也就是现在),说 rkt 一切都已经准备妥当还为时过早。不过,我相信,只是时间问题。
OCI 镜像格式还没有准备好
就像在上文中提到过的那样,rkt 支持 Docker 镜像格式,而且跟 Docker 仓库互相沟通联系。如果你能够坚持使用 Docker 镜像格式,那么相信它,它会运行得很好。
但是,如果你是个吹毛求疵的人——像我似的——你就没办法容忍在使用 Docker 的时候,你还无法使用命名的端口。所以,你就会想要使用 appc 容器。appc 容器是可以自由使用的。但是你要怎么上传 appc 容器给 Core OS 解决方案到 Docker Hub——quay.io?我自己是还没有找到这样的方法。
关于 appc 格式最好的一点就是,它并不是跟特定的 Docker 仓库相联系。反而,你可以在普通 http 服务器上,或者是在本地文件系统宿主镜像。你可以丰富包含元标签的 HTML 文件的原数据。
一旦 OCI 镜像格式达到,rkt 才有可能真正 take off,变得足够成熟,准备足够充分来被接受,作为每个人心中的标准——包括 Docker。
Nomad&Kubernetes 还没有完全成熟
为了在产品中运行容器,在某种程度上,你需要一个调度程序来控制运行什么程序,在哪里运行。目前,Kubernetes 和 Nomad 都支持 rkt。但是这种支持并没有大家期望得那样成熟。
Kubernetes 支持 rkt 是从积极开发的状况下来说的。它有最小文档以及一系列已经知道了解的问题。Nomad 支持标记试验,但是不支持动态端口。
对于其它平台来说就不那么轻便
好在 rkt 不仅仅支持 CoreOS。你可以在多个平台上,比如分布式 Linux,Debian,Ubuntu 以及 Fedora 上安装 rkt。
如果你想在你的 Apple/Windows 开发机上运行 rkt,你当然可以在像 virtualBox 这样的虚拟层运行。但是,这些设备没有 Docker 的工具盒那么好用——特别是最新的 Docker Mac/Windows 测试版,这也就给了你一种 native 的感觉。要承认的是,这可能就是 rkt 架构上的缺点,在这点上,可执行的 rkt 已经做了所有的工作,而不只是作为一个 REST 代理。
如果你想要在非 Linux 机器上开发,最好的办法就是仍然在本地用 Docker 镜像进行处理,并且在你转到 staging 和生产的时候,要将这些转化为 appc 镜像。
结论
虽然这么说还很早,但是 rkt 现在已经变成 Docker 的一个可执行方案。如果你不需要 Kubernetes,Nomad 的动态功能,以及更多像 systemd,fleet 这样的静态选项,来满足你的调度准则,那么你现在就可以将你的 staging 和产品服务器转移到 rkt 了。
相信再有多一点的时间,Docker 和其它容器平台间会有真正的互操作性,以 OCI 镜像的形式出现。同时,允许 Kubernetes 和 Nomad 支持 rkt 的发展,然后 Docker 会跟 rkt 容器平台会一样可交换。
那现在有必要摒弃 Docker 吗?不,没有必要。除了这篇文章中提到的一些 Docker 的缺点,Docker 还是个有着很多开发工具,调度程序,编排的巨大生态系统创新平台。重要的是,我们要有足够多的选项。现在 Docker 有很多的竞争对手来保持清醒。