Docker和虚拟机有什么不同?

4421

我一直在反复阅读Docker文档,试图理解Docker和完整虚拟机之间的区别。它如何在不过重的情况下提供完整的文件系统、隔离的网络环境等等。

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


16
Docker与KVM性能分析:http://bodenr.blogspot.com/2014/05/kvm-and-docker-lxc-benchmarking-with.html本文将介绍Docker和KVM的性能比较。这篇文章使用了基准测试并针对CPU、内存、磁盘IO和网络性能进行了比较。结果表明,Docker在大多数方面都优于KVM,尤其是在启动时间、CPU性能和内存占用方面。但是,在磁盘IO和网络性能方面,KVM表现更好。总体而言,选择哪种虚拟化技术应该考虑到具体的使用情境和需求。 - Dave
1
如果你正在寻找它们的图像之间的区别,请访问以下链接:https://dev59.com/cF4b5IYBdhLWcg3wdxcv?noredirect=1&lq=1 - devesh-ahuja
45
Docker不是一种虚拟机,而是一种配置管理工具。 - aaa90210
41
不要忘记,Docker for Mac和Docker for Windows确实使用虚拟化层。 - Shapeshifter
19
Docker不是一种虚拟机,而是一种配置管理工具。 - aquirdturtle
25个回答

3975
Docker最初使用LinuX容器(LXC),但后来转而使用runC(以前称为libcontainer),它与其主机运行在同一操作系统中,从而允许它共享大量主机操作系统资源。此外,它使用分层文件系统(AuFS)和管理网络。
AuFS是一种分层文件系统,因此您可以拥有一个只读部分和一个写入部分,它们会被合并在一起。您可以将操作系统的常用部分设为只读(并在所有容器之间共享),然后为每个容器提供自己的写入挂载点。
因此,假设您有一个1 GB的容器镜像;如果您想使用完整的虚拟机,则需要拥有1 GB x所需的VM数量。使用Docker和AuFS,您可以在所有容器之间共享1GB的大部分内容,即使您有1000个容器,操作系统的空间也可能只有略微超过1 GB(假设它们都运行相同的操作系统映像)。
一种完整的虚拟化系统会分配自己的资源,并且最小化共享。您可以获得更多的隔离性,但它会更加沉重(需要更多的资源)。使用Docker,您可以获得较少的隔离性,但容器很轻量级(需要更少的资源)。因此,您可以在主机上轻松运行数千个容器,甚至不会有任何问题。尝试使用Xen这样做,除非您有一个真正大的主机,否则我认为这是不可能的。
一个完整的虚拟化系统通常需要几分钟才能启动,而Docker / LXC / runC容器只需几秒钟,甚至不到一秒钟。
每种类型的虚拟化系统都有其优缺点。如果您想要完全隔离并保证资源,则完整的VM是正确的选择。如果您只想将进程互相隔离并希望在适当大小的主机上运行大量进程,则Docker / LXC / runC似乎是正确的选择。
欲了解更多信息,请查看这组博客文章,这些文章很好地解释了LXC的工作原理。
为什么将软件部署到Docker镜像(如果这是正确的术语)比仅仅部署到一致的生产环境更容易?
部署一致的生产环境说起来容易做起来难。即使您使用ChefPuppet等工具,主机和环境之间总是会有操作系统更新和其他变化。
Docker使您能够将操作系统快照到共享镜像中,并轻松地在其他Docker主机上部署。本地、开发、QA、生产等:所有都使用相同的镜像。当然,您可以使用其他工具来实现这一点,但不会像使用Docker那样容易或快速。
这对于测试非常有用;假设您有数千个需要连接到数据库的测试,每个测试都需要一个原始数据库副本并将更改数据。传统的方法是在每个测试之后重置数据库,使用自定义代码或Flyway等工具 - 这可能非常耗时,并且意味着必须串行运行测试。但是,使用Docker,您可以创建数据库的映像,并为每个测试运行一个实例,然后并行运行所有测试,因为您知道它们都将针对数据库的相同快照运行。由于测试在Docker容器中并行运行,它们可以同时在同一台计算机上运行,并且应该更快地完成。试试在完整虚拟机上进行操作。
从评论中看到...
有趣!我想我仍然对“快照操作系统”的概念感到困惑。如果没有制作操作系统映像,那么如何做到这一点?

