唱衰Docker,给大红大火的Docker泼点冷水

jopen 10年前

原文  http://www.infoq.com/cn/articles/bad-mouthing-docker


从我上一次对Docker进行 评价 到现在已然经过了一年有余,想当初我在文章里对这套容器技术方案的架构设计缺陷与糟糕的用户体验做出了严厉的批判。不过在这段时间当中,Docker项目也开始逐步走向成熟,迎来自己的 1.0版本 并在Amazon的推动下 声名大噪 ,但同时用户挫败感、 过度宣传 引发的指责甚至因 漏洞遭利用 而引发主机感染越来越多。当然, Docker Hub 中私有库的引入让用户不必再为了托管部署而运行自有Registry系统,再配合webhook以及同GitHub的紧密 集成 , Docker看起来有一个良好的前途。

有鉴于此,我决定再给Docker一次机会,并以六个月为周期将其引入生产环境。结果非常糟糕,Docker性能极差,而旁门左道的解决方案加上以用户体验为代表的种种短板简直令人抓狂。实际上,Docker的性能表现实在太差,禁用缓存功能竟然能够加快build速度。

(感兴趣的朋友可以查看 redditycombinator 网站上与该主题相关的讨论内容)。

Dockerfile

Dockerfile存在着一系列问题,它令人讨厌、充满局限、不伦不类而且包含根本性缺陷。假如要构建一个库的多个镜像,例如第二个镜像包含内容调试工具,但两个镜像拥有同样的基础运行要求。Docker不支持这种做法(详见 9198号问题 ),我们无法扩展Dockerfile(详见 735号问题 ),使用子目录会破坏构建上下文并导致用户无法使用ADD/COPY(详见 2224号问题 )或者“管道(piping)”(详见 2112号问题 ),我们也不能在构建过程中通过环境变量实现有条件的指令变更(详见 2637号问题 )。

我们给出的 解决方案 是创建一个基础镜像,两个特定环境的镜像以及其它一些包括重命名以及sed替换功能的Makefile自动化。除此之外,Docker中还有一些意想不到的“ 功能 ”有可能导致环境变量$HOME消失,进而产生无用的错误信息。太令人厌恶了。

Docker缓存/层

Docker有能力利用COW(即写入时复制)文件系统实现 缓存 Dockerfile指令,这一点与LVM 快照机制 相似,而且直到最近都只支持AuFS,而后者还存在大量问题。之后, 0.7版本 引入了多种不同的COW实现方式以改进稳定性与性能,感兴趣的朋友可以 点击此处 了解更多细节信息。

然而,这套缓存系统不智能,它不能阻止单一指缓存(详见 1996号问题 ),产生了一些意料之外的 副作用 。它的运行速度也极为缓慢,如果禁用缓存并避免使用层,其构建速度甚至能够得到提升。而Docker Hub缓慢的上传/下载速度则让情况进一步恶化,这个问题我们将在下文作进一步评述。

这些问题均源自Docker整体所采用的糟糕的架构设计,这直接导致它即使是在完全不适用的情况下,依然会强制执行线性指令(详见 2439号问题 )。作为构建缓慢的解决方案,可以使用支持异步执行的第三方工具,例如 Salt StackPuppet 甚至 bash ,它们完全能够达成层的目的而使层变得没用。

Docker Hub

Docker鼓励用户通过Docker Hub进行社会化合作。用户可以在上面发布Dockerfile——包括公开与私有文件。其他用户可以通过 FROM 指令而不是复制/粘贴来继承并使用这些Dockerfile。该生态系统类似于AWS 市场 以及Vagrant Boxes 中的AMI,从理论上讲还是非常有用的。

然而由于一些原因,Docker Hub的实现存在缺陷。Dockerfile不支持多FROM指令(详见 3378号5714号 以及 5726号问题 ),这意味着只能继承单个镜像。此外,它没有版本强制。举例来说,dockerfile/ubuntu:14.04的作者可以替换该标签的内容,这相当于 允许用户使用软件包管理器但又不没有版本强制机制。而且正如下文所提到,Docker Hub在这方面存在着令人沮丧的速度缓慢的限制。

Docker Hub还拥有一套自动化构建系统,能够检测到库中新提交内容并触发容器构建。因为许多原因,这项功能也是完全没用。由于几乎不能定制,构建配置受到了极大 限制,甚至无法支持最基本的脚本执行前/后的钩子。Docker Hub采用一套特殊的项目结构,一个项目下只能有一个Dockerfile,这破坏了我们先前提到的构建解决方案,而且构建速度极为缓慢。

我们的解决方案是使用 CircleCI ,它是一个优秀的托管CI平台,能够从Makefile触发Docker构建并推送到Docker Hub。虽然这种方式无法解决速度慢的问题,但唯一的可选方案是使用我们自己的Docker Registry,其 复杂程度 都到了荒唐的地步。

安全性

Docker最初使用LXC作为默认执行环境,但现在,0.9版本默认使用libcontainer。这使得用户可以 调整 命名空间功能、权限,并且可以在使用合适的exec-driver时使用自定义的LXC 配置文件

