漫谈容器发展史

左耳朵耗子说过一段话,我深以为然。

技术的发展过程非常重要,要尽可能早的进入时下的新技术,而不应等待这些技术成熟之后再进入。我进入 Go 和 Docker 的技术不能算早,但也不算晚,从 2012 年学习 Go,到 2013 年学习 Docker 到今天,我清楚地看到了这两种技术的生态圈发展过程。让我收获最大的并不是这些技术本身,而是一个技术的变迁和行业的发展。从中,我看到了非常具体的各种思潮和思路,这些东西比起 Go 和 Docker 来说更有价值。因为,这不但让我重新思考我已掌握的技术以及如何更好地解决已有的问题,而且还让我看到了未来。

改变世界的“箱子”

1956 年 4 月 26 号,麦克莱恩的第一艘集装箱货船理性X号从纽约港起航,他无从预想,这个简陋的发明,在未来几十年内会彻底改变人类的命运,将人类的航运业带入了一个巨大的新时代。

集装箱的发明,极大地推动了全球化的协作浪潮,使得制造业的产业链条发生了翻天覆地的变化。从集装箱规模化应用的那一刻,地球变成了一座巨大的工厂,开始轰鸣了起来。

因为集装箱,商品以及原材料可以以极低的运输成本送往世界各地,从而使得全球的资源都卷入到了分工链条当中,经济学家讲:分工产生效能,因为生产效能的极大提升,人类创造了不可胜数的财富。而集装箱的影响依然在持续,依然在不断的改变着我们每一个人的生活。

到了 80 年代,突然大门一开,进来一个胖子,就是我们中国人。我们啥都没有,我们没有资金没有技术,但是我们有勤劳的双手,我们有庞大的人口存量,所以我们用这个优势进入到了全球化的分工当中。

结果呢,中国用 35 年的时间完成了西方 300 多年的工业革命进程,而这已经是中国第四次进行工业化革命的尝试了,你能说集装箱没有功劳么。

可是,像这样具有伟大意义的发明,在其问世的前十年可谓历尽坎坷,并没有让麦克莱恩占据领航者的地位。甚至最早的集装箱都不是麦克莱恩发明的,但他却是集装箱真正意义上的践行者。因此,人类最终将集装箱之父的荣誉授予他。

《经济学家》这样评价:如果没有集装箱,就不会有全球化!

如果你打开 Google 翻译翻译一下“集装箱”这个词,你会得到如下的答案:

Container

在英文中一词多义可能表示多义之间具有意义的延伸

容器 不正是如此么?

第一道灵光

让我们将历史拨回到 1979 年,贝尔实验室的一群大男孩亦或是老男孩正在开发 unix v7(Version 7 Unix) 操作系统。项目进行到了最后的开发和测试阶段,即便是对于这些天才般的程序员来说,系统级别的软件构建和测试仍然是一个繁复且无比棘手的难题。你可以想象:当一次测试开始,源代码编译和安装完成之后,整个测试环境就被“污染”了。要想进行下一次测试,就需要重新搭建,并且再编译安装测试。那有没有办法来避免重复的环境搭建,快速的销毁和重建所需的基础设施环境呢?

我们今天有虚拟化和云计算,这个问题听起来好像不难解决。但是,在那个计算机软件刚刚萌芽的蛮荒时代,这样的想法近乎是异想天开。一块 64K 的内存条要卖 419 美元的年代,“快速销毁和重建基础设施”的想法还是有点“科幻”了。

但天才毕竟是天才,这些黑客程序员们就想,能不能在现有的操作系统环境下“隔离”出一个新环境用于软件的构建和测试呢?

于是,一个叫做 chroot(Change Root) 的系统调用就此诞生了!

chroot 会重定向进程及其子进程的根目录到操作系统上的一个新位置,使得该进程的文件“视图”与外面的“世界”完全隔离,它不能够对这个指定根目录之外的文件进行访问动作,不能读取,也不能更改它的内容。

这是容器史上第一道乍现的灵光,在 unix v7 上面被孕育出来。