好的,让我来解释一下。你需要一个基础镜像,然后进行更改,并使用Docker提交这些更改,它会创建一个新的镜像。这个镜像只包含与基础镜像不同的部分。当你想要运行这个镜像时,你还需要基础镜像,并使用分层文件系统将你的镜像叠加在基础镜像之上:正如上面提到的,Docker使用AuFS。AuFS将不同的层合并在一起,你就可以得到想要的内容;你只需要运行它即可。你可以继续添加更多的镜像(层),它将继续仅保存差异。由于Docker通常是在registry中构建基于现成镜像的,因此你很少需要自己“快照”整个操作系统。


除了在 macOS 上,它似乎是一个虚拟机。具体来说,是 LinuxKit 虚拟机。 - Chuck Le Butt

787

了解虚拟化和容器在低层级上的工作原理可能会很有帮助。这将澄清很多事情。

注意:下面的描述有些简化。更多信息请参见参考资料。

虚拟化在低层级上是如何工作的?

在这种情况下,虚拟机管理器接管CPU环 0(或新型CPU中的“根模式”)并截获客户操作系统发出的所有特权调用,以创建客户操作系统拥有自己硬件的假象。有趣的事实:在1998年之前,人们认为在x86架构上无法实现这一点,因为没有办法进行此类截获。VMware团队 是第一个 想到了重写客户操作系统特权调用的内存可执行字节来实现这一点。

总体效果是,虚拟化允许您在同一硬件上运行两个完全不同的操作系统。每个客户操作系统都经历引导、加载内核等所有过程。您可以拥有非常严格的安全性。例如,客户操作系统无法完全访问主机操作系统或其他客户操作系统并破坏其中的内容。

容器在低层级别如何工作?

大约在 2006 年,一些谷歌员工们实现了一个新的内核级特性,叫做 命名空间(然而这个想法早在之前就在FreeBSD中存在)。操作系统的一个功能是允许进程共享全局资源,比如网络和磁盘。如果这些全局资源被包装在命名空间中,那么它们只能被运行在同一命名空间的进程所看到。例如,你可以获得一块磁盘并将其放入命名空间X中,然后运行在命名空间Y中的进程无法看到或访问它。同样地,运行在命名空间X中的进程无法访问分配给命名空间Y的任何内存。当然,命名空间X中的进程也无法看到或与命名空间Y中的进程交互。这为全局资源提供了一种虚拟化和隔离方法。这就是 Docker 的工作原理:每个容器都在自己的命名空间中运行,但使用的是完全相同的内核。隔离是因为内核知道分配给进程的命名空间,在 API 调用过程中,它确保进程只能访问自己命名空间中的资源。

容器与虚拟机的限制现在应该很明显了:你不能像在虚拟机中那样在容器中运行完全不同的操作系统。但是,你可以运行不同的Linux发行版,因为它们共享相同的内核。隔离级别不如虚拟机强。事实上,在早期实现中,“客户”容器有一种方式可以接管主机。此外,当你加载一个新容器时,整个操作系统的副本并不像在虚拟机中那样启动。所有容器共享相同的内核。这就是为什么容器非常轻量的原因。与虚拟机不同的是,你不必为容器预先分配大量内存,因为我们不运行操作系统的新副本。这使得在一个操作系统上运行数千个容器成为可能,同时对它们进行沙箱处理,如果我们在自己的虚拟机中运行单独的操作系统副本,则可能不可能。

56
哇,感谢你提供了这么好的低级别解释(以及历史事实)。我一直在寻找这样的解释,但以上内容中没有找到。你所说的“你可以运行不同发行版的Linux,因为它们共享相同的内核”,是什么意思?你是在说一个客户容器必须具有完全相同的Linux内核版本,还是并不重要?如果并不重要,那如果我在客户端调用一个只支持客户端内核的操作系统命令会怎样呢?或者例如一个在客户机内核中修复而在主机内核中未修复的错误。所有客户机都会表现出这个错误,对吗?即使这些客户端已经打了补丁。 - Jeach
2
@Jeach 你好,正如Shital Shah所说,“每个容器只共享同一个内核”。因此,我对Docker Centos镜像感到困惑,因为容器中的所有应用程序在必要时都应该使用来自主机的内核。为什么还需要另一个操作系统镜像内核(我的意思是Centos镜像)呢?谢谢。 - Joe.wang

