吐槽:Docker真的好吗?
本文是一篇对Docker“吐槽”的文章,作者从Dockerfile、缓存、分层文件系统、Docker Hub、安全、容器和虚拟机几个方面入手,阐述了Docker和容器技术目前存在的一些问题,以至于说Docker的存在并没有必要。大家可以把这篇文章 的观点作为对Docker认识的一个补充,对Docker有一个更加客观的认识。
概述
距离我上次发表对Docker的看法已经一年了,那个时候我狠狠的批评了Docker在架构上的缺陷以及其糟糕的用户体验。虽然现在项目已经发展到1.0,但是还是得到了一些来自亚马逊的不满,用户失望程度也在不断增加,面临大量的指责,甚至还存在一些可能会导致主机污染的漏洞。然而Docker Hub上个人私有仓库的引入,使得用户自己不需要再运行个人的Registry,再加上webhook与GitHub整合,所有这些看起来是一个良好的开始。
于是我决定再给Docker一个“机会”,并且把其投入到生产中运行六个月,看看效果怎样。结果真的令我很失望,在使用过程中,Docker表现 很令人失望,不但性能糟糕,而且由于其本身功能的不足,我们还需要在解决方案上不断地进行变通,整个过程的用户体验也不尽如人意,这使得我几乎想要把自己 的脸撞碎在桌子上!事实上,在我看来Docker的性能的确很糟糕,以至于缓存被禁用之后编译过程变得更快。(看看reddit和hackernews上面讨论)
Dockerfile
Dockerfile有很多问题,在我看来,他很丑陋,有很多局限性,有的地方甚至相互矛盾,并且有很多根本性的缺陷。让我们来说说,假如你想创 建单个仓库的多个镜像,例如第二个镜像包含了调试工具, 但是对两个镜像的基本要是是一样的。Docker不支持这样操作(per #9198), 当前并没有能力去对Dockerfile进行扩展(per #735),使用子目录的话会破坏创建的上下文以及阻止你使用ADD/COPY(per #2224), 就像管道一样(per #2112),在容器构建的时候(build time)你也不能够使用环境变量根据不同的运行条件来改变指令(per #2637)。
我们的变通方案是创建一个基础的镜像,两个指定环境的镜像,以及一些包含重命名和sed替换的Makefile自动化脚本。还有一些意想不到不到的“特性”导致$HOME环境变量消失了,还会产生一些没用的错误信息,使用起来的确很不方便。
Docker 缓存/分层
Docker可以使用COW(copy-on-write)文件系统来缓存Dockerfile指令, 类似于LVM的快照,到现在仍然仅仅支持满是问题的AuFS(AnotherUnionFS)。为了提高稳定性以及性能,之后在0.7版本中对COW做了不同的实现, 你可以在这里了解到详细的情况。但是这个缓存系统并不智能,导致了一些令人惊讶的情况,比如不能阻止某一个指令被缓存(per #1996)。并且很慢, 鉴于这一点,如果你禁用缓存或者是避免使用分层,创建的速度反而快一点, 在Docker Hub上传和下载的时候这个表现的更严重,详情会在下面部分进行描述:
这些问题都是由Docker的架构设计导致的,Docker作为一个整体,总是线性的执行指令,即便是在很不合适的情况下也这样 (per #2439)。作为一个改变慢速创建的变通方法,你可以使用一个第三方支持异步执行的工具,例如Salt Slack, Puppet,甚至Bash,这样的话就完全放弃分层的思想使其毫无用武之地。
Docker Hub
Docker鼓励通过Docker Hub来进行社交合作, 允许你发布自己的Dokerfiles,不管是公有的还是私有的,这样其他人可以基于此来通过FROM来进行扩展,而不是拷贝,粘贴。这个生态系统类似于AWS marketplace的AMIs,以及Vagrant的boxes,原则上说是非常有用的。
然而由于一些原因,Docker Hub在实现上是有缺陷的。Dockerfile不支持多FROM指令(per #3378, #5714 and #5726), 这就意味着你只能继承自单个的镜像。并且他也没有强制版本化, 例如dockerfile/ubuntu:14.04的作者可以替换掉这个标签的内容,这就相当于在使用了没有强制版本的包管理工具。另外在后面也会提 到,Docker Hub在速度上的缺陷也很令人失望。
Docker Hub也有一个自动构建的系统,它可以监测仓库中新的提交,并且触发一个容器的创建。因为很多原因,这个功能基本上是没用处的。创建服务没有可定制化的功 能,甚至连最基本的前/后脚本钩子也没有。它还强制使用一个指定的项目结构,希望在根目录中只有单一的Dockerfile,导致创建过程变得相当的慢, 也破坏了我们之前提到的创建的变通方法。
我们采用的变通方法是使用CircleCI, 一个持续集成(CI)平台,可以触发基于Makefile的Docker创建, 并推送到Docker Hub。这个并不能解决速度慢的问题, 唯一的解法就是使用我们自己的Docker Registry,这样做确实有点复杂甚至可笑。
安全
Docker原来使用LXC作为默认的执行环境,从0.9之后采用libcontainer作为默认的执行环境。当使用合适的执行驱动(exec-driver)时,引入它来调整命名空间的能力,权限以及使用定制的LXC配置。
Docker需要一个根守护进程一直在主机上运行,并且有很多安全漏洞,比如CVE-2014-6407和CVE-2014-6408,非常坦率的说,这还不应该排在第一位。即便是Gartner, 根据他们可怜的评估记录,也表达了对Docker不成熟,以及安全问题的担忧。
Docker,从设计上来说,对namespace能力过 分的信任,但实际上namespace比一般的hypervisor暴露的攻击面要大很多,Xen在Linux里面有129个CVEs(Common Vulnerabilities and Exposures),与之相比它却有1279个。当然,这在某些情况下也是可以接受的,比如在Travis CI里面以公有的方式来构建, 但是在私有情况下和多用户的环境下,就显得比较危险了。
容器不是虚拟机
namespaces和cgroups是非常强大的,允许一个进程及其子进程有一个共享内核资源的私有视图, 例如网络栈和进程表。这种粒度的控制和隔离,加上chroot jailing和grsec, 可以提供一个很优秀的保护层。 有些应用, 例如uWSGI, 直接对这些优点加以利用,而不是通过Docker。 还有些应用不直接支持namespaces的可以用firejail封装成沙箱。如果你觉得很冒险的话, 你也可以直接在你的代码里面支持namespace。
容器化项目,诸如LXC和Docker, 利用这些特性,可以高效的在一个相同的内核空间中运行多个linux的发行版。与hypervisor相比,它们有时候会有一些优点,比如占用更少的内存并且启动速度更快。 但是这是以损失完全性,稳定性和兼容性为代价的。这里有一个跟Linux内核接口有关的边缘情况, 在内核和用户空间运行非兼容的和没有经过测试的glibc版本组合会导致一些不可预料的行为。
回到2008年, 当LXC被设想出来的时候,硬件辅助的虚拟化才有几年的时间, 很多hypervisor有性能和稳定性的问题,这样的虚拟化并没有被广泛的使用,也只是为了降低花费和减少物理机而做了折衷。但是现在 hypervisor的性能已经可以和物理机器一样快了,有趣的是,在一些情况下可能更快。运行自定义的虚拟机也变的更快更便宜,随着DigitalOcean在性能和花费方面不断的超越EC2, 使得以一对一的方式运行应用和虚拟机,在财政上变的成为可能。
Bryan Cantrill在这里指出, 虚拟化的性能主要取决于工作负载的类型,比如IO很重的应用会导致更低的性能。
对于有些特定的情况,使用容器化是正确的选择, 但是除非你能很明确的解释为什么你要使用容器,否则你便可以使用hypervisor来代替。即便是你使用了传统的虚拟化,你也可以直接应用namespaces的优势,诸如firejail就可以帮助你的应该在缺少本地支持的情况下来实现这样的特性。
Docker是没有必要的
Docker增加了一个复杂的入侵层,这使得开发,故障排查以及调试变得非常困难, 常常制造的问题比解决的问题还多。这对部署没有一点好处,因为你仍然需要使用的快照来达到自动扩展的目的。更糟糕的是,如果你不使用快照的话,你的生产环 境的扩展需要取决于Docker Hub的稳定性。
这个已经被baseimage-docker这个项目滥用了,这个镜像试图通过运行init.d作为入口使得检查、调试,以及兼容变得更容易,它还尝试提供给你一个可以用ssh登录的服务器,从而把一个容器看成是一个虚拟机,尽管作者使用了很无力的论据去反驳这一点。
结论
如果你的开发工作流非常的健全,那么就应该已经明白Docker是没有必要的。所有它宣传的特性要么是没用的要么就是实现的非常差,并且它主要的特性直接可以使用namespaces来完成。Dokcer本来应该是8年前的一个很可爱的想法,但是今天已经没什么用了。
更正/修订
表面上看,Docker有很多值得关注的地方。它鼓励开发人员围绕一个一成不变的部署流程, 可以快速的,简单的开始一个新的项目,还有些其他的人认为有用的东西。但是需要提醒大家的是,本文是聚焦在一个每日的,长期使用的Docker上面,包括本地和生产环境。
尽管大多数提到的问题都是显而易见的,本文并没有为Docker如何变得更好提出什么建议。对Docker来说有很多解决方法,毕竟不管什么项目都是有优缺点的,我会在接下来的文章中作详细的解释。
a-ko对使用容器化有个长期的讨论,markbnj也有一个详细的技术反驳,这两个都是很有用的。
我想对所有给他们反馈的人说声谢谢,看到大家很喜欢我的写作风格感觉非常的高兴,我也读了几个高级工程师的回应, 包括那些欣赏我的人,这非常震撼人心。
原文链接:Lets review.. Docker (again)(翻译:左伟 校对:王哲)
===========================
译者介绍
左伟,现就职于IBM,软件工程师,现从事于DevOps相关的研究,实现和推广。
来自:http://dockerone.com/article/194