这需要一直在主机上运行一个root守护进程,而且Docker一直存在着 若干 安全漏洞,例如 CVE-2014-6407 以及 CVE-2014-6408 。坦率地讲,这些问题起初就不应该存在。甚至Gartner公司也在其 追踪报告 中给出了糟糕评价,并表达了对Docker的不成熟和安全性问题的 担忧

按照设计,Docker对于命名空间 功能 给予充分信任,这就导致其攻击面要比其它典型的虚拟机管理程序更宽。Xen拥有 129 项CVE,相比之下Linux则拥有 1279 项。在某些情况下上述问题并非不可接受,例如在Travis CI当中进行公开构建,但这对于私有、多用户环境来说无疑是危险的。

容器与虚拟机并不是一回事

命名空间与cgroups功能 极为强大 ,允许一个进程及其子进程拥有一个共享内核资源——例如网络堆栈以及进程表——的私有视图。这种细粒度控制与隔离机制配合上chroot jailing与 grsec ,能够提供非常出色的保护层。一些应用程序,如 uWSGI ,可以在没有Docker的情况下直接利用这些特性的优点,而不支持命名空间的应用程序则可以利用 firejail 实现沙箱化处理。如果您有冒险精神,可以将这种支持直接添加到自己的容器化项目的 代码 中,例如LXC以及Dokcer,从而在单一内核空间中利用这些特性的有点高效地运行多套发行版。 相较于 虚拟机管理程序,这种作法有时候会有降低内存使用率和减少启动时间的好处,但其代价就是降低安全性、稳定性以及兼容性。举个与 Linux Kernel Interface 相关的最糟糕的 极端案例 ,在内核及用户空间中运行不兼容或者未经测试的glibc组合版本很可能引发意料之外的行为。

早在2008年LXC尚处于构思阶段时,硬件辅助虚拟化也仅仅诞生了几年时间,许多虚拟机管理程序都存在着性能以及稳定性问题。因此,虚拟化技术并没有得 到广泛应用,而面对成本以及物理基础设施占用减少等优势,上述问题是可以接受的。不过如今我们的虚拟机管理程序在性能表现方面几乎与裸机设备不相上下,而 且有趣的是,在 某些情况 下速度更快。另外,托管的、按需分配的虚拟机速度越来越快,成本越来越低, DigitalOcean 在性能与成本方面都要远远胜过EC2,这也使应用程序与虚拟机之间进行一对一映射从经济角度讲成为可能。

[编辑意见]正如 Bryan Cantrill 提出的 观点 ,虚拟化技术的性能将受到工作负载类型的显著影响。例如,IO任务繁重的应用程序会导致性能 降低

在某些特定的应用场景中,容器化确实是恰当的解决方案,不过除非能够明确说明为什么在你的应用场景中选择此类处理方式,否则你可能应用使用虚拟机管理程序 代替。而且即使使用虚拟化技术方案,你仍然应该利用命名空间的优点,而在应用程序没有对这些特性提供原生支持的情况下,像 firejail 这样的工具可以提供帮助。

Docker并不是必须的

Docker增加了一个复杂的侵入层,使开发、故障排查以及调试工作的难度大幅上升,它带来的问题往往多于它能够解决的问题。它在部署上也没有 任何优势,因为你仍然需要利用快照实现响应式自动扩展。更糟糕的是,如果大家并没有使用快照机制,那么生产环境的扩展就会依赖于Docker Hub的稳定性。

目前有不少项目都在滥用容器化技术,例如 baseimage-docker ,该镜像旨在通过运行作为入口的init.d简化检查、调试和兼容,甚至还提供一个可选的SSH服务器,实际上是将容器当作虚拟机对待,虽然作者本人以 无甚说服力的言词 反对这种观点。

总结

如果开发流程合乎情理,那么你已经明白Docker不是必须的。Docker中号称能够带来助益的全部功能要么完全无用,要么实现很差,而其最大的优势直接使用命名空间就很容易实现。如果放在8年前,Docker是一个有趣的概念,但以现在的眼光来看,它几乎是没用的。

修正/改进

从表面上看,Docker还有很多事情要做。它的生态系统鼓励开发人员倾向于“ 不可变部署(immutable deployment) ”这样一种理念,新项目能够更快更 轻松 的地完成,在这一点上其实用性确实得到了许多数人的肯定。然而需要注意的是,这篇文章的主旨在于探讨Docker的日常使用和长远使用,包括在本地及生产环境中。

尽管前面提到的大部分问题都清晰易懂,但文章并没有提及Docker如何才能做得更好。有许多可选的方案可以替代Docker,每种方案中都有自己的优点和不足,而我将在接下来的文章中进行详细阐述。

感兴趣的读者可以查看 a-komarkbnj 的进一步讨论,前者讨论了容器化的长远影响,后者从技术上进行了反驳,你可能会觉得它们都非常有用。

我要对在百忙中抽出时间给出个人反馈意见的每位朋友表示由衷的感谢。看到文章受到大家的关注确实令人倍感振奋,而且在读过多位工程技术大牛——其中包括许多年来都一直给我启发的那些人——的回应后,我也颇有种诚惶诚恐之感。

查看英文原文: Lets review.. Docker (again)

</div>