Docker镜像和容器有什么区别?

使用Docker时,我们从一个基本映像开始。我们启动它,创建更改,然后这些更改保存在形成另一个映像的图层中。

因此,最终我为我的PostgreSQL实例提供了一个图像,为我的Web应用程序提供了一个图像,对其进行的更改不断被持久化。

什么是容器?

411019 次浏览

映像的实例称为容器。您有一个映像,它是您描述的一组层。如果您启动此映像,您将拥有此映像的一个正在运行的容器。您可以拥有同一映像的多个正在运行的容器。

您可以看到所有带有docker images的图像,而您可以看到带有docker ps的正在运行的容器(您可以看到所有带有docker ps -a的容器)。

因此,映像的运行实例是一个容器。

虽然将容器视为正在运行的映像是最简单的,但这并不准确。

映像实际上是一个可以变成容器的模板。要把映像变成容器,Docker引擎会获取映像,在顶部添加一个读写文件系统,并初始化各种设置,包括网络端口、容器名称、ID和资源限制。正在运行的容器有一个当前正在执行的进程,但容器也可以被停止(或Docker术语中的退出)。退出的容器和映像一样没有,因为它可以重新启动,并将保留其设置和任何文件系统更改。

从我的文章自动化Docker部署(存档):

Docker镜像vs.容器

在Dockerland中,有图像容器。两者密切相关,但又截然不同。对我来说,掌握这种二分法极大地澄清了Docker。

什么是图像?

图像是一种惰性的、不可变的文件,本质上是容器的快照。使用建立命令创建图像,从运行开始时它们将生成一个容器。图像存储在Docker注册表中,例如registry.hub.docker.com。由于它们可以变得非常大,因此图像被设计成由其他图像层组成,从而在通过网络传输图像时允许发送最小量的数据。

本地图像可以通过运行docker images列出:

REPOSITORY                TAG                 IMAGE ID            CREATED             VIRTUAL SIZEubuntu                    13.10               5e019ab7bf6d        2 months ago        180 MBubuntu                    14.04               99ec81b80c55        2 months ago        266 MBubuntu                    latest              99ec81b80c55        2 months ago        266 MBubuntu                    trusty              99ec81b80c55        2 months ago        266 MB<none>                    <none>              4ab0d9120985        3 months ago        486.5 MB

有些事情要注意:

  1. IMAGE ID是图像真实标识符的前12个字符。您可以为给定图像创建许多标签,但它们的ID都相同(如上所述)。
  2. VIRTUAL SIZE是虚拟,因为它将所有不同底层的大小相加。这意味着该列中所有值的总和可能比所有这些图像使用的磁盘空间大得多。
  3. REPOSITORY列中的值来自docker build命令的-t标志,或来自docker tag-ing现有图像。您可以自由地使用对您有意义的命名法标记图像,但要知道docker将使用该标记作为docker pushdocker pull中的注册表位置。
  4. 标签的完整形式是[REGISTRYHOST/][USERNAME/]NAME[:TAG]。对于上面的ubuntu,REGISTRYHOST被推断为registry.hub.docker.com。因此,如果您计划将名为my-application的图像存储在docker.example.com的注册表中,您应该标记该图像docker.example.com/my-application
  5. TAG列只是充分标记的[: TAG]部分。
  6. latest标签并不神奇,它只是您不指定标签时的默认标签。
  7. 您可以拥有仅可通过其IMAGE ID识别的未标记图像。这些将获得<none>标签和存储。很容易忘记它们。

有关图像的更多信息,请参阅Docker留档词汇表

什么是集装箱?

用一个编程比喻来说,如果一个图像是一个类,那么容器就是一个类的实例——一个运行时对象。容器有望成为你使用Docker的原因;它们是运行应用程序的环境的轻量级和可移植封装。

使用docker ps查看本地正在运行的容器:

CONTAINER ID        IMAGE                               COMMAND                CREATED             STATUS              PORTS                    NAMESf2ff1af05450        samalba/docker-registry:latest      /bin/sh -c 'exec doc   4 months ago        Up 12 weeks         0.0.0.0:5000->5000/tcp   docker-registry

在这里,我正在运行docker注册表的dockerized版本,这样我就有一个私人的地方来存储我的图像。

  1. 与IMAGE ID一样,CONTAINER ID是容器的真实标识符。它具有相同的形式,但它标识不同类型的对象。
  2. docker ps只输出运行个容器。您可以使用docker ps -a查看所有容器(运行停止)。
  3. NAMES可用于通过--name标志标识已启动的容器。

如何避免图像和容器堆积

