如何在Ubuntu22.04上的Docker容器之间共享数据
介绍
Docker 是一种流行的容器化工具,用于为软件应用程序提供一个文件系统,该文件系统包含它们运行所需的一切。 使用 Docker 容器可确保软件无论部署在何处都以相同的方式运行,因为它的运行时环境是一致的。
通常,Docker 容器是短暂的,运行时间与容器中发出的命令完成所需的时间一样长。 然而,有时,应用程序需要在容器被删除后共享数据访问或持久化数据。 数据库、用户为网站生成的内容和日志文件只是不切实际或不可能包含在 Docker 映像中但应用程序需要访问的数据的几个示例。 Docker Volumes 提供对数据的持久访问。
Docker 卷可以在创建容器的同一命令中创建和附加,也可以独立于任何容器创建并在以后附加。 在本文中,您将了解在容器之间共享数据的四种不同方式。
先决条件
要阅读本文,您将需要一个 Ubuntu 22.04 服务器,其中包含以下内容:
- 具有 sudo 权限的非 root 用户。 Initial Server Setup with Ubuntu 22.04 指南解释了如何进行设置。
- 使用 How To Install and Use Docker on Ubuntu 22.04 的 Step 1 和 Step 2 的说明安装 Docker
注意: 尽管先决条件给出了在 Ubuntu 22.04 上安装 Docker 的说明,但只要安装了 Docker 并且sudo 用户已添加到 docker
组。
第 1 步 — 创建独立卷
在 Docker 的 1.9 版本中引入的 docker volume create
命令允许您创建卷,而无需将其与任何特定容器相关联。 您将使用此命令添加名为 DataVolume1
的卷:
docker volume create --name DataVolume1
显示名称,说明命令成功:
OutputDataVolume1
要使用该卷,您将从 Ubuntu 映像创建一个新容器,使用 --rm
标志在您退出时自动删除它。 您还将使用 -v
安装新卷。 -v
需要卷的名称、冒号,然后是卷应出现在容器内的绝对路径。 如果路径中的目录不作为映像的一部分存在,则将在命令运行时创建它们。 如果他们 do 存在,挂载的卷将隐藏现有的内容:
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 基本映像的 Docker 文件 、bash
中的默认命令,将我们放入 shell:
docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu
注意: -v
标志非常灵活。 它可以绑定或命名一个卷,只需稍微调整语法。 如果第一个参数以 /
或 ~/
开头,则您正在创建绑定挂载。 删除它,您将命名该卷。 例如:
-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
这一次,您将发出自己的 ls
命令,而不是依赖基础映像的默认 bash
命令,该命令将在不进入 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 中所做的更改
现在,通过重新启动 Container4
检查 Container5
写入数据卷的更改:
docker start -ai Container4
检查更改:
cat /datavolume4/Example4.txt
OutputThis file is shared between containers Both containers can write to DataVolume4
现在您已经验证了两个容器都能够从数据卷中读取和写入,您将退出容器:
exit
同样,Docker 不处理任何文件锁定,因此应用程序 必须 自行考虑文件锁定。 可以通过添加 :ro
将 Docker 卷挂载为只读,以确保在容器需要只读访问时不会意外发生数据损坏。 现在,您将了解它是如何工作的。
启动 Container 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 容器和主机之间共享数据。