使用 Docker 时,我们从基础镜像开始。我们启动它,创建更改,这些更改保存在形成另一个图像的层中。
所以最终我有一个我的 PostgreSQL 实例的图像和一个我的 web 应用程序的图像,对它们的更改继续被持久化。
什么是容器?
使用 Docker 时,我们从基础镜像开始。我们启动它,创建更改,这些更改保存在形成另一个图像的层中。
所以最终我有一个我的 PostgreSQL 实例的图像和一个我的 web 应用程序的图像,对它们的更改继续被持久化。
什么是容器?
图像的一个实例称为容器。您有一个图像,它是您描述的一组图层。如果你启动这个镜像,你就有了这个镜像的运行容器。您可以拥有多个运行同一映像的容器。
您可以看到所有图像,docker images
而您可以看到正在运行的容器docker ps
(并且您可以看到所有容器docker ps -a
)。
因此,图像的运行实例是一个容器。
来自我关于自动化 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
需要注意的一些事项:
-t
标志docker build
,或来自docker tag
现有图像。您可以使用对您有意义的命名法来标记图像,但要知道 docker 将使用标记作为 adocker push
或docker pull
.[REGISTRYHOST/][USERNAME/]NAME[:TAG]
. 对于ubuntu
上面, REGISTRYHOST 被推断为registry.hub.docker.com
。因此,如果您计划将您的图像存储my-application
在注册表中docker.example.com
,您应该标记该图像docker.example.com/my-application
。latest
标签并不神奇,它只是您不指定标签时的默认标签。<none>
TAG 和 REPOSITORY。很容易忘记它们。用编程的比喻来说,如果图像是一个类,那么容器就是一个类的实例——一个运行时对象。希望容器是您使用 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 化版本,这样我就有了一个私有的地方来存储我的图像。再次,有几点需要注意:
docker ps
只输出正在运行的容器。您可以使用 . 查看所有容器(正在运行或已停止)docker ps -a
。--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 的已知痛点,可能会在未来的版本中得到解决。但是,如果对图像和容器有清晰的了解,可以通过以下几种做法来避免这些情况:
docker rm [CONTAINER_ID]
。docker rmi [IMAGE_ID]
.用简单的话来说。
图片-
用于创建容器的文件系统和配置(只读)应用程序。更多细节。
容器-
容器和图像之间的主要区别在于顶部可写层。容器正在运行具有顶层可写层的 Docker 镜像实例 。容器运行实际的应用程序。容器包括应用程序及其所有依赖项。当容器被删除时,可写层也被删除。底层图像保持不变。更多细节。
其他需要注意的重要条款:
码头工人守护进程-
在管理构建、运行和分发 Docker 容器的主机上运行的后台服务。
码头工人-
允许用户与 Docker 守护进程交互的命令行工具。
码头工人商店-
除其他外,Store 是 Docker 映像的注册表。您可以将注册表视为所有可用 Docker 映像的目录。
这篇博文中的一张图片值一千字。
(要更深入地了解,请阅读此内容。)
概括:
docker run image_name:tag_name
) => 给出一个正在运行的 Image 即容器(可编辑)虽然将容器视为运行映像最简单,但这并不十分准确。
图像实际上是一个可以变成容器的模板。为了将镜像变成容器,Docker 引擎获取镜像,在顶部添加一个读写文件系统,并初始化各种设置,包括网络端口、容器名称、ID 和资源限制。一个正在运行的容器有一个当前正在执行的进程,但是一个容器也可以被停止(或者用 Docker 的术语来说是退出)。退出的容器与映像不同,因为它可以重新启动并保留其设置和任何文件系统更改。
也许解释整个工作流程会有所帮助。
一切都从Dockerfile开始。Dockerfile 是镜像的源代码。
创建 Dockerfile 后,您可以构建它以创建容器的映像。该图像只是“源代码”的“编译版本”,即 Dockerfile。
一旦你有了容器的镜像,你应该使用registry重新分发它。注册表就像一个 Git 存储库——您可以推送和拉取图像。
接下来,您可以使用该映像运行容器。运行中的容器在很多方面与虚拟机非常相似(但没有管理程序)。
Dockerfile → (Build) → Image → (Run) → Container。
Dockerfile:包含一组 Docker 指令,以您喜欢的方式配置您的操作系统,并安装/配置您的所有软件。
图片:编译后的 Dockerfile。节省您每次需要运行容器时重新构建 Dockerfile 的时间。这是一种隐藏您的供应代码的方法。
Container:虚拟操作系统本身。您可以 ssh 进入它并运行您希望的任何命令,就好像它是一个真实的环境一样。您可以从同一个镜像运行 1000 多个容器。
这是显示各种命令及其相关输入和输出的端到端工作流程。这应该澄清图像和容器之间的关系。
+------------+ docker build +--------------+ docker run -dt +-----------+ docker exec -it +------+
| Dockerfile | --------------> | Image | ---------------> | Container | -----------------> | Bash |
+------------+ +--------------+ +-----------+ +------+
^
| docker pull
|
+--------------+
| Registry |
+--------------+
要列出您可以运行的图像,请执行:
docker image ls
要列出可以在以下位置执行命令的容器:
docker ps
尽管阅读了这里的所有问题,但我还是无法理解图像和层的概念,然后最终偶然发现了来自 Docker 的这个出色的文档(呃!)。
那里的例子确实是理解整个概念的关键。这是一篇很长的文章,所以我总结了需要真正掌握的关键点,以使其清晰。
Image : 一个 Docker 镜像由一系列只读层组成
层:每一层代表图像的 Dockerfile 中的一条指令。
Example
:下面的 Dockerfile 包含四个命令,每个命令创建一个层。
来自 ubuntu:15.04
复制 。/应用程序
运行制作/应用
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 基础作为可执行文件,但它可以是任何其他二进制应用程序,只要主机操作系统的内核可以运行它(请参阅使用从头创建简单的基础映像)。无论 Docker 镜像中的二进制文件是 OS 用户空间还是简单的应用程序,对于 OS 主机来说,它只是另一个进程,一个由预设 OS 边界支配的包含进程。
其他应用程序,如 Docker,可以告诉主机操作系统在进程运行时应用哪些边界,包括LXC、libvirt和systemd。Docker 过去使用这些应用程序与 Linux 操作系统间接交互,但现在 Docker 使用自己的名为“ libcontainer ”的库直接与 Linux 交互。
所以容器只是在受限模式下运行的进程,类似于chroot过去所做的。
IMO,Docker 与任何其他容器技术的不同之处在于它的存储库(Docker Hub)及其管理工具,这使得使用容器变得非常容易。
请参阅Docker(软件)。
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 个容器。
其他需要考虑的事项:
docker images
。Image相当于 OOP 中的类定义,层是该类的不同方法和属性。
容器是图像的实际实例化,就像对象是实例化或类的实例一样。
一个 Docker 镜像将应用程序运行所需的应用程序和环境打包,容器就是该镜像的运行实例。
镜像是 Docker 的打包部分,类似于“源代码”或“程序”。容器是 Docker 的执行部分,类似于“进程”。
在这个问题中,只提到了“程序”部分,这就是图像。Docker 的“运行”部分是容器。当容器运行并进行更改时,就好像该进程对其自己的源代码进行更改并将其保存为新图像一样。
我想在docker images
和之间填补缺失的部分containers
。Docker为容器使用联合文件系统( UFS ),它允许将多个文件系统挂载在一个层次结构中并显示为单个文件系统。映像中的文件系统已作为一个read-only
层挂载,并且对运行容器的任何更改都会对read-write
挂载在此之上的层进行。正因为如此,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。
为了使这两种情况都有效,您需要以某种方式使用称为命名空间的操作系统功能。命名空间是一项功能,它使您有机会隔离进程、硬盘驱动器、网络、用户、主机名等。
因此,当我们谈论图像时,实际上是在谈论文件系统快照。图像是一个物理文件,其中包含构建特定容器的方向和元数据。容器本身就是图像的一个实例;它使用仅适用于此容器的命名空间来隔离硬盘驱动器。因此,容器是一个进程或一组进程,它将分配给它的不同资源分组。
我会用以下类比来说明它:
+-----------------------------+-------+-----------+
| Domain | Meta | Concrete |
+-----------------------------+-------+-----------+
| Docker | Image | Container |
| Object oriented programming | Class | Object |
+-----------------------------+-------+-----------+
Docker Client、Server、Machine、Images、Hub、Compose 都是项目工具软件,它们结合在一起形成一个平台,在这个平台上,围绕创建和运行称为容器的东西的生态系统,现在如果你运行命令docker run redis ,就可以达到称为 docker CLI 的东西到一个叫做 Docker Hub 的地方,它下载了一个叫做图像的文件。
码头工人形象:
映像是包含所有依赖项和运行非常特定程序所需的所有配置的单个文件,例如redis是您刚刚下载的映像(通过运行命令docker run redis)应该运行。
这是存储在硬盘上的单个文件,在某些时候,您可以使用此映像创建称为容器的东西。
容器是图像的一个实例,你可以把它想象成一个正在运行的程序,它有自己独立的硬件资源集,所以它有自己的小集合,或者它自己的小内存空间有自己的小空间网络技术和它自己的小空间硬盘空间也是如此。
现在让我们检查一下您何时发出以下命令: sudo docker run hello-world
上面的命令将启动 docker 客户端或 docker CLI,Docker CLI 负责从您那里获取命令,对它们进行一些处理,然后将命令传送到称为 docker 服务器的东西,而 docker 服务器在当我们运行命令 Docker run hello-world 时负责繁重的工作, 这意味着我们想使用名称为 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 服务器会使用它来创建一个容器的实例,我们知道容器是一个镜像的实例,它的唯一目的是运行一个非常特定的程序。因此,docker 服务器基本上从图像缓存中获取该图像文件并将其加载到内存中以从中创建一个容器,然后在其中运行一个程序。单个程序的目的是打印出您看到的消息。
什么是容器: 首先,图像是如何创建容器的蓝图。
容器是一个进程或一组进程,它们具有专门分配给它的一组资源,在下面的图表中,每当我们想到一个容器时,我们都有一些正在运行的进程,它向内核发送系统调用,内核将查看传入的系统调用并将其定向到硬盘驱动器的非常特定的部分、RAM、CPU 或它可能需要的任何其他东西,并且这些资源中的一部分可供该单一资源使用过程。
*在 docker 中,图像是一个不可变的文件,其中包含 docker 应用程序运行所需的源代码和信息。它可以独立于容器而存在。
*Docker 容器是在运行时创建的虚拟化环境,需要镜像才能运行。docker 网站上有一张图片显示了这种关系:
图像之于类,就如同容器之于对象。
容器是图像的实例,因为对象是类的实例。
映像是构建容器(运行实例)的蓝图。
Dockerfile 就像生成 tarball(Docker 映像)的 Bash 脚本。
Docker 容器就像是压缩包的提取版本。您可以在不同的文件夹(容器)中拥有任意数量的副本。
对于一个虚拟的编程类比,您可以认为 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 容器正在运行一个镜像的实例。您可以将图像与程序相关联,将容器与进程相关联:)
正如对象是面向对象编程语言中类的实例一样,Docker 容器也是 Docker 镜像的实例。
docker镜像或容器镜像通常是一个软件包,它将您的应用程序可执行文件、依赖项、配置和应用程序运行时包装到一个安全且不可变的单元中。当我说应用程序可执行时,它因应用程序而异。例如,如果它是一个 java 应用程序,它将是一个 jar 文件,对于节点应用程序可能是一个 js 文件。同样,应用程序运行时取决于您的应用程序本身。对于 java 应用程序,它将是 JRE,对于节点应用程序,它是节点二进制文件。
您可以根据 镜像清单文件(例如 Dockerfile)中提供的说明构建镜像。构建映像后,您可以将其存储在本地或某些容器映像存储库中,例如hub.docker.com。
当您想将映像作为应用程序工作负载运行时,您需要启动一个需要您的映像的容器。容器是图像的运行时实例。
要构建映像、存储映像并将其作为容器运行,您需要一个称为容器运行时的软件,例如Docker。这些容器运行时符合Open Container Initiative为图像创建和容器运行提供的标准。
图像就像一个类,容器就像一个类的对象,因此您可以拥有无限数量的容器,其行为类似于图像。一个类是一个蓝图,它自己不做任何事情。您必须在程序中创建对象的实例才能做任何有意义的事情。图像和容器的情况也是如此。您定义映像,然后创建运行该映像的容器。它并不完全相似,因为对象是一个类的实例,而容器就像一个空洞的地方,你可以使用图像来构建一个运行中的主机,而这正是图像所说的
映像或容器映像是包含应用程序代码、应用程序运行时、配置、依赖库的文件。该图像基本上将所有这些包装到一个安全的不可变单元中。使用适当的 docker 命令来构建映像。图像具有图像 ID 和图像标签。标签通常采用<docker-user-name>/image-name:tag的格式。
当您开始使用映像运行应用程序时,您实际上启动了一个容器。因此,您的容器是一个沙箱,您可以在其中运行映像。Docker 软件用于管理镜像和容器。
Image 是一个安全包,其中包含您的应用程序工件、库、配置和应用程序运行时。容器是图像的运行时表示。