使用 Docker 时,我们从基础镜像开始。我们启动它,创建更改,这些更改保存在形成另一个图像的层中。
所以最终我有一个我的 PostgreSQL 实例的图像和一个我的 web 应用程序的图像,对它们的更改继续被持久化。
什么是容器?
图像的一个实例称为容器。您有一个图像,它是您描述的一组图层。如果你启动这个镜像,你就有了这个镜像的运行容器。您可以拥有多个运行同一映像的容器。
您可以使用 docker images
查看所有图像,而您可以使用 docker ps
查看正在运行的容器(并且您可以使用 docker ps -a
查看所有容器)。
因此,图像的运行实例是一个容器。
来自我关于 Automating Docker Deployments 的文章(已存档):
Docker 映像与容器
在 Dockerland 中,有镜像,也有容器。两者密切相关,但又截然不同。对我来说,掌握这种二分法已经极大地阐明了 Docker。
什么是图像?
映像是一个惰性的、不可变的文件,它本质上是一个容器的快照。图像是使用 build 命令创建的,并且在使用 run 启动时会生成一个容器。图像存储在 Docker 注册表中,例如 registry.hub.docker.com。因为它们可以变得非常大,所以图像被设计为由其他图像层组成,从而在通过网络传输图像时允许发送最少量的数据。
可以通过运行 docker images
列出本地图像:
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 13.10 5e019ab7bf6d 2 months ago 180 MB
ubuntu 14.04 99ec81b80c55 2 months ago 266 MB
ubuntu latest 99ec81b80c55 2 months ago 266 MB
ubuntu trusty 99ec81b80c55 2 months ago 266 MB
<none> <none> 4ab0d9120985 3 months ago 486.5 MB
需要注意的一些事项:
IMAGE ID 是图像真实标识符的前 12 个字符。您可以为给定图像创建多个标签,但它们的 ID 都相同(如上)。 VIRTUAL SIZE 是虚拟的,因为它将所有不同底层的大小相加。这意味着该列中所有值的总和可能比所有这些图像使用的磁盘空间大得多。 REPOSITORY 列中的值来自 docker build 命令的 -t 标志,或来自 docker 标记现有图像。您可以使用对您有意义的命名法来标记图像,但要知道 docker 将使用标记作为 docker push 或 docker pull 中的注册表位置。标签的完整形式是 [REGISTRYHOST/][USERNAME/]NAME[:TAG]。对于上面的 ubuntu,REGISTRYHOST 被推断为 registry.hub.docker.com。因此,如果您计划将名为 my-application 的映像存储在 docker.example.com 的注册表中,则应将该映像标记为 docker.example.com/my-application。 TAG 列只是完整标签的 [:TAG] 部分。这是不幸的术语。 latest 标签并不神奇,它只是您不指定标签时的默认标签。您可以拥有只能通过其 IMAGE ID 识别的未标记图像。这些将获得
Docker documentation 和 glossary 提供了有关图像的更多信息。
什么是容器?
用编程的比喻来说,如果图像是一个类,那么容器就是一个类的实例——一个运行时对象。希望容器是您使用 Docker 的原因;它们是运行应用程序的环境的轻量级和可移植封装。
使用 docker ps
查看本地正在运行的容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2ff1af05450 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 注册表的 docker 化版本,这样我就有了一个私有的地方来存储我的图像。再次,有几点需要注意:
与 IMAGE ID 一样,CONTAINER ID 是容器的真实标识符。它具有相同的形式,但它标识了不同种类的对象。 docker ps 只输出正在运行的容器。您可以使用 docker ps -a 查看所有容器(运行或停止)。 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 的 known pain points,可能会在未来的版本中得到解决。但是,如果对图像和容器有清楚的了解,可以通过以下几种做法来避免这些情况:
始终使用 docker rm [CONTAINER_ID] 删除无用的已停止容器。始终使用 docker rmi [IMAGE_ID] 删除无用的已停止容器后面的图像。
docker image prune
清理悬空图像。 Prune unused Docker objects
docker system prune
来清理一切
虽然将容器视为运行映像最简单,但这并不十分准确。
图像实际上是一个可以变成容器的模板。为了将镜像变成容器,Docker 引擎获取镜像,在顶部添加一个读写文件系统,并初始化各种设置,包括网络端口、容器名称、ID 和资源限制。一个正在运行的容器有一个当前正在执行的进程,但是一个容器也可以被停止(或者用 Docker 的术语来说是退出)。退出的容器与映像不同,因为它可以重新启动并保留其设置和任何文件系统更改。
docker create
。
也许解释整个工作流程会有所帮助。
一切都从 Dockerfile 开始。 Dockerfile 是镜像的源代码。
创建 Dockerfile 后,您可以构建它以创建容器的映像。该图像只是“源代码”的“编译版本”,即 Dockerfile。
获得容器映像后,您应该使用注册表重新分发它。注册表就像一个 Git 存储库——您可以推送和拉取图像。
接下来,您可以使用该映像运行容器。运行中的容器在很多方面都与虚拟机非常相似(但没有 hypervisor)。
Dockerfile → (Build) → Image → (Run) → Container。
Dockerfile:包含一组 Docker 指令,以您喜欢的方式配置您的操作系统,并安装/配置您的所有软件。
图片:编译后的 Dockerfile。节省您每次需要运行容器时重新构建 Dockerfile 的时间。这是一种隐藏您的供应代码的方法。
容器:虚拟操作系统本身。您可以 ssh 进入它并运行您希望的任何命令,就好像它是一个真实的环境一样。您可以从同一个镜像运行 1000 多个容器。
工作流程
这是显示各种命令及其相关输入和输出的端到端工作流程。这应该澄清图像和容器之间的关系。
+------------+ docker build +--------------+ docker run -dt +-----------+ docker exec -it +------+
| Dockerfile | --------------> | Image | ---------------> | Container | -----------------> | Bash |
+------------+ +--------------+ +-----------+ +------+
^
| docker pull
|
+--------------+
| Registry |
+--------------+
要列出您可以运行的图像,请执行:
docker image ls
要列出可以在以下位置执行命令的容器:
docker ps
尽管阅读了这里的所有问题,但我还是无法理解 image 和 layer 的概念,然后最终偶然发现了这个 excellent documentation from Docker(呵呵!)。
那里的例子确实是理解整个概念的关键。这是一篇很长的文章,所以我总结了需要真正掌握的关键点,以使其清晰。
镜像:一个 Docker 镜像由一系列只读层构建而成
层:每一层代表图像的 Dockerfile 中的一条指令。
Example
:以下 Dockerfile 包含四个命令,每个命令创建一个层。
从 ubuntu:15.04 复制。 /app RUN make /app CMD python /app/app.py
重要的是,每一层都只是与其前一层的一组差异。
容器。当您创建一个新容器时,您会在基础层之上添加一个新的可写层。该层通常称为“容器层”。对正在运行的容器所做的所有更改,例如写入新文件、修改现有文件和删除文件,都会写入这个薄的可写容器层。
因此,容器和图像之间的主要区别在于顶部可写层。添加新数据或修改现有数据的所有对容器的写入都存储在此可写层中。当容器被删除时,可写层也被删除。底层图像保持不变。
从磁盘大小的角度理解图像和容器
要查看正在运行的容器的大致大小,您可以使用 docker ps -s
命令。您将获得 size
和 virtual size
作为两个输出:
大小:用于每个容器的可写层的数据量(在磁盘上)
Virtual Size:容器使用的只读图像数据所使用的数据量。多个容器可能共享部分或全部只读图像数据。因此,这些不是添加剂。即,您不能添加所有虚拟大小来计算映像使用了多少磁盘大小
另一个重要概念是写时复制策略
如果一个文件或目录存在于镜像中的较低层,并且另一个层(包括可写层)需要对其进行读取访问,则它只使用现有文件。其他层第一次需要修改文件时(构建镜像或运行容器时),文件被复制到该层并修改。
我希望这可以帮助像我这样的其他人。
简单地说,如果一个图像是一个类,那么一个容器就是一个类的一个实例,就是一个运行时对象。
容器只是一个可执行二进制文件,将由主机操作系统在一组限制下运行,这些限制使用知道如何告诉操作系统应用哪些限制的应用程序(例如,Docker)预设。
典型的限制是进程隔离相关、安全相关(如使用 SELinux 保护)和系统资源相关(内存、磁盘、CPU 和网络)。
直到最近,只有基于 Unix 的系统中的内核才支持在严格限制下运行可执行文件的能力。这就是为什么今天大多数容器讨论主要涉及 Linux 或其他 Unix 发行版。
Docker 是那些知道如何告诉操作系统(主要是 Linux)在哪些限制下运行可执行文件的应用程序之一。可执行文件包含在 Docker 映像中,它只是一个 tar 文件。该可执行文件通常是 Linux 发行版用户空间(Ubuntu、CentOS、Debian 等)的精简版本,预配置为在其中运行一个或多个应用程序。
尽管大多数人使用 Linux 基础作为可执行文件,但它可以是任何其他二进制应用程序,只要主机操作系统的内核可以运行它(参见 creating a simple base image using scratch)。无论 Docker 镜像中的二进制文件是 OS 用户空间还是简单的应用程序,对于 OS 主机来说,它只是另一个进程,一个由预设 OS 边界支配的包含进程。
其他应用程序(如 Docker)可以告诉主机操作系统在进程运行时应用哪些边界,包括 LXC、libvirt 和 systemd。 Docker 过去使用这些应用程序与 Linux 操作系统间接交互,但现在 Docker 使用自己的名为“libcontainer”的库直接与 Linux 交互。
所以容器只是在受限模式下运行的进程,类似于 chroot 过去所做的。
IMO,Docker 与任何其他容器技术的不同之处在于它的存储库(Docker Hub)及其管理工具,这使得使用容器变得非常容易。
请参阅 Docker (software)。
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
你可以看到它非常像一个操作系统。
将图像视为容器的“快照”可能会有所帮助。
您可以从容器制作图像(新的“快照”),也可以从图像启动新容器(实例化“快照”)。例如,您可以从基础镜像实例化一个新容器,在容器中运行一些命令,然后将其“快照”为新镜像。然后,您可以从该新映像中实例化 100 个容器。
其他需要考虑的事项:
图像由层组成,层是快照“差异”;当您推送图像时,只有“差异”被发送到注册表。
Dockerfile 在基础镜像之上定义了一些命令,这些命令创建了新的层(“diffs”),从而产生了一个新的镜像(“快照”)。
容器总是从图像中实例化。
图像标签不仅仅是标签。它们是图像的“全名”(“repository:tag”)。如果同一个镜像有多个名字,在做docker镜像的时候会显示多次。
Image 相当于 OOP 中的类定义,层是该类的不同方法和属性。
容器是图像的实际实例化,就像对象是实例化或类的实例一样。
一个 Docker 镜像将应用程序运行所需的应用程序和环境打包,容器就是该镜像的运行实例。
镜像是 Docker 的打包部分,类似于“源代码”或“程序”。容器是 Docker 的执行部分,类似于“进程”。
在这个问题中,只提到了“程序”部分,这就是图像。 Docker 的“运行”部分是容器。当容器运行并进行更改时,就好像该进程对其自己的源代码进行更改并将其保存为新图像一样。
与编程方面一样,
图片为源代码。
当源代码被编译和构建时,它被称为应用程序。
类似于“为图像创建实例时”,它被称为“容器”。
我认为最好在开始时解释。
假设您运行命令 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 才能运行。如果您只在计算机上安装了 Python v2,则只会运行 Chrome。
为了使这两种情况都有效,您需要以某种方式使用称为命名空间的操作系统功能。命名空间是一项功能,它使您有机会隔离进程、硬盘驱动器、网络、用户、主机名等。
因此,当我们谈论图像时,实际上是在谈论文件系统快照。图像是一个物理文件,其中包含构建特定容器的方向和元数据。容器本身就是一个图像的实例;它使用仅适用于该容器的命名空间来隔离硬盘驱动器。因此,容器是一个进程或一组进程,它将分配给它的不同资源分组。
我想在 docker images
和 containers
之间填写缺失的部分。 Docker 为容器使用 联合文件系统 (UFS),它允许多个文件系统以层次结构挂载并显示为单个文件系统.映像中的文件系统已挂载为 read-only
层,对运行容器的任何更改都会对挂载在此之上的 read-write
层进行。正因为如此,Docker 只需要查看最顶层的读写层就可以找到对运行系统所做的更改。
我会用以下类比来说明它:
+-----------------------------+-------+-----------+
| Domain | Meta | Concrete |
+-----------------------------+-------+-----------+
| Docker | Image | Container |
| Object oriented programming | Class | Object |
+-----------------------------+-------+-----------+
https://i.stack.imgur.com/wTqQm.png
码头工人形象:
映像是包含所有依赖项和运行非常特定程序所需的所有配置的单个文件,例如 redis 是您刚刚下载的映像(通过运行命令 docker run redis)应该运行。
https://i.stack.imgur.com/Cx1eo.png
容器是图像的一个实例,你可以把它想象成一个正在运行的程序,它有自己独立的硬件资源集,所以它有自己的小集合,或者它自己的小内存空间有自己的小空间网络技术和它自己的小空间硬盘空间也是如此。
现在让我们检查一下您何时发出以下命令: sudo docker run hello-world
https://i.stack.imgur.com/ti4q8.png
现在,当我们运行该命令并将其发送到 docker 服务器时,后台很快就会发生一系列操作。 Docker 服务器看到我们正在尝试使用名为 hello world 的映像启动一个新容器。
https://i.stack.imgur.com/BFuvo.png
现在因为你和我刚刚在我们的个人计算机上安装了 Docker,图像缓存当前是空的,我们没有之前已经下载过的图像。
因此,由于图像缓存为空,docker 服务器决定使用名为 Docker hub 的免费服务。 Docker Hub 是一个免费的公共镜像存储库,您可以免费下载并在您的个人计算机上运行。因此 Docker 服务器联系到 Docker Hub 并下载了 hello world 文件并将其存储在您的计算机上的图像缓存中,现在它可以在未来的某个时候非常快速地重新运行,而无需从码头工人枢纽。
之后 docker 服务器会使用它来创建一个容器的实例,而我们知道容器是一个镜像的实例,它的唯一目的就是运行一个非常具体的程序。因此,docker 服务器基本上从图像缓存中获取该图像文件并将其加载到内存中以从中创建一个容器,然后在其中运行一个程序。单个程序的目的是打印出您看到的消息。
什么是容器:首先,图像是如何创建容器的蓝图。
https://i.stack.imgur.com/xVdEm.png
图像之于类,就如同容器之于对象。
容器是图像的实例,因为对象是类的实例。
*在 docker 中,an image is an immutable file 包含运行 docker 应用程序所需的源代码和信息。它可以独立于容器而存在。
*Docker 容器是 virtualized environments created during runtime,需要映像才能运行。 docker 网站上有一张图片显示了这种关系:
https://i.stack.imgur.com/fO88v.png
对于一个虚拟的编程类比,您可以认为 Docker 有一个抽象的 ImageFactory,其中包含它们来自 store 的 ImageFactories。
然后,一旦您想从该 ImageFactory 创建一个应用程序,您将拥有一个新容器,您可以根据需要对其进行修改。 DotNetImageFactory 将是不可变的,因为它充当一个抽象工厂类,它只提供您想要的实例。
IContainer newDotNetApp = ImageFactory.DotNetImageFactory.CreateNew(appOptions);
newDotNetApp.ChangeDescription("I am making changes on this instance");
newDotNetApp.Run();
简而言之:
容器是内核中的一个分区(虚拟),它共享一个通用的操作系统并运行一个镜像(Docker 镜像)。
容器是一个自我维持的应用程序,它将包含包和所有必要的依赖项来运行代码。
一个 Docker 容器正在运行一个镜像的实例。您可以将图像与程序相关联,将容器与进程相关联:)
Dockerfile 就像生成 tarball(Docker 映像)的 Bash 脚本。
Docker 容器就像是压缩包的提取版本。您可以在不同的文件夹(容器)中拥有任意数量的副本。
映像是构建容器(运行实例)的蓝图。
正如对象是面向对象编程语言中类的实例一样,Docker 容器也是 Docker 镜像的实例。
图像就像一个类,容器就像一个类的对象,因此您可以拥有无限数量的容器,其行为类似于图像。一个类是一个蓝图,它自己不做任何事情。您必须在程序中创建对象的实例才能做任何有意义的事情。图像和容器的情况也是如此。您定义映像,然后创建运行该映像的容器。它并不完全相似,因为对象是一个类的实例,而容器就像一个空洞的地方,你可以使用图像来构建一个运行中的主机,而这正是图像所说的
映像或容器映像是包含应用程序代码、应用程序运行时、配置、依赖库的文件。该图像基本上将所有这些包装到一个安全的不可变单元中。使用适当的 docker 命令来构建映像。图像具有图像 ID 和图像标签。标签的格式通常为
当您开始使用映像运行应用程序时,您实际上启动了一个容器。因此,您的容器是一个沙箱,您可以在其中运行映像。 Docker 软件用于管理镜像和容器。
Image 是一个安全包,其中包含您的应用程序工件、库、配置和应用程序运行时。容器是图像的运行时表示。
docker 镜像或容器镜像通常是一个软件包,它将您的应用程序可执行文件、依赖项、配置和应用程序运行时包装到一个安全且不可变的单元中。当我说应用程序可执行时,它因应用程序而异。例如,如果它是一个 java 应用程序,它将是一个 jar 文件,对于节点应用程序可能是一个 js 文件。同样,应用程序运行时取决于您的应用程序本身。对于 java 应用程序,它将是 JRE,对于节点应用程序,它是节点二进制文件。
您可以根据 image manifest 文件(例如 Dockerfile)中提供的说明构建您的图像。构建映像后,您可以将其存储在本地或某些容器映像存储库中,例如 hub.docker.com。
当您想将映像作为应用程序工作负载运行时,您需要启动一个需要您的映像的容器。容器是图像的运行时实例。
要构建映像、存储映像并将其作为容器运行,您需要一个名为 container runtime 的软件,例如 Docker。这些容器运行时符合 Open Container Initiative 为映像创建和容器运行提供的标准。