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

341
在Docker中,容器和镜像有什么区别?在 Docker入门教程中这些术语都被使用了,但我不理解它们之间的区别。请问有人能够解释一下吗?
12个回答

389

图片是活动容器的不可变快照。容器是某个镜像的运行(或停止)实例。

从名为“ubuntu”的基础镜像开始。让我们在 ubuntu 镜像中交互式地运行 bash 并创建一个文件。我们将使用 -i-t 标志来给我们一个交互式的 bash shell。

$ docker run -i -t ubuntu  /bin/bash
root@48cff2e9be75:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@48cff2e9be75:/# cat > foo
This is a really important file!!!!
root@48cff2e9be75:/# exit

退出并重新启动镜像时,请不要指望文件会保留下来。你重新开始的状态与之前一样,而不是从你离开的地方继续。

$ docker run -i -t ubuntu  /bin/bash
root@abf181be4379:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@abf181be4379:/# exit

但是,容器现在不再运行,但它有状态并且可以保存(提交)到镜像中。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS                          PORTS                      NAMES
abf181be4379        ubuntu:14.04        /bin/bash              17 seconds ago       Exited (0) 12 seconds ago                                  elegant_ardinghelli    
48cff2e9be75        ubuntu:14.04        /bin/bash              About a minute ago   Exited (0) 50 seconds ago                                  determined_pare        
...

让我们从容器ID 48cff2e9be75创建一个图像,其中我们创建了我们的文件。
$ docker commit 48cff2e9be75 ubuntu-foo
d0e4ae9a911d0243e95556e229c8e0873b623eeed4c7816268db090dfdd149c2

现在,我们有一张包含非常重要文件的新图片:
$ docker run ubuntu-foo /bin/cat foo
This is a really important file!!!!

尝试运行命令docker images。您应该能够看到新的ubuntu-foo镜像以及我们开始使用的标准ubuntu镜像。

50
这个解释是迄今为止最清晰和最好的 - 提供了真实的例子,而不是“玩弄文字”来解释行话。 - CozyAzure
4
同意。这是一个精彩的小型教程。在我注意到这是我的朋友cbare写的之前,我就已经这样认为了。嗨@cbare! - eleanorahowe
3
顺便提一句,docker exec -t -i 48cff2e9be75 /bin/bash 命令可以回到容器中。 - Faccion
1
@Faccion 我收到了一个错误:“守护进程的错误响应:容器48cff2e9be75...未运行”。 - geoidesic
你从Ubuntu镜像创建了一个实例。那么这个Ubuntu就是人们所说的“主机操作系统”吗?假设我的系统运行在CentOS上。那不是主机操作系统,对吧?此外,每个容器都会拷贝Ubuntu镜像吗?我猜测不会创建拷贝,因为这样会像虚拟机一样占用大量空间。 - coder.in.me

161

