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个回答

4

容器可以将库和软件包与系统隔离,使您可以安装同一软件的不同版本和库而不会发生冲突。它在空闲时使用最少的存储和内存,几乎没有开销,使用相同的基本操作系统内核和可用的库,如果可能,有一个很小的增量差异。您可以直接或间接地将硬件暴露给容器,以便您可以使用加速,比如GPU进行计算。

在实践中,您可以使用Docker预制容器。您只需安装并在一行命令中运行它们。安装tensorflow-gpu就像使用docker run -it tensorflow-gpu一样简单。虽然我找不到太多关于LXD(LXC容器)预制容器,但我发现它们更易于定制,且更加稳定且性能更佳。

容器和虚拟机都可以用于分布式负载。但由于容器几乎没有开销,容器管理软件专注于创建容器群集,以便您轻松地将它们(及负载)分配给物理机器。

真实生活的例子:

假设你需要50多种计算环境和50多种服务,例如mysql、webhosting和基于云的服务(如jenkins和对象存储),并且你拥有50多个不同的裸金属服务器。通常这是一个拥有许多系别的学术环境。您需要高效地使用资源,并且需要高可用性。当一台服务器崩溃时,用户不应该遇到任何问题。
为了解决这个问题,您所做的基本上是在所有服务器上安装所有类型的容器。并将负载分配给所有裸金属机器。当需要更多一种类型的容器时,可以在一个或多个裸金属机器上自动产生更多的容器。因此,许多不同的用户可以连续灵活地使用不同的服务和环境。
在这种情况下,假设有100名学生同时使用系统。其中95人正在使用服务器进行基本服务,例如检查GPA、课程、图书馆数据库等。但是有5人正在执行5种不同类型的工程模拟。您会发现,49个裸金属服务器完全专注于工程模拟,每个服务器都有5种不同类型的计算容器,以%20硬件资源使用率相互竞争但保持平衡。当您增加2500名学生来完成基本任务时,这些学生将使用所有裸金属机器的%5。其余将用于计算。

因此,提供这种灵活性优势的容器最重要的区别特征是:

可以使用不同版本的相同软件和库而不会发生冲突,准备好部署的预制容器,几乎没有开销并且具有快速生成能力,可通过实时可调配额来进行管理。

使用 .cpu_allowencess、.ram_allowances 或直接 cgroup。Kubernetes 为您完成所有这些工作。在尝试过 Docker 和 LXD 后,您可能想要查看它。


1
虚拟机是一个完整的多进程系统。Docker对于每个容器只能运行一个前台进程,如果有多个进程的话,只有一个会在前台运行,并且你也无法在其中使用systemctl等工具。
当有人构建一个Docker容器时,他们只会将所需的软件包放在容器中,不会多余添加其他内容。如果有人只需要ping命令而不需要其他内容,Docker也可以轻松实现。因为这些额外的软件包已经在主机设备上运行,所以Docker只需共享它们(内核),使得容器非常小且运行速度快。
所有软件包的依赖关系都存储在容器中。你可以将它们上传到Docker Hub,在另一台电脑上下载,它们的运行方式完全相同。现在想象一下,你有一个安装了15个配置应用程序的虚拟机,还有一个安装了15个容器的虚拟机。在容器中,你可以比在虚拟机中更快地从故障中恢复,包括升级/降级软件包。因为所有的配置和安装步骤已经完成,你只需要运行命令15次即可。
如果你升级到新版本的软件,但它无法正常工作,只需一个命令就可以回滚。需要一直运行吗?有一个始终重启的标志。所以它几乎可以做任何事情,包括备份管理,除了一个限制:每个容器只能运行一个进程。
想象一下,如果你被黑客攻击,有人闯入你的容器,猜猜他们能得到什么?只有容器里的东西。它与主机虚拟机隔离开来,除非配置错误。对于像nginx和mysql这样的单进程应用程序来说,它是一个更优越的解决方案,你甚至可以动态更改端口映射。你还可以使用-v标志将你的mysql映射到主机设备上进行持久存储,尽管如果你从Windows迁移到Linux,请小心文件路径不同的问题。

1

我假设您已经熟悉虚拟机依赖于硬件级别仿真的方式。

然而,Docker容器作为主机操作系统上的常规进程运行,并依赖于提供隔离的Linux内核功能:命名空间。如果您想要,您实际上可以单独使用命名空间进行操作,这可能有助于您理解它们如何配合使用。以下是一个列表:

  • 进程命名空间:在PID命名空间中运行的进程只能看到相同命名空间中的进程。因此,如果您在进程命名空间中运行bash shell并执行“ps -a”命令,则内核将仅显示相同命名空间中的进程。有趣的是,在docker主机上,您可以通过执行“ps -a”命令查看所有容器进程,因为内核不会限制您在命名空间之外 - 它们只是普通进程,其可见性受到限制。
  • 网络命名空间:网络命名空间具有其自己隔离的完整Linux网络堆栈实例。因此,请考虑接口、iptables规则、路由表等。实际上,网络命名空间从一开始就与外部世界完全断开连接,而docker在其上添加了一些网络功能,以便为容器提供默认接口,并使用NAT访问主机网络连接。
  • IPC命名空间:这与PID命名空间紧密相关,如您所预期的那样。
  • 挂载命名空间:这是docker为容器提供其自己文件系统的方式。在挂载命名空间中,docker会将一个完整的文件系统从文件挂载到根目录。只有属于该命名空间的进程才能看到这个文件系统。其他答案已经描述了docker使用UnionFS的巧妙之处,因此我在此不再赘述(编辑:我决定在下面的部分详细说明这如何帮助docker变得轻量级)。
  • 用户命名空间:用户命名空间是允许docker容器拥有自己专用用户而不会影响系统其余部分的方式。(这些用户仍将具有UID / GID,因此当您将它们从容器中取出时,它们创建的文件将由那些UID / GID拥有,并且容器内的root用户将能够修改您挂载到容器中的任何文件,因此仍需要小心)
  • UTS命名空间:显然,这使容器拥有自己的主机名和域名 - 我并不经常考虑这个问题。
  • 时间命名空间:这允许容器拥有自己独立的系统时钟。我怀疑docker不会费心处理这个问题,因为像NTP这样的后台服务不会在容器中运行。