我早期对Docker的挫败感之一是看似不断堆积的未标记图像和停止的容器。在少数情况下,这种积累导致硬盘驱动器最大化,减慢了我的笔记本电脑或停止了我的自动构建管道。谈论“无处不在的容器”!

我们可以通过将docker rmi与最近的dangling=true查询组合来删除所有未标记的图像:

docker images -q --filter "dangling=true" | xargs docker rmi

Docker将无法删除现有容器后面的映像,因此您可能必须先删除docker rm的已停止容器:

docker rm `docker ps --no-trunc -aq`

这些是Docker的已知痛点,可能会在未来的版本中得到解决。但是,通过对映像和容器的清晰理解,可以通过以下几种做法来避免这些情况:

  1. 总是删除一个无用的,停止容器docker rm [CONTAINER_ID]
  2. 总是用docker rmi [IMAGE_ID]删除无用的、停止的容器后面的图像。

Docker的核心概念是使创建“机器”变得容易,在这种情况下可以被视为容器。容器有助于可重用性,允许您轻松创建和删除容器。

图像描绘了容器在每个时间点的状态。所以基本的工作流程是:

  1. 创建图像
  2. 启动一个容器
  3. 对容器进行更改
  4. 将容器另存为图像

也许解释整个工作流程会有所帮助。

一切都从dockerfile开始。Dockerfile是映像的源代码。

创建Dockerfile后,您构建它以创建容器的图像。映像只是“源代码”的“编译版本”,即Dockerfile。

获得容器的映像后,您应该使用注册表重新分发它。注册表就像一个Git存储库——您可以推送和拉取图像。

接下来,您可以使用映像运行容器。正在运行的容器在许多方面与虚拟机非常相似(但没有管理程序)。

Docker映像打包了应用程序运行所需的应用程序和环境,容器是映像的运行实例。

镜像是Docker的打包部分,类似于“源代码”或“程序”。容器是Docker的执行部分,类似于“进程”。

在这个问题中,只提到了“程序”部分,那就是映像。Docker的“运行”部分是容器。当容器运行并进行更改时,就好像进程对自己的源代码进行了更改并将其保存为新映像。

容器只是一个可执行的二进制文件,它将由主机操作系统在一组限制下运行,这些限制是使用知道如何告诉操作系统应用哪些限制的应用程序(例如Docker)预设的。

典型的限制是进程隔离相关、安全相关(如使用SELinux保护)和系统资源相关(内存、磁盘、CPU和网络)。

直到最近,只有基于Unix的系统中的内核支持在严格限制下运行可执行文件的能力。这就是为什么今天大多数容器讨论主要涉及Linux或其他Unix发行版。

Docker是那些知道如何告诉操作系统(Linux主要是)运行可执行文件的限制的应用程序之一。可执行文件包含在Docker映像中,它只是一个tarfile。该可执行文件通常是Linux发行版的用户空间(Ubuntu、CentOS、Debian等)的精简版本,预先配置为在其中运行一个或多个应用程序。

尽管大多数人使用Linux库作为可执行文件,但它可以是任何其他二进制应用程序,只要主机操作系统的内核可以运行它(参见创建一个简单的基础图像)。无论Docker映像中的二进制是操作系统用户空间还是简单的应用程序,对操作系统主机来说,它都只是另一个进程,一个由预设操作系统边界统治的包含进程。

其他应用程序,如Docker,可以告诉主机操作系统在进程运行时应用哪些边界,包括LXClibvirt系统d。Docker过去使用这些应用程序间接与Linux操作系统交互,但现在Docker使用自己的名为“lib容器”的库直接与Linux交互。

所以容器只是在受限模式下运行的进程,类似于chroot曾经做的事情。

在我看来,Docker与任何其他容器技术的区别在于它的存储库(Docker Hub)及其管理工具,这使得使用容器变得非常容易。

Docker(软件)

dockerfile→(构建)→图片→(运行)→容器

  • Dockerfile:包含一组Docker指令,以您喜欢的方式配置您的操作系统,并安装/配置您的所有软件。

  • Image:编译好的Dockerfile。节省您每次需要运行容器时重建Dockerfile的时间。这是一种隐藏预置代码的方法。

  • 容器:虚拟操作系统本身。你可以ssh进入它并运行任何你想要的命令,就像它是一个真实的环境一样。你可以从同一个映像运行1000多个容器。

在编程方面,

图片是源代码。

源代码被编译和构建时,它被称为应用程序。

类似于“为图像创建实例时”,它被称为“容器”。

Dockerfile就像生成tarball(Docker映像)的Bash脚本。