9
这个术语描述是有意义的,但我无法将[docker教程](https://docs.docker.com/mac/step_two/)中以下定义与之联系起来:*容器是Linux操作系统的简化基本版本。镜像是您加载到容器中的软件。* - orad
4
因为教程中的描述很糟糕,我会联系 Docker 的人来修正。 - Adrian Mouat
1
从Git来看,Docker中图像和容器之间的概念区分似乎是不必要和浪费的。它们都只是状态而已。也许更简洁的模型可以被建模为DAG甚至只是一棵树。 - Asclepius
5
Docker的概念比较混淆,而且在shell中的参数也有些困惑。 - stackdave
@stackdave 我以为只有我这么想! - Homunculus Reticulli

71
使用面向对象编程的比喻,Docker镜像和Docker容器之间的区别就像类和对象之间的区别一样。对象是类的运行时实例。同样地,容器是镜像的运行时实例。
对象只有在实例化时才会创建一次。同样地,容器可以运行或停止。容器是从镜像创建的,尽管这并不总是情况。下面的示例创建了一个Apache服务器镜像,运行该镜像,列出镜像,然后列出容器:
  1. Create a Dockerfile with the following contents:

    FROM httpd:2.4
    
  2. Install Apache server

    sudo docker build -t my-apache2 .
    
  3. Run the image

    sudo docker run -it --rm --name my-running-app my-apache2
    
  4. List Docker images

    sudo docker images
    
  5. List the running Docker containers

    docker ps
    
  6. List all containers

    docker ps -a
    
  7. List latest created containers

    docker ps -l
    

5
这个比喻很好,如果你懂Java的话会更容易理解。我原本想说一个镜像就像AWS的AMI,而容器则类似于EC2实例(运行或停止)- 但这需要你了解亚马逊网络服务才能明白这个比喻。 - phpguru
当我读到@cbare的回答时,正是我所想的。不过这并不仅限于Java。 - anlogg
1
当然,@phpguru。AMI vs EC2实例的类比是另一种将Docker镜像与Docker容器相关联的方式。 - StackOverFlow User
3
我认为这个比喻有缺陷。在Java中,类更像是一个对象创建指南。如果你想将其与Docker中的任何内容进行比较,我认为最佳匹配的是Dockerfile。而Docker镜像则存储了文件系统的状态,更类似于Java中序列化的对象,从镜像创建容器,则像是在Java中反序列化存储的对象。总体来说,我认为这些概念太不同以至于无法进行比较。 - Gellweiler

16

一张镜像基本上是一个不可变的模板,用于创建容器。通过考虑将镜像转换为容器时发生的情况,更容易理解镜像和容器之间的区别。

Docker引擎将在镜像顶部添加一个读写文件系统,然后初始化各种设置。这些设置包括网络选项(IP、端口等)、名称、ID以及任何资源限制(CPU、内存)。如果Docker引擎被要求运行容器,它还会在其中初始化一个进程。容器可以停止和重新启动,在这种情况下,它将保留所有设置和文件系统更改(但将失去内存中的任何内容,并且所有进程将重新启动)。因此,停止或退出的容器与镜像不同


12

DockerFile --(构建)--> Docker镜像 --(运行)--> Docker容器

DockerFile 是您或开发人员编写的用于执行某些操作(例如,安装软件)的代码。

Docker镜像 是您构建DockerFile时获取的内容。

Docker容器 是您运行Docker镜像时获取的内容。

我们可以通过拉取Docker Hub上的镜像来获得Docker镜像,然后运行它以获取容器。


7

镜像 [类似于虚拟机]

  • 只读模板,用于创建容器
  • 由您或其他Docker用户构建
  • 存储在Docker Hub或本地Registry中

容器 [类似于运行中的机器]

  • 隔离的应用程序平台
  • 包含运行应用程序所需的所有内容
  • 基于镜像

链接到显示容器是什么的图像


2

Images:用于运行容器的文件系统和元数据。可以将其视为包含运行应用程序所需的所有依赖项和执行该应用程序的默认设置的应用程序打包格式。元数据包括运行命令的默认值、环境变量、标签和健康检查命令。

Containers:隔离应用程序的实例。容器需要镜像来定义其初始状态,并使用来自镜像的只读文件系统以及容器特定的读写文件系统。正在运行的容器是一个围绕运行进程的封装,为该进程提供文件系统、网络和PID等名称空间。

当您执行docker run命令时,在命令行上提供一个镜像以及任何配置,Docker会根据该镜像定义和提供的配置返回一个容器。


References:对于Docker引擎来说,镜像只是一个图像ID。这是一个唯一的不可变哈希。对图像进行更改会导致创建新的图像ID。但是,您可以有一个或多个引用指向一个图像ID,类似于符号链接。这些引用可以更新以指向新的图像ID。请注意,当您创建一个容器时,Docker将在容器创建时解析该引用,因此您无法更新正在运行的容器的图像。相反,您需要创建一个新的镜像,并基于该新镜像创建一个新的容器。

Layers:更深入地了解一下,您有文件系统层。Docker使用分层文件系统组装镜像。每个层都是对文件系统的只读更改集,并且该层由唯一的哈希表示。使用这些只读层,多个图像可以扩展另一个图像,只需存储或通过网络传输这些图像之间的差异即可。当运行Docker容器时,它会接收一个特定于容器的读写文件系统层,该层对于该容器是唯一的,并且所有图像层都与该容器使用联合文件系统组装。读取通过每个层进行处理,直到找到文件、找到删除或在底层未找到文件为止。写入将从镜像只读层复制到特定于容器的读写层。删除将记录为对特定于容器的读写层的更改。构建镜像的常见步骤是在基于先前镜像文件系统状态的临时容器中运行命令,并将生成的容器特定层保存为新镜像中的层。


2
在Docker中,一切都始于镜像。镜像是组成操作系统的所有文件,足以满足你所需的功能。传统上,每个应用程序都需要安装整个操作系统及其所有内容。但使用Docker,你可以将其精简到只有足够完成所需功能的小容器,并且可以在计算机上高效地拥有很多这样的容器。
使用docker images命令查看已安装的镜像以及docker ps命令查看正在运行的镜像。当你输入docker run命令时,它会使用镜像并创建一个带有运行进程的容器。我倾向于使用以下命令:

docker run -ti <image>:<tag> bash

最后,镜像有自己的ID集合,容器也有自己的ID集合 - 它们不重叠。

2

容器是基于镜像的。在Docker运行命令中需要传递一个镜像。

例如:

BusyBox 镜像

http://i.stack.imgur.com/eK9dC.png

这里我们指定了一个名为 busybox 的镜像。Docker本地没有这个镜像,会从公共注册表中拉取。

注册表是Docker镜像目录,Docker客户端可以与之通信并从其中下载镜像。一旦镜像被拉取,Docker就会启动一个容器并执行echo hello world命令。


0

官方的区别在于容器是最后一个可写层,而下面的层只能读取,它们属于您的图像。直观的区别在于,Docker实例是由您的Docker守护程序虚拟化的实例,并且运行您的图像,在您的内核的隔离部分中操作(此过程对您隐藏)。然而,图像是静态的,不会运行,它只是一堆层(静态文件)。如果我们将这种范式与面向对象编程联系起来,图像就是您的类定义,而Docker实例则是驻留在内存中的类生成对象。

我编写了一篇教程来加强您的Docker知识直觉:

http://javagoogleappspot.blogspot.com/2018/07/docker-basics.html


您可以拥有只读容器。 - BMitch

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