706

很好的答案。为了了解容器与虚拟机之间的图像表示,请查看下面的图像。

输入图像描述

来源


29
据我所了解,“Docker引擎”上方应该有一个共享的Linux内核。然后通常还有共享的二进制文件/库。在那之后是针对每个容器特定的二进制文件/库和应用程序。如果我理解错误,请纠正我。我错了。Docker镜像与主机共享内核,请参阅 http://superuser.com/questions/889472/docker-containers-have-their-own-kernel-or-not。但是,为了说明容器的联合文件系统,可以在docker引擎直接上方有一个共享的libs/bins层。 - Betamos
18
我对上面的图片有疑问,因为虚拟化监控程序可以安装在裸机/基础设施上,但 Docker 目前还不能。 - reza
@reza,我同意在裸金属上可以安装Hypervisor,但问题是Docker被推荐用于容器化应用程序,并且如何限制或避免不需要/不适用于某些场景的虚拟化。Ken Cochrane在https://dev59.com/9mQo5IYBdhLWcg3wdPAa#16048358中详细解释了这一点。 - manu97
@reza,假设您使用的不是Windows容器,则有一个被认为是Linux的“主机操作系统”。如果您认为裸金属是空白驱动器,那么Docker不能在那里安装。 - ryanwebjackson

377

我喜欢 Ken Cochrane 的回答

但我想增加另外一个角度,这里没有详细讨论。在我的观点中,Docker 的整个过程也有所不同。相较于虚拟机,Docker 不仅仅是关于硬件资源的最佳共享,更重要的是它提供了一个打包应用的“系统”(最好是一组微服务,但不是必须的)。

对我来说,它填补了开发者工具(如rpm、Debian软件包、Maven、npm + Git 等)和运维工具(如Puppet、VMware、Xen 等)之间的空缺。

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

你的问题假设存在一致的生产环境。但如何保持其一致性呢? 考虑一些数量(>10)的服务器和应用程序,以及管道中的阶段。

为了保持同步,你将开始使用诸如 Puppet、Chef 或自己的配置脚本、未发布的规则和/或大量文档... 理论上,服务器可以无限期地运行,并保持完全一致和最新。实践失败了,无法完全管理服务器的配置,因此存在相当大的配置漂移范围,以及对正在运行的服务器的意外更改。

因此,有一种被称为不可变服务器的已知模式可以避免这种情况。但是,由于 Docker 之前使用的 VM 的限制,不可变服务器模式并不受欢迎。处理几个 GB 大小的镜像、移动这些大型镜像以更改应用程序中的某些字段非常麻烦,这是可以理解的...

在 Docker 生态系统中,您永远不需要在“小更改”时移动几个 GB(感谢 aufs 和 Registry),而且您不需要担心将应用程序打包到 Docker 容器中会降低性能。您也不需要担心该镜像的版本。

最后,您甚至通常可以在 Linux 笔记本电脑上重现复杂的生产环境(如果在您的情况下无法工作,请勿联系我 ;))

当然,您可以在 VM 中启动 Docker 容器(这是一个好主意)。在 VM 级别上减少服务器配置。所有这些都可以由 Docker 管理。

P.S. 同时,Docker 使用其自己的实现“libcontainer”,而不是 LXC。但仍可使用 LXC。


1
似乎将git与rpm和dpkg等工具放在一起有些奇怪。我提到这个问题是因为我看到尝试使用版本控制系统如git作为分发/打包工具的尝试会带来很多混乱。 - blitzen9872
5
他说的没错,Git可以用作包管理工具,例如Bower内部基本上就是一个下载Git标签的高级命令行界面。 - actual_kangaroo
3
将应用程序打包到容器中是一种有趣且有效的方法。然而,如果您使用Docker来打包它,这将是过度设计,因为没有直接支持依赖项或任何共享库。这正是像Ubuntu Snap和Redhat的Flatpak这样的新打包技术正在努力实现的目标。在我看来,其中一种打包技术将获胜并成为Linux打包的未来。 - yosefrow
@blitzen9872同意这一点。但是之所以提到它,正是因为在实践中经常用于分发,再次强调我也不喜欢它。 - aholbreich

