我一直在重读Docker文档,试图理解Docker和完整VM之间的区别。它是如何设法提供一个完整的文件系统、隔离的网络环境等而不那么沉重的?

为什么将软件部署到Docker映像(如果这是正确的术语)比简单地部署到一致的生产环境更容易?


当前回答

了解虚拟化和容器如何在低级别上工作可能会有所帮助。这将澄清很多事情。

注意:我在下面的描述中简化了一点。有关详细信息,请参阅参考文献。

虚拟化如何在低级别工作?

在这种情况下,VM管理器接管CPU环0(或较新CPU中的“根模式”),并拦截来宾操作系统发出的所有特权调用,以产生来宾操作系统拥有自己硬件的错觉。有趣的事实:在1998年之前,人们认为在x86架构上实现这一点是不可能的,因为没有办法进行这种拦截。VMware的员工是第一个有想法重写内存中的可执行字节以供来宾操作系统的特权调用来实现这一点的人。

其净效果是虚拟化允许您在同一硬件上运行两个完全不同的操作系统。每个来宾操作系统都要经过引导、加载内核等所有过程。例如,来宾操作系统无法完全访问主机操作系统或其他来宾操作系统,从而造成混乱。

容器如何在低液位下工作?

2006年左右,包括谷歌员工在内的一些人实现了一个名为名称空间的新内核级功能(然而,这个想法早就存在于FreeBSD中)。操作系统的一个功能是允许在进程之间共享网络和磁盘等全局资源。如果这些全局资源被包装在命名空间中,以便它们只对在同一命名空间中运行的那些进程可见,该怎么办?例如,您可以获取一块磁盘并将其放在命名空间X中,然后在命名空间Y中运行的进程无法看到或访问它。同样,命名空间X中的进程无法访问分配给命名空间Y的内存中的任何内容。当然,X中的程序无法看到或与命名空间Y中的进程对话。这为全局资源提供了一种虚拟化和隔离。Docker是这样工作的:每个容器都在自己的命名空间中运行,但使用与所有其他容器完全相同的内核。之所以发生隔离,是因为内核知道分配给进程的命名空间,并且在API调用期间,它确保进程只能访问自己命名空间中的资源。

容器与虚拟机的局限性现在应该很明显:你不能像虚拟机那样在容器中运行完全不同的操作系统。但是,您可以运行不同的Linux发行版,因为它们共享相同的内核。隔离级别不如VM中的隔离级别强。事实上,在早期的实现中,“来宾”容器可以接管主机。您还可以看到,当您加载一个新容器时,OS的整个新副本并不像在VM中那样启动。所有容器共享同一内核。这就是为什么集装箱重量轻。与VM不同的是,您不必为容器预先分配大量内存,因为我们没有运行新的OS副本。这允许在一个操作系统上运行数千个容器,同时对它们进行装箱,如果我们在它们自己的VM中运行操作系统的单独副本,这可能是不可能的。

其他回答

了解虚拟化和容器如何在低级别上工作可能会有所帮助。这将澄清很多事情。

注意:我在下面的描述中简化了一点。有关详细信息,请参阅参考文献。

虚拟化如何在低级别工作?

在这种情况下,VM管理器接管CPU环0(或较新CPU中的“根模式”),并拦截来宾操作系统发出的所有特权调用,以产生来宾操作系统拥有自己硬件的错觉。有趣的事实:在1998年之前,人们认为在x86架构上实现这一点是不可能的,因为没有办法进行这种拦截。VMware的员工是第一个有想法重写内存中的可执行字节以供来宾操作系统的特权调用来实现这一点的人。

其净效果是虚拟化允许您在同一硬件上运行两个完全不同的操作系统。每个来宾操作系统都要经过引导、加载内核等所有过程。例如,来宾操作系统无法完全访问主机操作系统或其他来宾操作系统,从而造成混乱。

容器如何在低液位下工作?

2006年左右,包括谷歌员工在内的一些人实现了一个名为名称空间的新内核级功能(然而,这个想法早就存在于FreeBSD中)。操作系统的一个功能是允许在进程之间共享网络和磁盘等全局资源。如果这些全局资源被包装在命名空间中,以便它们只对在同一命名空间中运行的那些进程可见,该怎么办?例如,您可以获取一块磁盘并将其放在命名空间X中,然后在命名空间Y中运行的进程无法看到或访问它。同样,命名空间X中的进程无法访问分配给命名空间Y的内存中的任何内容。当然,X中的程序无法看到或与命名空间Y中的进程对话。这为全局资源提供了一种虚拟化和隔离。Docker是这样工作的:每个容器都在自己的命名空间中运行,但使用与所有其他容器完全相同的内核。之所以发生隔离,是因为内核知道分配给进程的命名空间,并且在API调用期间,它确保进程只能访问自己命名空间中的资源。

