Dockerfile
Dockerfile 有很多问题,丑陋、约束、矛盾还有从根本上的缺陷。比方说在一个仓库里生成多个镜像,所有的镜像都需要同样的依赖环境,但是其中一个包含调试工具。Docker不支持这个(per #9198),你也不能扩展Dockerfile(per #735),使用子目录会破坏创建环境同时会阻止你使用ADD/COPY命令(per #2224),因为dockerfile使用管道方式读入的(per #2112),你也不能使用环境变量在生成镜像的时候根据条件来改变指令(per #2637)。
我们的团队曾经创建了一个基础镜像,两个特定环境的镜像,还有一些makefile脚本用来做批量改名和sed替换。一些不期望的“特性”会导致环境变量$HOME消失,然后返回无用的错误信息。实在是太恶心了。
Docker 缓存/层
Docker有通过使用COW(写时复制)文件系统缓存Dockerfile指令的能力。和LVM快照类似,并且直到至今只支持AuFS,这个有无数的问题。接下来在0.7发布版中不同于COW实现被引入了来改善稳定性和性能,详情。
然而这个缓存系统是不智能的,导致了一些令人惊讶的负效应,不能阻止来自缓存的单指令(per #1996)。它也慢的痛苦,从这一点来说,如果阻止缓存和使用层,会让构建快一些。缓慢的上下载速度使Docker Hub的性能变得更糟糕。接下来详细描述。
这些问题都是由Docker作为一个完全的强制行线性指令执行甚至在它完全不适合的条件下的架构设计导致的。作为一个慢速构建的工作环境,你可以使用第三方工具支持异步执行,比如Salt Stack, Puppet 或者 甚至是 bash, 完全打败层的目标和使他们一无是处。
Docker Hub
Docker鼓励通过Docker Hub进行社区协作,在Docker Hub上你可以以公开或私有的方式发布你的Dockerfile,这样其他用户可以通过FROM命令来使用或者扩展你的Dockerfile,而不是复制粘贴。但是这样做也是有问题的。Dockerfile不支持多个FROM命令(per #3378, #5714 and #5726),意味着你只能继承一个镜像。Docker Hub也没有强制的版本,比如dockerfile/ubuntu:14.04的作者可以更换标签对应的内容,这就像使用一个没有强制版本的打包管理器。而且它TMD还有速度限制,这个下面会提到。
Docker Hub还有一个自动构建系统,它会检查你仓库中新的提交然后触发一个容器构建。这是完全无用的。构建配置几乎没有什么定制的能力,甚至没有最基础的构建前/后的脚本钩子。这迫使我们创建一个特殊的项目结构,在项目的根目录放一个单独的Dockerfile,这破坏了我们之前提到的构建环境,而且构建时间太TMD长了。
我们的小组曾近使用CircleCI,一个很特别的托管CI平台,它可以根据Makefile触发Docker的构建然后上传到Docker Hub上。这个也没有解决龟速的问题,看来唯一的选择就是使用我们自己的Docker仓库,但是这个实在是太复杂了。
安全性
Docker最初使用LXC作为基础的运行环境,但是从0.9版开始他们使用自己的容器库。通过它可以调整命名空间的性能、权限,而且可以通过定制LXC配置使用适当的exec-driver。
它需要在宿主机上启动一个守护进程,Docker上有很多安全上的缺陷,比如CVE-2014-6407 和 CVE-2014-6408,坦白说这些缺陷都不应该出现在第一位。甚至当Gartner看到他们跟踪记录上可怜的评估时,对Docker的不完善和安全问题表示了关注。
从设计上来说,Docker太信任命名空间的能力,而命名空间相比普通的hypervisor有太多的漏洞,像Xen现在有129个公开的漏洞,而Linux里有1279个漏洞。在一些场景下这是可以接受的,比如在Travis CI上的公开构建,但是在多用户的私有环境里是非常危险的。
容器不是虚拟机
Namespaces 和 cgroups 非常 强大,它们允许一个进程以及它的子进程从共享的内核资源(入网络栈和进程列表)里获得一个仅自己可见的视图。这种细粒度的控制和隔离,配合chroot jailing和 grsec,可以提供一层非常完美的保护。一些程序即使不用 docker 也可以获得这种好处,比如 uWSGI;一些不支持 Namespace 的程序则可以使用 firejail 从而运行在沙箱之中。
集装箱化的项目,比如 LXC 和 Docker,利用这些特性可以非常有效的在同一个内核空间里运行多个发行版。与 hypervisors 相比,这样的优势在于更低的内存占用,更快的启动速度,但是会降低安全性、稳定性和兼容性。一个极端的情况就是 Linux 内核接口,在内核和命名空间中运行不相容的或者未经测试的 glibc 可能产生一些无法预测的操作。