Docker容器就像tarball的提取版本。您可以在不同的文件夹(容器)中拥有任意数量的副本。

工作流程

这是端到端工作流,显示了各种命令及其相关的输入和输出。这应该澄清图像和容器之间的关系。

+------------+  docker build   +--------------+  docker run -dt   +-----------+  docker exec -it   +------+| Dockerfile | --------------> |    Image     | --------------->  | Container | -----------------> | Bash |+------------+                 +--------------+                   +-----------+                    +------+^| docker pull|+--------------+|   Registry   |+--------------+

要列出您可以运行的图像,请执行:

docker image ls

要列出您可以在其上执行命令的容器:

docker ps

我无法理解图像的概念,尽管阅读了这里的所有问题,然后最终偶然发现了这个优秀的Docker留档(废话!)。

这个例子确实是理解整个概念的关键。这是一篇冗长的文章,所以我总结了需要真正抓住的关键点,以获得清晰。

  • Image:Docker镜像由一系列只读层构建而成

  • Layer:每个层代表映像的Dockerfile中的一条指令。

Example:下面的Dockerfile包含四个命令,每个命令创建一个层。

来自ubuntu:15.04

收到 /app

跑 /app

CMD python /app/app.py

重要,每个层只是与它之前的层的一组差异。

  • 容器.当您创建一个新容器时,在底层之上添加一个新的可写层。这一层通常称为“容器层”。对正在运行的容器所做的所有更改,例如写入新文件、修改现有文件和删除文件,都会写入这个可写容器层。

因此,容器和图像之间的主要区别是顶部可写层。所有写入添加新或修改存量数据存储在此可写层中容器被删除,可写层也被删除基本图像保持不变。

从磁盘尺寸的角度理解图像cnd容器

要查看正在运行的容器的大致大小,您可以使用docker ps -s命令。你得到sizevirtual size作为两个输出:

  • 大小:用于每个容器的可写层的数据量(在磁盘上)

  • 虚拟大小:容器使用的只读映像数据所使用的数据量。多个容器可能共享部分或全部只读映像数据。因此,这些不是累加的。即,您不能添加所有虚拟大小来计算映像在磁盘上使用了多少大小

另一个重要的概念是写时复制策略

如果文件或目录存在于映像中的较低层,而另一层(包括可写层)需要对其进行读访问,则仅使用现有文件。当另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改。

我希望这能帮助像我这样的人。

简单地说,如果一个图像是一个,那么一个容器是一个类的实例是一个运行时对象

简而言之:

容器是内核中的一个部门(虚拟),它共享一个公共操作系统并运行一个映像(Docker映像)。

容器是一个自我维持的应用程序,它将包和所有必要的依赖项一起运行代码。

图片相当于OOP中的类定义,层是该类的不同方法和属性。

容器是图像的实际实例化,就像对象是类的实例化或实例一样。

对于虚拟编程类比,您可以认为Docker有一个抽象的ImageFactory,其中包含来自商店的ImageFactory。

然后,一旦你想从ImageFactory创建一个应用程序,你将有一个新的容器,你可以根据需要修改它。DotNetImageFactory将是不可变的,因为它充当抽象工厂类,它只提供你想要的实例。

IContainer newDotNetApp = ImageFactory.DotNetImageFactory.CreateNew(appOptions);newDotNetApp.ChangeDescription("I am making changes on this instance");newDotNetApp.Run();

Docker容器正在运行映像的实例。您可以将映像与程序联系起来,将容器与进程联系起来:)

图像对于类来说就像容器对于对象一样。

容器是图像的实例,因为对象是类的实例。

正如许多答案指出的那样:你构建dockerfile得到图像,你图像得到容器

但是,以下步骤帮助我更好地了解了Docker映像和容器是什么:

1)构建Dockerfile:

docker build -t my_image dir_with_dockerfile

2)将图像保存到.tar文件

docker save -o my_file.tar my_image_id

my_file.tar将存储图像。使用tar -xvf my_file.tar打开它,您将看到所有图层。如果您深入研究每一层,您可以看到每一层添加了哪些更改。(它们应该非常接近Dockerfile中的命令)。

3)要查看容器内部,您可以执行以下操作:

sudo docker run -it my_image bash

你可以看到它非常像一个操作系统。

我认为最好一开始就解释清楚。

假设您运行命令docker run hello-world。会发生什么?

它调用docker cli,负责接收Docker命令并转换为调用docker服务器命令。一旦docker服务器收到运行图像的命令,它就会检查图像缓存是否持有具有此类名称的图像