332
Docker不是一种虚拟化方法。它依赖于其他实现基于容器的虚拟化或操作系统级虚拟化的工具。为此,Docker最初使用LXC驱动程序,然后转移到现在被重命名为runc的libcontainer。Docker主要专注于自动化应用程序容器内部的部署。应用程序容器旨在打包和运行单个服务,而系统容器旨在运行多个进程,如虚拟机。因此,在容器化系统上,Docker被视为容器管理或应用程序部署工具。
为了了解它与其他虚拟化的区别,让我们先了解虚拟化及其类型。然后,就更容易理解其中的区别了。
虚拟化
在其构思形式中,它被认为是一种逻辑上划分大型机的方法,允许多个应用程序同时运行。然而,当公司和开源社区能够以某种方式处理特权指令并允许在单个基于x86的系统上同时运行多个操作系统时,情况发生了 drastical 变化。
Hypervisor

虚拟机监控程序负责创建虚拟环境,供客户虚拟机运行。它监督客户系统并确保资源按需分配给客户。虚拟机监控程序位于物理机和虚拟机之间,并为虚拟机提供虚拟化服务。为了实现这一点,它拦截虚拟机上的客户操作系统操作,并在主机操作系统上模拟操作。

云计算等虚拟化技术的快速发展推动了虚拟化的进一步应用,使用诸如Xen、VMware Player、KVM等虚拟化程序以及在普通处理器中加入硬件支持(例如Intel VT和AMD-V),使得多个虚拟服务器可以在单个物理服务器上创建。

虚拟化类型

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

  • 仿真
  • 半虚拟化
  • 基于容器的虚拟化

仿真

仿真,也称为完全虚拟化,在软件中完全运行虚拟机操作系统内核。这种类型使用的虚拟化程序被称为第二类虚拟化程序。它安装在主机操作系统的顶部,负责将客户操作系统内核代码转换为软件指令。翻译完全是通过软件完成的,不需要硬件参与。仿真使得可以运行任何支持被模拟环境的未修改操作系统。这种虚拟化的缺点是增加了额外的系统资源开销,导致性能比其他虚拟化类型下降。

Emulation

此类软件包括VMware Player、VirtualBox、QEMU、Bochs、Parallels等。

半虚拟化

半虚拟化,也称为Type 1 hypervisor,直接运行在硬件上或“裸机”上,并直接向运行在其上的虚拟机提供虚拟化服务。它帮助操作系统、虚拟化硬件和真实硬件协作以达到最佳性能。这些hypervisors通常具有较小的占用空间,本身不需要大量资源。

此类软件包括Xen、KVM等。

Paravirtualization

基于容器的虚拟化

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

Container-based virtualization

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

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

Linux Control Groups(cgroups)子系统是启用基于容器的虚拟化的下一个主要组件,用于分组进程并管理其聚合资源消耗。它通常用于限制容器的内存和CPU消耗。由于容器化的Linux系统只有一个内核,并且内核完全看到容器,因此只有一级资源分配和调度。

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

容器 vs 虚拟机

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

由于基于容器的虚拟化对主机机器几乎没有额外开销,因此具有接近本地性能。

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

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

与虚拟机镜像相比,容器状态(Docker 或 LXC 镜像)体积小,因此容器镜像易于分发。

通过 cgroups 实现容器中的资源管理。cgroups 不允许容器消耗超过分配给它们的资源。然而,目前为止,虚拟机中可见主机机器的所有资源,但不能使用。可以通过同时在容器和主机机器上运行 tophtop 来实现此功能。所有环境的输出将看起来类似。

更新:

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