容器与虚拟机的局限性现在应该很明显:你不能像虚拟机那样在容器中运行完全不同的操作系统。但是,您可以运行不同的Linux发行版,因为它们共享相同的内核。隔离级别不如VM中的隔离级别强。事实上,在早期的实现中,“来宾”容器可以接管主机。您还可以看到,当您加载一个新容器时,OS的整个新副本并不像在VM中那样启动。所有容器共享同一内核。这就是为什么集装箱重量轻。与VM不同的是,您不必为容器预先分配大量内存,因为我们没有运行新的OS副本。这允许在一个操作系统上运行数千个容器,同时对它们进行装箱,如果我们在它们自己的VM中运行操作系统的单独副本,这可能是不可能的。

Docker不是一种虚拟化方法。它依赖于实际实现基于容器的虚拟化或操作系统级虚拟化的其他工具。为此,Docker最初使用的是LXC驱动程序,然后转移到libcontainer,现在改名为runc。Docker主要致力于自动化应用程序容器内的应用程序部署。应用程序容器设计用于打包和运行单个服务,而系统容器设计用于运行多个进程,如虚拟机。因此,Docker被认为是容器化系统上的容器管理或应用程序部署工具。

为了了解它与其他虚拟化的区别,我们来了解一下虚拟化及其类型。那么,更容易理解两者之间的区别。

虚拟化

在其构想形式中,它被认为是一种逻辑上划分大型机以允许多个应用程序同时运行的方法。然而,当公司和开源社区能够提供一种以某种方式处理特权指令的方法,并允许多个操作系统在一个基于x86的系统上同时运行时,情况发生了巨大变化。

管理程序

管理程序负责创建来宾虚拟机运行的虚拟环境。它监督宾客系统,并确保在必要时为宾客分配资源。管理程序位于物理机和虚拟机之间,并向虚拟机提供虚拟化服务。为了实现它,它拦截虚拟机上的来宾操作系统操作,并模拟主机操作系统上的操作。

虚拟化技术(主要是云技术)的快速发展进一步推动了虚拟化的使用,允许在虚拟机管理程序(如Xen、VMware Player、KVM等)的帮助下,在单个物理服务器上创建多个虚拟服务器,并将硬件支持纳入商品处理器(如Intel VT和AMD-V)。

虚拟化类型

虚拟化方法可以根据其模拟客户操作系统的硬件和模拟客户操作环境的方式进行分类。主要有三种类型的虚拟化:

仿真准虚拟化基于容器的虚拟化

仿真

仿真(也称为完全虚拟化)完全在软件中运行虚拟机OS内核。此类型中使用的管理程序称为类型2管理程序。它安装在主机操作系统的顶部,负责将来宾操作系统内核代码转换为软件指令。翻译完全由软件完成,不需要硬件参与。仿真使运行任何支持被仿真环境的未修改操作系统成为可能。这种虚拟化的缺点是额外的系统资源开销,与其他类型的虚拟化相比,这会导致性能下降。

此类别中的示例包括VMware Player、VirtualBox、QEMU、Bochs、Parallels等。

准虚拟化

准虚拟化(Paravirtualization)也称为第1类虚拟机管理程序,直接在硬件或“裸机”上运行,并直接向其上运行的虚拟机提供虚拟化服务。它帮助操作系统、虚拟化硬件和真实硬件协作以实现最佳性能。这些虚拟机监控程序通常占地面积很小,本身不需要大量资源。

此类别中的示例包括Xen、KVM等。

基于容器的虚拟化

基于容器的虚拟化,也称为操作系统级虚拟化,支持在单个操作系统内核中执行多个独立的执行。它具有最佳的性能和密度,并具有动态资源管理功能。这种类型的虚拟化提供的隔离虚拟执行环境被称为容器,可以被视为一组可跟踪的进程。

通过添加到Linux内核2.6.24版本中的命名空间特性,容器的概念成为可能。容器将其ID添加到每个进程,并将新的访问控制检查添加到每个系统调用。它由clone()系统调用访问,该系统调用允许创建先前全局命名空间的单独实例。

命名空间可以以多种不同的方式使用,但最常见的方法是创建一个独立的容器,该容器对容器外部的对象没有可见性或访问权限。在容器内运行的进程似乎在正常的Linux系统上运行,尽管它们与位于其他命名空间中的进程共享底层内核,其他类型的对象也是如此。例如,当使用命名空间时,容器内的根用户不会被视为容器外的根用户,从而增加了额外的安全性。

Linux控制组(cgroups)子系统是实现基于容器的虚拟化的下一个主要组件,用于对进程进行分组并管理其总资源消耗。它通常用于限制容器的内存和CPU消耗。由于容器化的Linux系统只有一个内核,并且内核对容器具有完全的可见性,因此只有一个级别的资源分配和调度。