假设hello-world不存在。docker服务器转到Docker Hub(Docker Hub只是一个免费的图像存储库)并询问,嘿Hub,你有一个名为hello-world图像吗?集线器响应-是的,我做的。然后给我,请。下载过程开始。一旦docker镜像被下载,docker服务器将其放入图像缓存

因此,在我们解释什么是Docker映像和Docker容器之前,让我们先介绍一下您计算机上的操作系统以及它如何运行软件。

例如,当您在计算机上运行Chrome时,它会调用操作系统,操作系统本身会调用内核并询问,嘿,我想运行这个程序。内核设法从您的硬盘上运行文件。

现在假设您有两个程序,Chrome和Node.js.Chrome需要Python版本2才能运行,Node.js需要Python版本3才能运行。

要使这两种情况都有效,您需要以某种方式使用称为命名空间的操作系统功能。命名空间是一种功能,它使您有机会隔离进程、硬盘驱动器、网络、用户、主机名等。

所以,当我们谈论图像时,我们实际上谈论的是文件系统快照。图像是一个物理文件,其中包含构建特定容器的方向和元数据。容器本身是图像的一个实例;它使用仅适用于容器的命名空间隔离硬盘驱动器。所以容器是一个进程或一组进程,它们对分配给它的不同资源进行分组。

图像视为容器的“快照”可能会有所帮助。

您可以从容器创建图像(新的“快照”),也可以从图像启动新容器(实例化“快照”)。例如,您可以从基本映像实例化一个新容器,在容器中运行一些命令,然后将其“快照”为新映像。然后您可以从该新映像实例化100个容器。

其他需要考虑的事情:

  • 图像由图层组成,图层是快照“差异”;当您推送图像时,只有“差异”被发送到注册表。
  • dockerfile在基本图像之上定义了一些命令,这些命令创建新图层(“差异”),从而产生新图像(“快照”)。
  • 容器总是从图像中实例化。
  • 图像标签不仅仅是标签。它们是图像的“全名”(“存储库:标签”)。如果同一图像有多个名称,则在执行docker images时会显示多次。

docker对容器使用联合文件系统UFS),它允许多个文件系统挂载在一个层次结构中,并显示为单个文件系统。图像中的文件系统已挂载为read-only层,对正在运行的容器的任何更改都会挂载在其上的read-write层。因此,Docker只需查看最顶层的读写层即可找到对正在运行的系统所做的更改。

我将用以下类比来说明这一点:

+-----------------------------+-------+-----------+|             Domain          | Meta  | Concrete  |+-----------------------------+-------+-----------+| Docker                      | Image | Container || Object oriented programming | Class | Object    |+-----------------------------+-------+-----------+

*在docker中,图像是一个不可变的文件保存docker应用程序运行所需的源代码和信息。它可以独立于容器存在。

*Docker容器是运行时创建的虚拟化环境,需要图像才能运行。docker网站有一个图像显示了这种关系:

docs.docker.com图像

映像是构建容器/s(正在运行的实例)的蓝图。

Docker客户端、服务器、机器、图像、集线器、组合都是项目工具软件,它们聚集在一起形成一个平台,在这个平台上,围绕创建和运行称为容器的东西的生态系统,现在如果你运行命令docker run redis,称为docker CLI的东西伸手到称为Docker Hub的东西,它下载了一个名为图像的文件。输入图片描述

Docker镜像:

映像是包含所有依赖项和运行非常特定的程序所需的所有配置的单个文件,例如redis是您刚刚下载(通过运行命令docker run redis)应该运行的映像。

这是一个存储在硬盘上的单个文件,在某些时候您可以使用此图像创建称为容器的东西。输入图片描述

容器是映像的一个实例,你可以把它想象成一个运行中的程序,它有自己独立的硬件资源集,所以它有自己的小集合或自己的小内存空间,有自己的小网络技术空间和自己的小硬盘空间。

现在让我们检查一下何时发出贝洛命令:sudo docker运行hello-world

上述命令将启动docker客户端或docker CLI,docker CLI负责从您那里获取命令,对它们进行一些处理,然后将命令传递给称为docker server的东西,当我们运行命令Docker run hello-world时,docker server负责繁重的工作,在此处输入图片描述这意味着我们想要使用名为hello world的图像启动一个新容器,hello world图像内部有一个微小的标题程序,其唯一目的或唯一工作是打印出您在终端中看到的消息。

现在,当我们运行该命令并将其发送到docker服务器时,后台很快发生了一系列操作。Docker服务器看到我们试图使用名为hello world的映像启动一个新容器。