如果容器之所以可行,是因为 Linux 内核中可用的特性,那么显然的问题是非 Linux 系统如何运行容器。Docker for Mac 和 Windows 都使用 Linux VM 来运行容器。Docker 工具箱曾经在 Virtual Box VM 中运行容器。但是最新版本的 Docker 在 Windows 上使用 Hyper-V,在 Mac 上使用 Hypervisor.framework。
现在,让我详细介绍一下 Docker for Mac 如何运行容器。
Docker for Mac 使用 https://github.com/moby/hyperkit 模拟虚拟化功能,Hyperkit 在其核心中使用 hypervisor.framework。Hypervisor.framework 是 Mac 的本地虚拟化解决方案。Hyperkit 还使用 VPNKit 和 DataKit 对网络和文件系统进行命名空间管理。
Docker 运行在 Mac 上的 Linux VM 是只读的。但是,您可以通过运行以下命令进入 Linux VM: screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty 现在,我们甚至可以检查此 VM 的内核版本: # uname -a Linux linuxkit-025000000001 4.9.93-linuxkit-aufs #1 SMP Wed Jun 6 16:86_64 Linux 所有容器都在这个 VM 中运行。
由于 hypervisor.framework 的一些限制,Docker 在 Mac 上不会暴露 docker0 网络接口。因此,您无法从主机访问容器。目前,docker0 仅在虚拟机内部可用。
Hyper-v 是 Windows 的本地虚拟化管理程序。他们还试图利用 Windows 10 的能力本地运行 Linux 系统。

6
非常简洁的答案。但需要注意/补充的是,使用WSL2和Windows运行“真正的”Linux内核后,不再需要Hyper-V,容器可以在本地运行。这对性能产生了显着影响。 - andreee
1
似乎在较新版本的Docker + OSX中,对于命令“screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty”,tty不存在。另一种选择是使用netcat,如下所示:nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock - user1503941
@andreee 我相信 macOS 没有类似的替代品,这就解释了 macOS 用户所见到的 Docker 性能较差。 - Chuck Le Butt

229

这里大部分回答都谈到了虚拟机。我会给你一个简短的回答,这个回答在我使用 Docker 的过去几年中帮助我最多。它就是:

Docker 只是运行一个进程的高级方法,而不是虚拟机。

现在,让我更详细地解释一下这是什么意思。虚拟机是完全不同的东西。我觉得解释一下 Docker 会比解释虚拟机更有用。特别是因为在这里有很多好的答案告诉你当别人说“虚拟机”时确切的意思。所以...

Docker 容器只是一个 (以及它的子进程) 在主机系统内核中使用cgroups 进行隔离的进程。你可以通过在主机上运行 ps aux 命令来查看 Docker 容器进程。例如,在“容器”内启动 apache2 只是在主机上启动 apache2 的一个特殊进程。它只是被隔离在机器上的其他进程之外。需要注意的是,容器不会存在于容器化进程之外。当你的进程结束时,你的容器也会结束。这是因为 Docker 使用你的应用程序替换了容器内的 pid 1pid 1 通常是 init 系统)。最后关于 pid 1 的这一点非常重要。

就这些容器进程使用的文件系统而言,Docker使用基于UnionFS的镜像,当您执行docker pull ubuntu时,下载的就是它。每个“镜像”实际上都是一系列层及其相关元数据。层次结构的概念在此非常重要。每个层次结构只是对其下面层次结构所做更改的记录。例如,在构建Docker容器时,如果您在Dockerfile中删除文件,实际上是在最后一层之上创建了一层,该层表示“已删除此文件”。顺带提一下,这就是为什么您可以从文件系统中删除大文件,但镜像的磁盘空间仍然相同的原因。该文件仍然存在在当前层以下的层中。层级本身只是文件的tar包。您可以使用docker save --output /tmp/ubuntu.tar ubuntu测试这一点,然后使用cd /tmp && tar xvf ubuntu.tar查看。然后您可以四处看看。所有看起来像长哈希值的目录实际上都是各自的单独层。每个层包含文件(layer.tar)和有关该特定层次结构的元数据(json)。这些层仅描述对文件系统所做的更改,这些更改作为“在其原始状态之上”的层级进行保存。读取“当前”数据时,文件系统会将数据读取为仅查看顶部层次结构的样子。这就是为什么该文件似乎已被删除,即使它仍然存在于“先前”的层中,因为文件系统仅查看最上面的层次结构。这使得完全不同的容器可以共享它们的文件系统层,即使每个容器的最上面的层次结构都发生了重大变化。当容器共享它们的基础映像层时,这可以节省大量磁盘空间。但是,当您通过卷将主机系统中的目录和文件挂载到容器中时,这些卷会“绕过”UnionFS,因此更改不会存储在层级结构中。