但令人叹息的是,unix v7 是贝尔实验室发布的最后一个可自由分发的版本,之后 AT&T 开始收回 Unix 的版权,倾情弹奏 Unix 商业化的序曲。也正因为此, Richard Matthew Stallman 在 1983 年发起 GUN(GNU's Not UNIX) 计划和自由软件运动,几十年后垂垂老矣的 Unix 最终被商业的 Linux 击溃。

然而,chroot 毕竟打开了进程隔离的大门,虽然孕育它的 Unix 在后续的发展中逐渐式微,但容器化的思想却如同奔流不息的河流一般,跨越了重重艰难险阻,不断地在历史的精彩处漫延。

百家争鸣

2000 年,Unix伯克利大学分发版 FreeBSD 操作系统发布了 jail 命令。jail 是从 chroot 得到的启发,并从中进一步发展而来,它将隔离扩展到了整个用户环境,使得进程在一个沙盒内运行。在进程看来,跟实际的操作系统几乎是一样的,对于进程来说就像被关进了监狱,这也是 jail 名称的由来,jail 中的进程甚至可以拥有自己的 IP 地址,可以对环境进行各种定制。我们可以说, chroot 开创了进程隔离的思想,但 FreeBSD Jails 才真正实现了进程的沙箱化

2004年,Solaris Containers 发布,它也是秉承了 jail 的思想,为进程提供一个隔离的沙盒,进程在其中独立运行。它也被称为“吃了类固醇的 chroot ”(chroot on steroids)

不过,无论是 FreeBSD Jails ,还是紧接着出现的 Solaris Containers ,都没有能在更广泛的软件开发和交付场景中扮演到更重要的角色。在这段属于 Jails 们的时代,进程沙箱技术因为“云”的概念尚未普及,始终被局限在了小众而有限的世界里。就如同集装箱刚刚出现的那十年,所有和集装箱配套的设施都还没有,整个社会都还没有为集装箱做好准备。你看,他们两者不仅意义上相近,就连命运也如出一辙。

可见,任何一种新技术,即便问世很早,可要整个社会系统去适应它,却需要一个无比漫长的过程。

让我们回到容器技术上来,事实上,在 Jails 大行其道的这几年间,同样在迅速发展的 Linux 阵营上也陆续出现多个类似的沙箱技术比如 Linux VServerOpen VZ (未进入内核主干)。但如同那些 jail 的前辈一样,受当时计算机环境的制约,被局限在一个小众的圈子里。

早在 2002 年,Plan 9 from Bell Labs(Go语言的运行时就是使用的该操作系统的汇编器语法)操作系统对 Namespace 的广泛运用为 Linux 带来了灵感, Linux 在其内核 2.4.19 版本上加入了 Namesapce 功能(可以实现对应资源的隔离)。最初只有mount namespace,后续的pidnetipcUTSuser等一直到内核3.8版本才实现完成。内核4.6中又添加了Cgroup namespace

要知道 Namespace 是现代容器技术最底层的技术支撑。它虽然解决了虚拟化和资源环境隔离的问题,但是我们还希望对隔离的进程在资源使用上加以限制,namespace并没有提供解决方案。

2007年,一种名叫 Process Container 技术的发布。它是由 Google 的工程师 Paul MenageRohit Seth 发起并实现的,旨在对一组进程进行资源上的隔离、限制。在合并入 Linux 内核的时候,由于 Linux 中存在 Container 的概念,故被重命名为 Cgroups

Cgroups 有两个版本,v1 版本由 Paul Menage Rohit Seth 维护。v2 版本首次出现在 2016 年 3 月发布的内核 4.5 中,Tejun Heo 重新设计并重写了Cgroups,主要解决 v1 在用户体验上的问题。

2008 年,通过将 Cgroups 的资源管理能力和 Linux Namespace 的视图隔离能力组合在一起, LXC(Linux Container) 这样的完整的容器技术出现在了 Linux 内核当中。它是第一个完善的容器管理方案,你可以通过 LXC 来创建和启动容器了。 LXC 跟之前出现的沙盒技术非常类似,但其赶上了 Linux 大规模商用的浪潮,境遇要比那些前辈们要好一些。伴随着公有云市场的崛起,很快催生了一个全新的、名为 PaaS 的产业。

2011 年,由 Vmware 主导的 Cloud Foundry 开发了一个新项目:Warden,它最开始是一个 LXC 的封装,后来重构成了直接对 Cgroups 以及 Linux Namespace 操作的架构。

Cloud Foundry 项目的诞生,第一次对 PaaS 的概念完成了清晰而完整的定义。这其中,“PaaS 项目通过对应用的直接管理、编排和调度让开发者专注于业务逻辑而非基础设施”,以及“PaaS 项目通过容器技术来封装和启动应用”等理念,也第一次出现在云计算产业当中并得到认可。

按照这个剧本,容器技术以及云计算的发展,理应向着 PaaS 的和以应用为中心的方向继续演进下去。

如果不是有一家叫做 Docker 的公司出现的话。

另一只”箱子“

时间来到了 2013 年,Docker 的第一个版本发布,它是基于 LXC 的,但它创建和使用应用容器的逻辑跟 Warden 等没有本质不同。只不过是把 LXC 复杂的创建和使用方式简化成了自己的一套命令体系。但是 Docker 作为 PaaS 行业的搅局者,其真正的杀手锏是容器镜像。Docker 通过镜像技术,提出了buildshiprun的概念,创造了一次构建、处处运行的新思想,将容器技术向IT产业链条的上游和下游进行了延伸。

关于如何封装应用,这本身不是开发者所关心的事情,所以 PaaS 项目有着无数的发挥空间。但到这如何定义应用这个问题,就是跟每一位技术人员息息相关了。在那个时候,Cloud Foundry 给出的方法是 Buildpack ,它是一个应用可运行文件(比如 WAR 包)的封装,然后在里面内置了 Cloud Foundry 可以识别的启动和停止脚本,以及配置信息。

然而,Docker 项目通过容器镜像,直接将一个应用运行所需的完整环境,即:整个操作系统的文件系统也打包了进去。这种思路,可算是解决了困扰 PaaS 用户已久的一致性问题,制作一个“一次构建、处处运行”的 Docker 镜像的意义,一下子就比制作一个连开发和测试环境都无法统一的 Buildpack 高明了太多。

更为重要的是,Docker 项目还在容器镜像的制作上引入了“层”的概念,这种基于“层”(也就是“commit” ) 进行 buildpushupdate 的思路,显然是借鉴了 Git 的思想。所以这个做法的好处也跟 Github 如出一辙:制作 Docker 镜像不再是一个枯燥而乏味的事情,因为通过 DockerHub 这样的镜像托管仓库,你和你的软件立刻就可以参与到全世界软件分发的流程当中了。

至此,你就应该明白,Docker 项目实际上解决的确实是一个更高维度的问题:软件究竟应该通过什么样的方式进行交付?

更重要的是,一旦当软件的交付方式定义的如此清晰并且完备的时候,利用这个定义在去做一个托管软件的平台比如 PaaS,就变得非常简单而明了了。这也是为什么 Docker 项目会多次表示自己只是“站在巨人肩膀上”的根本原因:没有最近十年 Linux 容器等技术的提出与完善,要通过一个开源项目来定义并且统一软件的交付历程,恐怕如痴人说梦。

然而,这并没有使 Docker 站在领导者的位置上。CoreOS 是一个专注于容器的操作系统,一度与 Docker 打的很火热,但是 CoreOS 渐渐的发现 Docker 野心很大,甚至动了 CoreOS 的市场。至于 Docker 有什么野心,我后面会讲。 CoreOS 不肯坐以待毙,于是在 2014 年推出了新的容器引擎 rocket 。后来谷歌开始支持 CoreOS ,容器就此分化成了 Docker 阵营和 Google 阵营。也就在同一年,Docker 0.9 发布,用 libcontainer 库代替了原来的 LXC 。

到了 2015 年,容器圈太乱了,大家觉得长此以往不利于发展,于是就组织起来,在 Linux 基金会的支持下成立了 OCI(Open Constitution Initiative)OCI 致力于围绕容器镜像格式容器运行时建立开发的行业标准,让容器可以在各种兼容性的操作系统和平台上移植,没有人为的技术屏障。大家并不希望这个工业标准由 Docker 一家说了算。

2016 年 Docker 发布了 1.11 版本,做了一些架构调整,里面新出现了符合 OCI 标准的 runC 。runC 其实就是对 libcontainer 的调用,是一种符合开放式容器格式标准的一种实现,后来 Docker 就把 runC 贡献出来了。

下图是 2018 年容器市场份额统计:

Container Market Shares

可见 2018 年的容器市场 Docker 占了 83% ,这个数据乍看之下相当可观,可是你要知道在 2017 年这个份额还是99%。位居第二的就是占比12%的 rocket ,第三是 mesos ,占比4%,第四是我们介绍过的 LXC 占比仅有1%。

现在,我们可以得出一个结论,**Docker 并不等于容器**。如果你想用容器,其实你有很多选择,Docker 仅仅是你的首选而已。如今下一代容器架构的呼声言犹在耳( Podman + Skopeo + Buildah ),我们不禁要问了:既然 Docker 这么优秀,为什么会落到如此众叛亲离的地步呢?

编排大战

如今我们知道,现代容器的底层技术为 NamespaceCgroups ,Docker 因为带来了革命性的容器镜像思想而异军突起,在容器领域大张挞伐,并在短时间内挤占市场。虽然表面上风光无限,却终究难掩容器技术门槛偏低的事实,也就是说任何公司和组织都可以利用 NamespaceCgroups 进入容器市场(比如 CoreOS 的 rocket ), Docker 更是深谙此道,因此它必须寻求突破。

试想一下, Docker 容器发端之后,感觉一下子拥有了可以施展黑科技的魔法棒,但为什么只能搞搞开发、测试这种小打小闹的活儿呢?根本没有生产力大爆发啊?好像也没有改变整个行业的协作方式啊?你看,这是不是和集装箱刚刚问世的前十年非常像呢?那个时候整个社会协作体系还没有为集装箱做好准备,配套的道路、桥梁、码头、吊车等设备和基础设施全都没有就绪。

回到 Docker 上,我们不禁要问:我们的码头和吊车,乃至相应的货轮、桥梁准备好了么?全产业界已经接受了以容器镜像为主要形态的软件发布模式了吗?应用的执行都基于容器了吗?分布式以及微服务架构已经非常普及了吗?

显然还没有。

这里我们不得不提一个观点:容器本身并没有价值。就像集装箱一样,本身就是个大铁盒子,它没有太多价值,单单靠它提高不了社会协作的效能。它只有流动起来,才会产生价值,把货物从一个车间运到另一个车间,这种连接才是其价值所在。容器也一样,本身没有太多商业价值,你弄得再完美,你也只能在一个服务器上折腾,翻不出多大的浪花,那怎么样才能产生价值呢?

那就是真正价值所在——容器编排

在既有硬件资源的基础上,启动容器不需要关注具体运行的节点,各个容器之间仍能保持通信,信息在容器之间依然可以流动。这样就拥有了商业价值,容器技术便可以付诸商用,整个软件的开发交付流程就会变得高效和颠覆。所以我们需要的是一个分布式的调度器,其主要功能就是容器编排。

Docker 也深知这一点,当它不顾一切于 2015 年带着 Swarm 挤进编排领域的时候,发现面前立着一座大山: Kubernetes

Kubernetes 源于 Google 内部的 Borg 项目,经 Google 使用 Go 语言重写后,被命名为 Kubernetes,并于 2014 年 6 月开源。Kubernetes 希腊语意思是“舵手”,致力于管理数以万计的容器集群。你看舵手不正是隐喻了方向流动么。因其开头字母和结尾字母之间共有 8 个字,所以简短的称其为k8s

2014 年 k8s 开源之后,同年底 Docker 就设计了 machine+swarm+compose 的组合方案。2015 年 7 月 k8s 发布了第一个商用版本 1.0 ,同一年 Docker 的 Swarm 发布,编排大战已经开始上演了。

2016 年 2 月 Docker 发布了 1.12 版本,它不顾众怒,将 Swarm 强行内置到 Docker 的容器引擎里面,企图利用 Docker 项目在容器领域的领导地位推动 Swarm 的发展。这有点像什么?举个不恰当的例子,我订购了一个集装箱,你却附赠了一个不太好用的货轮。这一下一石惊起千层浪,业界都纷纷谴责 Docker 。同年的 7 月 Apache mesos 发布了 1.0 ,也是一个容器编排框架,至此,KubernetesDocker SwarmApache Mesos 已成三足鼎立之势。

然而,2017 年 9 月,Mesosphere 宣布支持 Kubernetes,这也是迫于用户压力,在对抗和妥协面前,不得不选择后者。

2017 年 10 月,在欧洲的 DockerCon 大会上,Docker 公司 CTO Solomon Hykes 宣布,Docker 的下个版本将支持 Kubernetes,台下观众响起热烈掌声,因为这是容器圈等待已久的消息。

Kubernetes 在众多厂商和开源爱好者的共同努力下迅速崛起,时至今日已成长为容器管理领域的事实标准。Kubernetes 极大推动了云原生领域的发展,被称为影响云计算未来 10 年的技术。

毫无疑问,Kubernetes 已经赢得了容器编排的大战。

Docker 会是改变世界的那只“箱子”吗?

2001 年 5 月 30 号,集装箱之父麦克莱恩去世。全世界所有的集装箱船,不管在哪个港口,不管在全球的哪个角落,都拉响了汽笛,向这位老人致敬,我想这是一个创新者得到的最高荣誉。

那么, Docker 会是改变世界的那只“箱子”吗?

这要看你怎么理解 Docker ,如果我们把它理解为容器技术buildshiprun 这样一次构建,处处运行的理念,那么我相信它是改变世界的箱子。当我们软件发布模式已经切换到容器镜像形态的时候,当我们应用的运行都是基于容器的时候,当分布式的操作系统或者平台已经整装待发的时候,它有什么理由不改变世界呢?我们没法想象那时 IT 世界会是什么样子的,但有一点可以肯定,IT 世界的分工协作方式以及产业链条肯定是一个全新的面貌。

反观 Docker 作为一家公司,它会是改变世界的箱子么?我觉得很大可能不会。

上个世纪 80 年代,在集装箱刚刚发力的时候,麦克莱恩破产了。那他犯了什么错误吗?没有,他就是跑慢了,没有什么实质性的错误。Docker 很可能步他的后尘,但是那又怎么样呢? Docker 已经完成了它的历史使命,在让 IT 的世界工厂运转起来的路上,它已经推了一把,这本身就已经足够了。

总结

早在 2014 年,RedHat 就与 Kubernetes 达成了战略合作关系,宣布全面投入 Kubernetes。在当时的一份官宣中, RedHat 以非常自信的姿态表达了对容器的“颠覆性”创新的认可,并大胆预言 Kubernetes 会在 2015 年后取得应用编排与管理领域的统治地位。

当时业界对这个论断大多不以为然,甚至嗤之以鼻,但今天回过头来再看,预言已经成为事实。

Rancher 的创始人梁胜博士有过一句评论,非常在理:时至今日,在容器技术领域依然有许多创新,只不过这些创新大多发生在 Kubernetes 以及 CNCF 生态系统中了。

是的,比如最近很火的 Service Mesh 的实现 istio , 它就是基于 kubernetes 进行的创新啊。

这有点像以前在单片机上玩汇编,汇编说白了就是单片机上运行的应用程序,但是后来有人写了个超级大的应用程序,那就是操作系统,然后大家就都在操作系统上玩了。而 Kubernetes 不正是云时代分布式的操作系统么?

回到文章开头的主题上,我们应该及早的进入到一个新兴的技术领域,即使 Docker 会在不久的将来被下一代容器取代,我们也仍然有必要去学习它。因为你最终得到的并不是这项技术本身,而是整个技术领域的发展脉络与思潮演变,这才是无比珍贵的东西。因为一旦你对技术的发展有所感知,它就可能会影响到你未来的人生选择。

我们仍然身处在容器化变革的浪潮当中,你无法想象它将来会对你的命运产生什么样的影响。我们每一个人都像是这个湍流中行进的小船,方向决定了你驶向远方还是抵触暗礁。我们唯一能做的,就是拿一根竹篙,根据水流的情况和环境的变化随时的轻轻点那么一下,微微的改变一下我们的航迹。

注:本文很多段落,大段引用了张磊老师的文章,我读过他的《Docker 容器与容器云》,订阅过他的极客时间专栏《深入剖析Kubernetes》,深深佩服其学识之渊博。在有些关于容器发展史的描述中,我基本上引用了原话,因为自知不能写的更好。

参考文章:

  1. Linux namespaces
  2. cgroups
  3. DOCKER基础技术:LINUX CGROUP
  4. Docker 会是改变世界的那只“箱子”吗?
  5. 为什么说 2019,是属于容器技术的时代?
  6. Linux 容器技术史话:从 chroot 到未来
  7. DOCKER基础技术:LINUX NAMESPACE(上)
  8. Linux Namespace : 简介
  9. 下一代容器架构已出,Docker何去何处?
  10. Docker 背后的标准化容器执行引擎——runC