Linux容器有多种管理工具,包括LXC、LXD、systemd nspawn、lmctfy、Warden、Linux VServer、OpenVZ、Docker等。

容器与虚拟机

与虚拟机不同,容器不需要启动操作系统内核,因此可以在不到一秒钟的时间内创建容器。这一特性使基于容器的虚拟化比其他虚拟化方法更为独特和可取。

由于基于容器的虚拟化几乎不会或根本不会增加主机的开销,因此基于容器的虚化具有接近本机的性能

对于基于容器的虚拟化,与其他虚拟化不同,不需要额外的软件。

主机上的所有容器共享主机的调度程序,从而节省了额外的资源。

与虚拟机映像相比,容器状态(Docker或LXC映像)的大小较小,因此容器映像易于分发。

容器中的资源管理是通过cgroups实现的。Cgroups不允许容器消耗比分配给它们的资源更多的资源。然而,到目前为止,主机的所有资源在虚拟机中都是可见的,但无法使用。这可以通过同时在容器和主机上运行top或htop来实现。所有环境的输出看起来都很相似。

更新:

Docker如何在非Linux系统中运行容器?

如果由于Linux内核中的可用特性,容器是可能的,那么显而易见的问题是非Linux系统如何运行容器。Docker for Mac和Windows都使用Linux虚拟机来运行容器。Docker工具箱用于在Virtual Box VM中运行容器。但是,最新的Docker在Windows中使用Hyper-V,在Mac中使用Hypervisor.framework。

现在,让我详细描述Docker for Mac如何运行容器。

Docker for Mac使用https://github.com/moby/hyperkit为了模拟hypervisor功能,Hyperkit在其核心中使用hypervisor.framework。Hypervisor.framework是Mac的本地管理程序解决方案。Hyperkit还使用VPNKit和DataKit分别命名网络和文件系统。

Docker在Mac上运行的Linux VM是只读的。但是,您可以通过运行以下命令来实现:

screen~/Library/Containers/com.docker.docker/Data/vms/0/tty。

现在,我们甚至可以检查此VM的内核版本:

#uname-aLinux linuxkit-025000000001 4.9.93-linuxkit-aufs#1 SMP 6月6日星期三16:86_64 Linux。

所有容器都在此VM内运行。

hypervisor.framework有一些限制。因为Docker没有在Mac中公开docker0网络接口。因此,您无法从主机访问容器。目前,docker0仅在VM中可用。

Hyper-v是Windows中的本机管理程序。他们还试图利用Windows 10的功能以本机方式运行Linux系统。

好答案。为了获得容器与VM的图像表示,请查看下面的一个。

来源

1.重量轻

这可能是许多码头工人学习者的第一印象。

首先,docker映像通常比VM映像小,因此易于构建、复制和共享。

第二,Docker容器可以在几毫秒内启动,而VM可以在几秒钟内启动。

2.分层文件系统

这是Docker的另一个关键特性。图像具有图层,不同的图像可以共享图层,从而更节省空间,构建速度更快。

如果所有容器都使用Ubuntu作为它们的基本映像,那么不是每个映像都有自己的文件系统,而是共享相同的下划线Ubuntu文件,并且只在它们自己的应用程序数据上有所不同。

3.共享OS内核

将容器视为进程!

在主机上运行的所有容器实际上都是一堆具有不同文件系统的进程。它们共享相同的OS内核,只封装系统库和依赖项。

这在大多数情况下都很好(没有额外的OS内核维护),但如果容器之间需要严格隔离,则可能会出现问题。

为什么重要?

所有这些似乎都是进步,而不是革命。好吧,数量的积累导致质量的转变。

考虑应用程序部署。如果我们想部署一个新的软件(服务)或升级一个,最好是更改配置文件和进程,而不是创建一个新VM。因为创建一个具有更新服务的VM,测试它(开发人员和QA之间共享),部署到生产需要几个小时,甚至几天。如果出了什么问题,你必须重新开始,浪费更多的时间。因此,使用配置管理工具(木偶、盐堆、厨师等)安装新软件,最好下载新文件。

说到docker,不可能使用新创建的docker容器来替换旧容器。维护更容易!构建一个新映像,与QA共享,测试,部署它只需要几分钟(如果一切都是自动化的),最坏的情况下需要几个小时。这被称为不可变基础设施:不要维护(升级)软件,而是创建一个新的。

它改变了服务的交付方式。我们需要应用程序,但必须维护VM(这是一个难题,与我们的应用程序无关)。Docker让你专注于应用程序,让一切变得流畅。

在我看来,这取决于你的应用程序的需求,为什么决定部署到Docker,因为Docker根据其功能将应用程序分解为小部分,这变得有效,因为当一个应用程序/功能出现错误时,它对其他应用程序没有影响,与使用完整的vm相比,它的配置会更慢、更复杂,但在某些方面比码头工人更安全