Docker中的网络连接是通过使用以太网桥(在主机上称为docker0)和为主机上的每个容器创建的虚拟接口来实现的。它在docker0上创建了一个虚拟子网,使您的容器可以“之间”相互通信。这里有许多网络选项,包括为您的容器创建自定义子网,以及共享主机的网络堆栈以供您的容器直接访问。

Docker正在快速发展。它的文档是我见过的最好的文档之一。它通常写得很好,简明准确。我建议您查看可用的文档以获取更多信息,并相信文档比您在网上阅读的任何其他内容(包括Stack Overflow)更可靠。如果您有具体问题,强烈建议加入Freenode IRC上的#docker并在那里提问(您甚至可以使用Freenode的Web聊天工具进行提问!)。


很好的解释,让我对很多事情有了更深的理解,尤其是在使用Docker容器时能够产生共鸣。 - gilf0yle

168
通过本文,我们将在VM和LXC之间画出一些区别的线条。 让我们先定义它们。 VM: 虚拟机模拟物理计算环境,但CPU、内存、硬盘、网络和其他硬件资源的请求由虚拟化层来管理,该层将这些请求转换为底层物理硬件。
在此上下文中,VM被称为Guest,而其运行的环境被称为host。 LXCs: Linux容器(LXC)是操作系统级的功能,使得可以在一个控制主机(LXC主机)上运行多个隔离的Linux容器。Linux容器作为轻量级替代VM,因为它们不需要Hypervisors,如Virtualbox、KVM、Xen等。
现在,除非你被Alan(Hangover系列电影中的Zach Galifianakis)灌醉并在拉斯维加斯呆了一年,否则你应该很清楚最近几个月关于Linux容器技术巨大的兴趣激增,如果我要具体说明一个容器项目,在全球范围内引起了轰动,那就是Docker,由此引发了一些共鸣,认为云计算环境应该放弃虚拟机(VM),并用容器替换它们,因为容器的开销更低,性能可能更好。
但是大问题是,这可行吗? 这将是明智的吗?
a. LXCs仅限于Linux实例。 它可能是不同版本的Linux(例如,在CentOS主机上运行Ubuntu容器,但仍然是Linux)。 同样,基于Windows的容器仅限于Windows实例。 如果我们看VM,它们具有更广泛的范围,并且使用虚拟化程序您不受限于操作系统Linux或Windows。b. LXCs的开销较低,性能比虚拟机更好。基于LXC技术构建的Docker等工具为开发人员提供了运行应用程序的平台,并使操作人员有能力在生产服务器或数据中心上部署同一容器。它试图使开发人员运行应用程序、启动和测试应用程序以及操作人员部署该应用程序之间的体验无缝,因为这是摩擦所在的地方,而DevOps的目的就是打破这些隔离。 因此,最好的方法是云基础设施提供商应该倡导适当使用虚拟机和LXC,因为它们各自适合处理特定的工作量和场景。 现阶段放弃虚拟机并不切实际。因此,虚拟机和LXC都有各自独立存在和重要性。

123

Docker用于封装应用程序及其所有依赖项。

虚拟化程序则封装了一个操作系统,能够在其上运行任何它本来可以在物理计算机上运行的应用程序。


1
我正在学习LXC,如果我错了,请纠正我,但它可能是某种虚拟环境?但显然更广泛,不仅限于Python。 - NeoVe
2
我最喜欢这个答案。它简单明了,直戳要点。现在,既然有了基本的LXC和虚拟化技术的理解,其他阅读材料中的细节也就会变得清晰明了。 - Phil
2
@Phil 在我先阅读了上面详细的答案之后,确实有用。 - johnny
我认为他们想知道如何封装。这是一个重要的部分,可以展示它们之间的区别,但你没有回答。 - Light.G
这并不是非常准确的 - 可能会有一个只包含操作系统文件的容器 - 这是因为操作系统内核不是 Docker 容器镜像的一部分,而是在虚拟机镜像中。 - ryanwebjackson