docker服务器做的第一件事是检查它是否已经有一个本地副本,比如你的个人机器上的hello world映像或hello world文件的副本。所以docker服务器查看了一个叫做映像缓存的东西。输入图片描述

现在因为你和我刚刚在我们的个人电脑上安装了Docker,图像缓存目前是空的,我们没有以前已经下载过的图像。

因此,由于映像缓存是空的,Docker服务器决定联系一个名为Docker Hub的免费服务。Docker Hub是一个免费公共映像的存储库,您可以在个人计算机上免费下载和运行。因此Docker服务器联系了Docker Hub,下载了hello world文件并将其存储在映像缓存中并存储在您的计算机上,现在可以在未来的某个时候非常快速地重新运行它,而无需从docker Hub重新下载它。

之后,docker服务器将使用它来创建容器的实例,我们知道容器是映像的实例,它的唯一目的是运行一个非常特定的程序。因此,docker服务器基本上是从映像缓存中获取该映像文件并将其加载到内存中,以便从中创建一个容器,然后在其中运行一个程序。该单个程序的目的是打印出您看到的消息。

什么是容器:首先,图像是如何创建容器的蓝图。

输入图片描述容器是一个进程或一组进程,它们有一组专门分配给它的资源,在波纹管中是一个图表,每当我们想到一个容器时,我们有一些正在运行的进程向内核发送系统调用,内核将查看传入的系统调用并将其引导到硬盘驱动器,RAM,CPU或它可能需要的其他任何东西的非常特定的部分,并且每个资源的一部分都可用于该单一进程。

正如对象是面向对象编程语言中类的实例一样,Docker容器也是Docker映像的实例。

图像就像一个类,而容器就像一个类的对象,所以你可以有无限数量的容器表现得像图像。类是一个蓝图,它自己不做任何事情。你必须在你的程序中创建对象的实例才能做任何有意义的事情。图像和容器也是如此。你定义你的图像,然后创建运行该图像的容器。它并不完全相似,因为对象是一个类的实例,而容器就像一个空洞的地方,你用图像建立一个运行的主机,完全按照图像所说的

映像或容器映像是一个文件,其中包含您的应用程序代码、应用程序运行时、配置、依赖库。映像基本上将所有这些包装成一个单一的、安全的不可变单元。使用适当的docker命令来构建映像。映像有图像ID和图像标签。标签通常为/image-name: tag文件名格式。

当您开始使用映像运行您的应用程序时,您实际上启动了一个容器。因此,您的容器是一个运行映像的沙盒。Docker软件用于管理映像和容器。

Image是一个安全的包,其中包含您的应用程序工件、库、配置和应用程序运行时。容器是映像的运行时表示。

一般来说,docker镜像容器镜像是一个软件包,它将您的应用程序可执行文件、依赖项、配置和应用程序运行时包装成一个安全且不可变的单元。当我说应用程序可执行文件时,它因应用程序而异。例如,如果它是Java应用程序,它将是一个jar文件,因为节点应用程序可能是一个js文件。同样,应用程序运行时取决于您的应用程序本身。对于Java应用程序,它将是JRE,对于节点应用程序,它是节点二进制文件。

您可以根据图像清单文件(例如Dockerfile)中提供的说明构建映像。构建映像后,您可以将其存储在本地或像hub.docker.com这样的容器映像存储库中。

当您想将映像作为应用程序工作负载运行时,您启动一个需要您的映像的容器。容器是映像的运行时实例。

要构建镜像、存储镜像并将其作为容器运行,您需要一个名为容器运行时的软件,就像docker一样。这些容器运行时符合开放容器倡议为镜像创建和容器运行提供的标准。

长话短说。

Docker镜像:

用于创建容器的文件系统和配置(只读)应用程序。

Docker容器:

容器和镜像的主要区别在于顶可写层。容器是运行具有顶可写层的Docker镜像实例。容器运行实际的应用程序。容器包括一个应用程序及其所有依赖项。当容器被删除时,可写层也被删除。底层镜像保持不变。


其他需要注意的重要术语:

Docker守护进程:

在主机上运行的后台服务,用于管理构建、运行和分发Docker容器。

Docker客户端:

允许用户与Docker守护程序交互的命令行工具。

Docker Store:

Store是Docker映像的注册表。您可以将注册表视为所有可用Docker映像的目录

这个博客文章中的一张图片胜过千言万语。输入图片描述

总结:

  • 从Docker中心拉取映像或从Dockerfile构建=>给出一个Docker镜像(不可编辑)。
  • 运行镜像(docker runimage_name:tag_name)=>运行图像即容器(可编辑)