文章

云原生(二):容器技术

容器就是一个视图隔离、资源可限制、独立文件系统的进程。 是云原生技术里面一个关键的技术点。它本质上解决的是应用程序部署运行的问题。

应用程序部署

在容器技术出现之前,人们部署一个应用程序有两种部署方法。

直接部署

最简单直接的部署方式,将应用程序直接在物理机上部署,然后使用 systemd 类的守护类程序把多个应用程序管理起来。这种方式可以很方便地查看每个业务程序的运行情况并进行手动、自动化的启停操作。但随着业务复杂化,应用程序增多,就不可避免地出现了应用程序之间依赖不兼容的问题。

image-vjxv.png

这会导致程序A和程序B无法在同一台物理机上面进行部署。因为物理机器的成本高昂,如果每当应用程序之间发生依赖冲突后,就新增加一台物理机来解决问题,这就会导致业务成本的不断上涨。这是无法被接受的。那么有没有其他方法将依赖环境互相隔离呢?

虚拟机部署

虚拟机 (VM) 是一种以软件形式模拟物理计算的虚拟环境。

应用程序的运行总是天生地和依赖环境相耦合,如果我们尝试将应用程序和依赖本身完全一体化,把它们当做一个独立的单元去管理,不就能够实现相互隔离了么?

这就不能不提到虚拟化技术。虚拟化技术使用软件,在计算机硬件上创建一个抽象层,能够将单台计算机的硬件元素(处理器、内存、存储等)分成多个虚拟计算机(通常称为虚拟机 (VM))。 每个虚拟机都会运行自己的操作系统 (OS),其行为就像一台独立的计算机,即使它只在一部分实际底层计算机硬件上运行。通过虚拟机管理程序(一个小型软件层)为每个虚拟机分配物理计算资源(例如处理器、内存、存储)。它将每个虚拟机与其他虚拟机隔离,以免相互干扰。每个虚拟机包含客户机操作系统、操作系统运行所需的硬件的虚拟副本以及应用程序及其相关的库和依赖项。

基于虚拟化技术,我们可以实现:

image-tmpk.png

将应用程序和依赖一起打包到了一个虚拟机镜像中,然后以虚拟机镜像作为程序管理部署的基本单位。

因为传统虚拟化技术(如使用虚拟机管理程序)需要为每个虚拟机分配完整的操作系统Guest OS,包括内核、系统库等。这导致了虚拟化技术有如下缺点:

  • 镜像过大,它需要一个完整的 Guest OS,影响在不同的物理机上传输。
  • 启动时间过长,影响应用程序部署效率。
  • 虚拟化后,应用程序性能损耗过大,浪费物理机的性能。

为了解决这些缺点,容器化技术诞生了。

容器化技术

容器只包含应用程序及其依赖项,多个容器可以共享主机操作系统的内核。这使得容器的体积通常很小,可能只有几十 MB 到几百 MB。例如,一个简单的 Node.js 应用容器可能只有几十 MB 大小。容器的启动速度也很快,因为不需要加载完整的操作系统,资源占用主要是应用程序本身和其依赖项,大大提高了资源利用率,在相同的物理服务器上可以部署更多的容器。

镜像问题

虚拟机镜像过大是因为包含了完整的 Guest OS,存储了操作系统运行所需要的基本文件。也就是相当于说,如果我们启动多个相同的虚拟机镜像,那么就会拥有许多重复的文件。甚至说,我们是否能够直接使用宿主机自带的文件?

这就带来了优化空间:对于相同的文件,我们只保留一份或是复用宿主机的文件。

如果说,我们的镜像在运行的时候修改了这个文件呢?可以考虑 Copy-On-Write 的思想。

其次:我们的应用程序如果可以运行到最小的可执行环境中,那么,我们的镜像是否也可以被缩小?

容器化技术采用了联合文件系统(UFS)来降低降低容器镜像的大小,加快容器镜像分发速度。

Union文件系统(UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

启动停止问题

虚拟机镜像包含了完整的 Guest OS,所以每一次启动和停止都要经历一遍完整的操作系统启停动作。那么我们能否去除掉这一个动作呢?

虚拟机技术是基于操作系统的隔离,很明显是不行的。

回到原本的问题上来:我们一开始是因为依赖的问题所以要起个个虚拟机来做隔离。
那么应用程序到底要隔离什么?

在linux中实现一个程序的隔离,主要是以下几方面:

  • 文件系统
  • 进程视图
  • 进程通信
  • 网络
  • 用户和权限

如果我们能够使得我们的应用程序在上述几个方面上都有自己的专属空间,那么就实现了隔离。Linux 提供了基于 Namespace 和 Cgroup 技术的 LXC(Linux Container)。如果我们将应用程序放置于 LXC 中去运行,那么其启停速度会比在虚拟机上更加快速。

性能损耗问题

随着虚拟化技术的发展,通用硬件层面的损耗基本上被消除了,但特殊设备(如GPU、各类I/O设备等)的硬件损耗仍然存在

虚拟机在物理机上起的是完整的操作系统,也就意味着有存在着多套内核,重复的内核会造成不必要的性能损耗。

容器技术通过共用宿主机的内核,共享底层操作系统的系统资源,来减少这些不必要的内核开销。

Docker

现在当我们谈到容器化技术的时候,下意识的反应就是Docker

Docker 使用 Linux 内核和内核功能(例如 Cgroups 和命名空间)来隔离进程,以使它们能够独立运行。Docker 本质上是将应用程序及其依赖项转换成虚拟容器,这些容器可以在任何运行 Windows、MacOS 或 Linux 的计算机系统上运行。

Docker以LXC和UFS类技术作为基础,Docker设计和开发了容器镜像和运行的标准,使得程序和其依赖环境绑定作为独立单元的打包(Dockerfile),具备版本管理(image)、分发(registry)、部署运行以及管理(runtime)等功能,并提供了一套完整易用的工具,Docker 成功地解决了前面提到的应用程序部署痛点。

License:  CC BY 4.0