当您定义一个容器时,通常是定义一个单一的根进程,Docker会将其引导到一组专用的命名空间中,我们称之为容器。 Docker将监视此父进程作为容器,并在其停止时将其视为已停止的容器。这就是为什么通常不应尝试在容器内运行后台服务的原因 - 将Docker视为服务管理器,而您的应用程序是单个服务。子进程保留在与其父进程相同的命名空间中,因此隔离得到保留。 "Docker exec"实际上只是对由内核提供的实际"nsenter"命令的一种花式编排,允许您在命名空间内执行程序。


除了共享相同的内核外,Docker还使用了一些技巧来实现轻量化。

镜像层

首先,正如其他人指出的那样,Docker使用联合文件系统(UnionFS),这是一种分层文件系统。之前的层是只读的,当前层是可编辑的,但是采用写时复制(copy-on-write)方式。因此,如果在以前的层中有一个文件A,并且您修改了该文件,则整个文件及其修改将保存到当前的读/写层中。UnionFS负责仅显示文件的最新副本。这听起来可能很浪费——即使您不再使用旧文档,您仍然保留着旧文档的副本?但是,这允许镜像层像树一样结构化,您可以从常见的基础镜像中分支出大量不同的配置,而无需复制这些基础层中的文件。

假设您为Web服务器构建了一个Docker镜像。您从中开始的第一层通常是操作系统。操作系统开发人员通常会发布只有一层或两层的映像,因为他们预先构建了操作系统,然后将其解压缩到单个映像层中。在Dockerfile中,每个命令通常创建一个新的层,这就是为什么您经常会看到带有许多行连续和删除临时文件的RUN命令:您在图像层中留下的任何内容都会永远停留在那里,即使您在新的层中删除它。构建完Web服务器映像后,所有层都变成只读的。
现在假设您启动了100个Web服务器容器。每个容器都有自己的可编辑运行时层,但重要的是,所有容器都共享Web服务器映像层 - 无需花费1个Web服务器存储要求的100倍的成本。在运行时,容器仅消耗用于运行时工件(例如日志或临时文件)的新存储空间。通常,容器化应用程序的设计是,这些容器层是短暂的,可以被销毁而不会产生影响。

这个问题超出了本回答的范围,但是如果您的容器将创建比单个容器的生命周期更长时间需要保留的重要文件(如数据库),则可以在运行时将“卷”挂载到容器中,这些卷将提供安全的数据持久性,与镜像/容器生命周期分开。

控制组

另一个可用于容器的内核功能称为“cgroups”或“控制组”:这些允许您限制一组进程(如容器)可用的硬件资源。

默认情况下,Docker 容器拥有所有 RAM 和 CPU 资源可用,这非常高效。与 VM 不同,资源相对专用,容器可以根据负载一起垂直地扩展和缩小,共享资源。

然而,这有时可能不安全 - 应用程序可能会有负载峰值,消耗过多资源,并使其他容器饥饿。假设这不是恶意或糟糕编写的应用程序,处理此合法的负载峰值的好方法是使用集群中的多个主机,并使用某种编排系统动态启动容器的新实例。


-3
在我看来,这要视应用程序的需求而定。为什么要决定部署到Docker中?因为Docker可以根据应用程序的功能将其分成小部分,这很有效,因为当一个应用程序/功能出错时,它不会影响其他应用程序,与使用完整虚拟机相比,它会更慢并且配置更加复杂,但从某些方面来说比Docker更安全。

-8
Docker文档(和自我解释)区分了“虚拟机”和“容器”。他们有倾向于以稍微不同寻常的方式解释和使用事物。他们可以这样做,因为他们在文档中写什么是由他们决定的,并且因为虚拟化术语还没有真正确切的定义。
事实上,Docker文档对“容器”的理解,在现实中是半虚拟化(有时称为“操作系统级虚拟化”),与硬件虚拟化相反,而Docker则不是。
Docker是一种低质量的半虚拟化解决方案。容器与虚拟机的区别是由Docker开发人员发明的,用来解释其产品的严重缺陷。
它变得如此流行的原因是,他们“点燃了普通人的火焰”,也就是说,它使得在Win10工作站上简单使用典型的服务器(= Linux)环境/软件产品成为可能。这也是我们容忍他们的小“细节”的原因。但这并不意味着我们也应该相信它。

情况更加复杂的是,Windows 主机上的 Docker 使用 HyperV 中嵌入的 Linux,并在其中运行其容器。因此,Windows 上的 Docker 使用了一种组合硬件和半虚拟化解决方案。

简而言之,Docker 容器是低质量的(半)虚拟机,具有巨大的优势和许多缺点。


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