介绍
Docker 是一种流行的容器化工具,用于为软件应用程序提供一个文件系统,该文件系统包含它们运行所需的一切。使用 Docker 容器可确保软件无论部署在何处都能以相同的方式运行,因为其运行时环境是一致的。
一般来说,Docker 容器是短暂的,只要容器中发出的命令完成就可以运行。然而,有时应用程序需要在容器被删除后共享对数据的访问或保留数据。数据库、用户生成的网站内容和日志文件只是一些不切实际或不可能包含在 Docker 映像中但应用程序需要访问的数据示例。Docker Volumes提供了对数据的持久访问。
Docker 卷可以在创建容器的同一命令中创建和附加,也可以独立于任何容器创建并在以后附加。在本文中,我们将研究在容器之间共享数据的四种不同方式。
先决条件
要阅读本文,您需要一个 Ubuntu 20.04 服务器,其中包含以下内容:
- 具有 sudo 权限的非 root 用户。使用 Ubuntu 20.04的初始服务器设置指南解释了如何设置。
- 按照如何在 Ubuntu 20.04 上安装和使用 Docker 的步骤 1 和步骤 2中的说明安装 Docker
注意:尽管先决条件给出了在 Ubuntu 20.04 上安装 Docker 的说明,但docker
只要安装了 Docker 并且已将 sudo 用户添加到docker
组中,本文中针对 Docker 数据卷的命令应该可以在其他操作系统上运行。
步骤 1 — 创建独立卷
该docker volume create
命令在 Docker 的 1.9 版本中引入,允许您创建卷而无需将其与任何特定容器相关联。我们将使用此命令添加名为 的卷DataVolume1
:
- docker volume create --name DataVolume1
显示名称,说明命令成功:
OutputDataVolume1
为了使用该卷,我们将从 Ubuntu 映像创建一个新容器,使用该--rm
标志在退出时自动删除它。我们还将用于-v
挂载新卷。-v
需要卷的名称,一个冒号,然后是卷应出现在容器内的位置的绝对路径。如果路径中的目录不作为映像的一部分存在,则会在命令运行时创建它们。如果它们确实存在,则安装的卷将隐藏现有内容:
- docker run -ti --rm -v DataVolume1:/datavolume1 ubuntu
在容器中,让我们向卷写入一些数据:
- echo "Example1" > /datavolume1/Example1.txt
因为我们使用了--rm
标志,所以我们的容器会在退出时自动删除。但是,我们的卷仍然可以访问。
- exit
我们可以使用以下命令验证该卷是否存在于我们的系统中docker volume inspect
:
- docker volume inspect DataVolume1
Output[
{
"CreatedAt": "2018-07-11T16:57:54Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/DataVolume1/_data",
"Name": "DataVolume1",
"Options": {},
"Scope": "local"
}
]
注意:我们甚至可以查看主机上列为Mountpoint
. 但是,我们应该避免更改它,因为如果应用程序或容器不知道更改,它可能会导致数据损坏。
接下来,让我们启动一个新容器并附加DataVolume1
:
- docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu
验证内容:
- cat /datavolume1/Example1.txt
OutputExample1
退出容器:
- exit
在这个例子中,我们创建了一个卷,将它附加到一个容器,并验证了它的持久性。
第 2 步 – 创建一个在移除容器时仍然存在的卷
在我们的下一个示例中,我们将在创建容器的同时创建一个卷,删除该容器,然后将该卷附加到一个新容器。
我们将使用该docker run
命令使用基本 Ubuntu 映像创建一个新容器。 -t
会给我们一个终端,并-i
允许我们与之交互。为清楚起见,我们将使用--name
来标识容器。
该-v
标志将允许我们创建一个新的卷,我们称之为DataVolume2
. 我们将使用冒号将此名称与卷应安装在容器中的路径分开。最后,我们将指定基Ubuntu的形象,并依靠在默认命令Ubuntu的基本图像的泊坞文件,bash
对我们放到一个shell:
- docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu
注:该-v
标志是非常灵活的。它可以绑定挂载或命名卷,只需稍微调整语法即可。如果第一个参数以 a 开头,/
或者~/
您正在创建一个绑定挂载。删除它,您正在命名卷。例如:
-v /path:/path/in/container
挂载主机目录,/path
在/path/in/container
-v path:/path/in/container
创建一个path
与主机没有关系的卷。
有关从主机绑定安装目录的更多信息,请参阅如何在 Docker 容器和主机之间共享数据
在容器中,我们将一些数据写入卷:
- echo "Example2" > /datavolume2/Example2.txt
- cat /datavolume2/Example2.txt
OutputExample2
让我们退出容器:
- exit
当我们重新启动容器时,卷将自动挂载:
- docker start -ai Container2
让我们验证卷确实已挂载并且我们的数据仍然存在:
- cat /datavolume2/Example2.txt
OutputExample2
最后,让我们退出并清理:
- exit
如果一个卷被容器引用,Docker 不会让我们删除它。让我们看看当我们尝试时会发生什么:
- docker volume rm DataVolume2
该消息告诉我们该卷仍在使用中,并提供了容器 ID 的长版本:
OutputError response from daemon: unable to remove volume: remove DataVolume2: volume is in use - [d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63]
我们可以使用这个 ID 来移除容器:
- docker rm d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
Outputd0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
移除容器不会影响音量。我们可以通过列出卷来看到它仍然存在于系统上docker volume ls
:
- docker volume ls
OutputDRIVER VOLUME NAME
local DataVolume2
我们可以用docker volume rm
它来删除它:
- docker volume rm DataVolume2
在此示例中,我们在创建容器的同时创建了一个空数据卷。在我们的下一个示例中,我们将探索当我们使用已经包含数据的容器目录创建卷时会发生什么。
步骤 3 — 从包含数据的现有目录创建卷
通常,独立创建卷docker volume create
和在创建容器的同时创建卷是等效的,只有一个例外。如果我们在创建容器的同时创建一个卷,并提供包含基本映像中数据的目录的路径,则该数据将被复制到该卷中。
例如,我们将创建一个容器并在 中添加数据卷/var
,该目录包含基础镜像中的数据:
- docker run -ti --rm -v DataVolume3:/var ubuntu
基础镜像/var
目录中的所有内容都被复制到卷中,我们可以将该卷挂载到一个新容器中。
退出当前容器:
- exit
这一次,bash
我们将发出自己的ls
命令,而不是依赖基础映像的默认命令,该命令将在不进入 shell 的情况下显示卷的内容:
- docker run --rm -v DataVolume3:/datavolume3 ubuntu ls datavolume3
该目录datavolume3
现在具有基本映像/var
目录内容的副本:
Outputbackups
cache
lib
local
lock
log
mail
opt
run
spool
tmp
我们不太可能希望以/var/
这种方式挂载,但如果我们已经制作了自己的映像并想要一种简单的方法来保存数据,这可能会有所帮助。在下一个示例中,我们将演示如何在多个容器之间共享卷。
第 4 步——在多个 Docker 容器之间共享数据
到目前为止,我们一次将一个卷附加到一个容器。通常,我们希望多个容器附加到同一个数据卷。这相对容易完成,但有一个重要的警告:此时,Docker 不处理文件锁定。如果您需要多个容器写入卷,则在这些容器中运行的应用程序必须设计为写入共享数据存储,以防止数据损坏。
创建 Container4 和 DataVolume4
使用docker run
创建一个名为新容器Container4
附有一个数据卷:
- docker run -ti --name=Container4 -v DataVolume4:/datavolume4 ubuntu
接下来我们将创建一个文件并添加一些文本:
- echo "This file is shared between containers" > /datavolume4/Example4.txt
然后,我们将退出容器:
- exit
这使我们返回到主机命令提示符,我们将在其中创建一个新容器,从Container4
.
创建 Container5 并从 Container4 挂载卷
我们将创建Container5
,并从Container4
以下位置挂载卷:
- docker run -ti --name=Container5 --volumes-from Container4 ubuntu
让我们检查数据持久性:
- cat /datavolume4/Example4.txt
OutputThis file is shared between containers
现在让我们附加一些文本Container5
:
- echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt
最后,我们将退出容器:
- exit
接下来,我们将检查我们的数据是否仍然存在于Container4
.
查看在 Container5 中所做的更改
让我们Container5
通过重新启动来检查写入数据卷的更改Container4
:
- docker start -ai Container4
检查更改:
- cat /datavolume4/Example4.txt
OutputThis file is shared between containers
Both containers can write to DataVolume4
现在我们已经验证了两个容器都能够读取和写入数据卷,我们将退出容器:
- exit
同样,Docker 不处理任何文件锁定,因此应用程序必须自己解决文件锁定问题。可以将 Docker 卷挂载为只读,以确保当容器需要只读访问时,通过添加:ro
. 让我们看看这是如何工作的。
启动容器 6 并以只读方式挂载卷
一旦卷被挂载到容器中,而不是像我们使用典型的 Linux 文件系统那样卸载它,我们可以改为创建一个以我们想要的方式挂载的新容器,并在需要时删除以前的容器。为了使卷只读,我们附加:ro
到容器名称的末尾:
- docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu
我们将通过尝试删除我们的示例文件来检查只读状态:
- rm /datavolume4/Example4.txt
Outputrm: cannot remove '/datavolume4/Example4.txt': Read-only file system
最后,我们将退出容器并清理我们的测试容器和卷:
- exit
现在我们完成了,让我们清理我们的容器和卷:
- docker rm Container4 Container5 Container6
- docker volume rm DataVolume4
在此示例中,我们展示了如何使用数据卷在两个容器之间共享数据以及如何将数据卷挂载为只读。
结论
在本教程中,我们创建了一个数据卷,它允许通过删除容器来保留数据。我们在容器之间共享数据量,但需要注意的是,应用程序需要设计为处理文件锁定以防止数据损坏。最后,我们展示了如何以只读模式挂载共享卷。如果您有兴趣了解如何在容器和主机系统之间共享数据,请参阅如何在 Docker 容器和主机之间共享数据。