【云原生•容器】容器的崛起之路
Docker
「从2006年亚马逊云推出,到2009年国内互联网大厂的纷纷跟进,再到2010年中国将其纳入战略性产业,云计算进入快速发展期,云时代正式来临。大家看中云计算平台主要基于其美好愿景:让计算型资源能成为类似水煤电等基础设施,按需提供给用户。云计算平台也开启了以PaaS为核心构建平台层服务能力的变革,基于"应用托管"能力,用户上云可以极大简化部署、运维工作。」
「基于虚拟化技术的云计算和之前说的基于容器化技术的LXC,从不同技术路线都可以搞定资源隔离、资源限制等运行时问题,但是在使用过程中发现:并未解决困扰运维人员多年的应用打包和发布这一难题。这就影响了上云体验效果:明明本地运行好好的应用,却需要做很多的修改和配置工作才能在PaaS平台上运行起来,期间可能还要经过一波三折的打包、部署等不停地重试,才能搞定。」
「2013年,Docker的横空出世,并在接下来短短的几个月时间里迅速崛起,并吸引了 Amazon、Google、Red Hat 等大公司的关注,使得容器技术迅速发展,完全重塑了整个云计算市场的形态。那Docker到底有什么样的魔力,让我们对 Docker 如此的喜爱,以至于 Docker 能够在短时间内就从雏鸟成长为大鹏,成为万众瞩目的新星的呢?」
**「首先,我们看下 Docker 的实现主要归结于三大技术:命名空间 ( Namespaces ) 、控制组 ( Cgroups ) 和联合文件系统 ( Union File System ) 。」**之前介绍过,Namespaces 和 Cgroups 是 Linux 内核原生支持的容器技术,用于解决容器运行时资源隔离和资源限制问题,LXC 底层也是基于 Namespaces 和 Cgroups 技术实现,所以,对于这部分需求一开始 Docker 并没有自己去实现,而是选择基于 LXC 去实现,直到0.9版本之后,由于一些原因 Docker 才选择自己直接使用 Namespaces 和 Cgroups 实现,这就是 libcontainer,并在之后移除对 LXC 的依赖。
「从上面可以看到,Docker 在容器功能和实现原理上并没有太大创新,而 Docker 引入镜像概念和容器技术进行融合,巧妙的解决了应用打包和发布的问题,这才是Docker 能够迅速崛起的精髓。Docker就是基于现有技术,但是在产品功能上进行颠覆创新,从而开启了"Docker"的全新时代。」
Docker 的核心架构见下图:
Docker 采用C/S架构,我们常使用的 docker 指令都是客户端 client,它会与 Docker Engine 后端服务进行通信,比如发送pull、build、run等命令给docker engine,而 docker engine 则是镜像和容器的大管家,负责从远端镜像仓库(Registry)拉取镜像、在本地存储镜像,还有从镜像生成容器、管理容器等所有功能。而镜像则存储在远端的 Registry 仓库里,客户端并不能直接访问镜像仓库。所以,在 Docker Engine 里,真正干活的其实是默默运行在后台的 Docker daemon,而我们实际操作的命令行工具"docker"只是个"传声筒"的角色。
镜像
「一切在云端,处处是镜像。」
Docker 镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是 image 镜像文件,远比传统交付提供应用可执行文件 + 启停脚本的方式丰富的多。
传统的软件交付是怎样的呢?windows下的 2016-0ffice.exe ,不能在 xp 运行;redis.rpm 在 redhat 系列 Linux 上运行,但是不能在其他 linux 版本上运行,还比如 vsftpd-3.0.2-10.el7.x86_64.rpm 这个包只能在 rhel7 上安装运行,却不能在 rhel6 安装运行。
「docker 的创新:docker 镜像一次构建,到处运行。」 实际上,大多数 Docker 镜像是直接由一个操作系统的所有文件和目录构成的,所以这个压缩包里的内容跟你本地开发和测试环境用的操作系统是完全一样的。这就有意思了:假设你的应用在本地运行时,能看见的环境是 CentOS 7.2 操作系统的所有文件和目录,那么只要用 CentOS 7.2 的 ISO 做一个压缩包,再把你的应用可执行文件也压缩进去,那么无论在哪里解压这个压缩包,都可以得到与你本地测试时一样的环境。当然,你的应用也在里面!这就是 Docker 镜像最厉害的地方:只要有这个压缩包在手,你就可以使用某种技术创建一个"沙盒",在"沙盒"中解压这个压缩包,然后就可以运行你的程序了。更重要的是,这个压缩包包含了完整的操作系统文件和目录,也就是包含了这个应用运行所需要的所有依赖,所以你可以先用这个压缩包在本地进行开发和测试,完成之后,再把这个压缩包上传到云端运行。在这个过程中,你完全不需要进行任何配置或者修改,因为这个压缩包赋予了你一种极其宝贵的能力:「本地环境和云端环境的高度一致!」
「那么我们来看下 Docker 镜像底层原理又是怎么实现的呢?」
「一般而言,Linux的操作系统由两类文件系统组成:bootfs(boot file system)和rootfs(root file system),它们分别对应着系统内核与根目录文件。」 bootfs 层主要为系统内核文件,这层的内容是无法修改的,当我们的系统在启动时会加载 bootfs,当加载完成后整个内核都会存到内存中,然后系统会将 bootfs 卸载掉。而 rootfs 层则包含了系统中常见的目录和文件,比如 /dev
, /proc
, /bin
, /etc
等等。
**「不同 Linux 发行版本的主要区别在于 rootfs 层,比如 Debian 使用 apt 管理软件,而 Centos 使用 yum 方式,而在内核层面,两者的差别并不大。」**因此,可以在一台宿主机上同时运行不同 Linux 发行版的镜像而不出错,如在一个安装了 centos 的宿主机上同时启动 Centos 镜像的容器和 Debian 镜像的容器。
平时我们安装进虚拟机的 CentOS 都是好几个 G,为什么 docker 这里才 200M?
对于一个精简的 OS,rootfs
可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的 kernel,自己只需要提供 rootfs
就行了。由此可见对于不同的 linux 发行版, bootfs
基本是一致的, rootfs
会有差别, 因此不同的发行版可以共用 bootfs
。
「Docker镜像涉及另一个巧妙的地方在于:采用分层构建,即镜像是由一系列镜像层(layer)组成的,每一层代表了镜像构建过程中的一次提交,当需要修改镜像内的某个文件时,只需要在当前镜像层的基础上新建一个镜像层,并且只存放修改过的文件内容。」
比如下面展示基于 Dockerfile 构建镜像原理:
go
FROM debian
RUN apt-get install -y emacs
RUN apt-get install -y apache2
... ...
EXPOSE 80
CMD ["/usr/sbin/httpd -D FOREGROUND"]
「原理说明:」
-
步骤一:
FROM debian
:一般镜像都是基于一个基础镜像构建(Base Image),这个基础镜像比较常见的是系统镜像,比如这里是基于 debian 操作系统构建镜像; -
步骤二:
RUN apt-get install -y emacs
:使用 debian 系统 apt-get 方式安装 emacs 软件,安装涉及的新增系统文件和配置变更都会保存到新增的镜像层里,对之前镜像层不会产生任何影响,同时 parent 指向上一层即基础镜像(Base Image); -
步骤三:
RUN apt-get install -y apache2
:同理上一步,使用 debian 系统 apt-get 方式安装 apach2 软件,安装涉及的新增系统文件和配置变更都会保存到新增的镜像层里,对之前镜像层不会产生任何影响,同时 parent 指向上一层即步骤二产生的镜像; -
就这样不停地产生新镜像层,最后按照顺序将这些镜像层叠加到一起,就构建出一个完整的镜像。
**「镜像分层最大的一个好处就是共享资源,方便复用,就和写程序代码中为啥要抽象方法、类是一个道理。」**比如构建10个基于centos系统的镜像,那么这10个镜像是可以共用同一个centos基础镜像层。镜像层的复用可有效减少存储占用,每个主机上可能成百上千的镜像,这个影响还是非常大的。到后面我们将容器时,你会发现:将镜像运行起来变成容器,只需要添加一个读写层即可,实现的也非常巧妙。
镜像是一层层的分层设计,那使用的时候又是通过什么技术将他们叠加到一起的呢?这就要说到 Docker 三大核心技术的最后一个:联合文件系统技术。「联合文件系统(Union File System,Unionfs)是一种分层的轻量级文件系统,它可以把多个目录内容联合挂载到同一目录下,从而形成一个单一的文件系统,这种特性可以让使用者像是使用一个目录一样使用联合文件系统。」
联合文件系统示意图如下:
❝
如果不同的镜像层中有相同路径的文件,则上层镜像层会覆盖下层镜像层的内容,如上图所示,由于第二层的文件2与第一层的文件2具有相同的文件路径,则镜像将以第二层的文件2内容进行展示,第一层的文件2会被第二层的文件2给覆盖,第一层只有文件1会被最终显示。
❞
「联合文件系统就像能提供一个从上往下透视能力一样,上层文件会将下层文件覆盖掉,这样就将镜像层一层层的叠加起来,最终提供一个从上往下的汇总视图,这样用户看到的就是镜像层叠加后的完整文件系统内容。」
Kubernetes
「Docker 项目的出现,则为项目打包、部署这个根本性的问题提供了一个近乎完美的解决方案,迅速成为了基础设施领域近十年难得一见的技术明星。虽然,Docker 项目一日千里的发展势头迅猛,但是Docker缺少平台能力。因为,对于用户来说,他们要部署网站、系统或产品,更需要那些能为他们提供平台层能力的产品,而 Docker 项目只能用来创建和启停容器的小工具,最终只能充当这些平台项目的"幕后英雄"。」
所以,2014年 Docker发布 Swarm,「Docker Swarm 是 Docker 的原生集群管理工具,它允许你将多个 Docker 主机连接在一起,并在它们之间协调和调度容器的部署。」 随后,又完成对Fig项目的收购,后来改名为Docker Compose,「在Docker Compose产品中首次提出了"容器编排"的概念」。
「什么是"容器编排"呢?」
假如现在用户需要部署的是应用容器 A、数据库容器 B、负载均衡容器 C,那么 Fig 就允许用户把 A、B、C 三个容器定义在一个配置文件中,并且可以
指定它们之间的关联关系,比如容器 A 需要访问数据库容器 B,数据库容器B要先于容器A启动,或者一组容器在一个网络域中,另一组容器在另一个网络域中等等,这些配置文件中定义好后,只需要一个启动命令:docker-compose up
,Docker Compose就会把这些容器的定义和配置交给 Docker API 按照先后顺序依次创建、网络配置、启动,还可以设置容器副本数。
**「2014 年 6 月,基础设施领域的翘楚 Google 公司突然发力,正式宣告了一个名叫 Kubernetes 项目的诞生。而这个项目,如同当年 Docker 项目的横空出世一样,再一次改变了整个容器市场的格局。」**Kubernetes 项目的基础特性,并不是几个工程师突然"拍脑袋"想出来的东西,而是Google 公司在容器化基础设施领域多年来实践经验的沉淀与升华。Kubernetes 项目让人耳目一新的设计理念和号召力,很快就构建出了一个与众不同的容器编排与管理的生态。就这样,Kubernetes 项目在 GitHub 上的各项指标开始一骑绝尘,将 Swarm 项目远远地甩在了身后。
「Kubernetes 在整个社区推进"民主化"架构」,即:从 API 到容器运行时的每一层,Kubernetes 项目都为开发者暴露出了可以扩展的插件机制,鼓励用户通过代码的方式介入到 Kubernetes 项目的每一个阶段。Kubernetes 项目的这个变革的效果立竿见影,很快在整个容器社区中催生出了大量的、基于 Kubernetes API 和扩展接口的二次创新工作,比如:目前热度极高的微服务治理项目 Istio、被广泛采用的有状态应用部署框架 Operator,就这样,在这种鼓励二次创新的整体氛围当中,Kubernetes 社区在 2016 年之后得到了空前的发展。
面对 Kubernetes 社区的崛起和壮大,Docker风光不再,只能选择逐步放弃开源社区而专注于自己的商业化转型。所以,从 2017 年开始,Docker 公司先是将 Docker 项目的容器运行时部分 Containerd捐赠给 CNCF 社区,标志着 Docker 项目已经全面升级成为一个 PaaS 平台;紧接着,Docker 公司宣布将 Docker 项目改名为 Moby,然后交给社区自行维护。2017 年 10 月,Docker 公司出人意料地宣布,将在自己的主打产品 Docker 企业版中内置 Kubernetes 项目,这标志着持续了近两年之久的"编排之争"至此落下帷幕,2022 年 Kubernetes v1.24 发布,Docker Shim 被移除了,CRI Dockerd 登场,CRI Dockerd和Docker关系后续再谈。
「自从 2013 年 Docker 诞生以来,容器一跃成为了 IT 界最热门的话题。而 Kubernetes 则趁着容器的"东风",借助 Google 和 CNCF 的强力"背书",击败了 Docker Swarm,成为了"容器编排"领域的王者至尊。换一个更通俗易懂的说法,那就是:现在 Kubernetes 已经没有了实际意义上的竞争对手,它的地位就如同Linux 一样,成为了事实上的云原生操作系统,是构建现代应用的基石。」
「总结:很多公司的业务都在使用容器技术搭建自己的云平台,使用容器云来支撑业务运行成为了一种趋势。」
「【容器的崛起之路】通过三篇博文大概梳理了容器技术的演进历程,最后,让我们顺着时间轴再一次回顾容器近30年发展过程中,一个个技术就如同"演员"们站上舞台中央表演如同星光般璀璨夺目,又一个个转身缓缓离开那个曾经让它闪耀的地方,带着过去的辉煌时刻的回味和无限思索,技术的车轮不断向前碾压。」
参考资料:张磊《深入剖析Kubernetes》