74
他们两者非常不同。Docker 是轻量级的,使用 LXC/libcontainer(这依赖于内核命名空间和 cgroups),并且没有像 hypervisor、KVM 和 Xen 那样的机器/硬件仿真。
Docker 和 LXC 更适用于沙箱、容器化和资源隔离。它使用宿主操作系统的(目前仅限于 Linux 内核)克隆 API,为 IPC、NS(挂载)、网络、PID、UTS 等提供命名空间。
那么内存、I/O、CPU 等呢?这是通过 cgroups 控制的,在其中您可以创建带有某些资源(CPU、内存等)规范/限制的组,并将进程放入其中。在 LXC 之上,Docker 提供了存储后端(http://www.projectatomic.io/docs/filesystems/),例如联合挂载文件系统,您可以在其中添加层并在不同的挂载命名空间之间共享层。
这是一个强大的功能,其中基本映像通常是只读的,只有当容器修改层中的某些内容时,它才会将某些内容写入可读写分区(即写时拷贝)。它还提供许多其他包装器,如镜像注册表和版本控制。

使用普通的LXC需要提供一些rootfs或共享rootfs,当共享时,更改会反映在其他容器中。由于增加了许多这些功能,Docker比LXC更受欢迎。在嵌入式环境中,LXC流行用于实现围绕暴露给外部实体(如网络和UI)的进程的安全性。在期望一致的生产环境的云多租户环境中,Docker很受欢迎。

普通的虚拟机(例如VirtualBox和VMware)使用虚拟化程序和相关技术,其具有专用固件,成为第一个操作系统(主机操作系统或客户机操作系统0)的第一层,或者是在主机操作系统上运行的软件,以提供硬件仿真,如CPU、USB/附件、内存、网络等,对于客户机操作系统。截至2015年,虚拟机在高安全性的多租户环境中仍然很受欢迎。

Docker/LXC几乎可以在任何便宜的硬件上运行(少于1GB的内存也可以,只要您有更新的内核),而普通的虚拟机需要至少2GB的内存等才能进行有意义的操作。但是,在Windows等操作系统上不支持在主机操作系统上运行Docker(截至2014年11月),而可以在Windows、Linux和Mac上运行多种类型的虚拟机。

这是来自docker/rightscale的图片: 这是来自rightscale的图片

仅供更新,自Windows 10/11和Server 2019起,启用WSL(Windows子系统)后支持Windows下的Docker,这需要安装Ubuntu等内核。 - JJ Ward

54

1. 轻量级

这可能是许多Docker学习者的第一印象。

首先,Docker镜像通常比虚拟机镜像小得多,便于构建、复制和共享。

其次,Docker容器可以在几毫秒内启动,而虚拟机需要数秒钟。

2. 分层文件系统

这是Docker的另一个关键特性。镜像有层,不同的镜像可以共享层,使其更节省空间,构建更快。

如果所有容器都使用Ubuntu作为它们的基本镜像,则不是每个镜像都有自己的文件系统,而是共享相同的底层Ubuntu文件,并且只有它们自己的应用程序数据不同。

3. 共享操作系统内核

将容器看作进程!

在主机上运行的所有容器实际上是带有不同文件系统的一堆进程。它们共享同一个操作系统内核,仅封装系统库和依赖项。

这对大多数情况很好(不需要额外的操作系统内核维护),但在容器之间需要严格隔离时可能会出问题。

为什么重要?

所有这些看起来都像是改进,而不是革命。好吧,量的积累会导致质的转变

想想应用程序部署。如果我们想要部署新软件(服务)或升级一个现有的,最好更改配置文件和进程,而不是创建一个新的虚拟机。因为创建带有更新服务的虚拟机,测试它(在开发和QA之间共享),并将其部署到生产环境需要数小时,甚至数天。如果出了任何问题,就必须重新开始,浪费更多时间。所以,使用配置管理工具(puppet、saltstack、chef等)安装新软件,下载新文件更好。

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

它改变了服务的交付方式。我们需要应用程序,但必须维护虚拟机(这很麻烦,与我们的应用程序关系不大)。Docker让您专注于应用程序并使一切变得更加顺畅。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接