如何在Docker容器之间共享数据

一般来说,Docker容器是短暂的,运行只要在容器中发出的命令完成就可以运行。然而,有时,应用程序需要在容器被删除后共享对数据的访问或持久化数据。数据库,网站的用户生成的内容和日志文件只是一些数据的示例,这些数据在Docker镜像中是不切实际或不可能包含的,但是哪些应用程序需要访问。 Docker卷提供对数据的持久访问。

介绍

Docker是一种流行的容器化工具,用于向软件应用程序提供包含运行所需的所有内容的文件系统。使用Docker容器确保软件将以相同的方式运行,而不管其部署在哪里,因为它的运行时环境无残忍地一致。 一般来说,Docker容器是短暂的,只要在容器中发出的命令完成就运行。然而,有时,应用程序需要在容器被删除后共享对数据的访问或持久化数据。数据库,网站的用户生成的内容和日志文件只是一些数据的示例,这些数据在Docker镜像中是不切实际或不可能包含的,但是哪些应用程序需要访问。 Docker卷提供对数据的持久访问。

先决条件

要跟随这篇文章,你将需要一个Ubuntu 16.04服务器,具有以下: 注:即使先决条件给出了在Ubuntu 16.04安装Docker指令, docker本文中Docker数据量的命令应该在其它操作系统上,只要Docker已安装并运行sudo用户已被添加到docker组。 Docker卷可以在创建容器的同一命令中创建和附加,也可以独立于任何容器创建,然后再连接。在本文中,我们将介绍在容器之间共享数据的四种不同方法。

1 - 创建独立卷

在Docker的1.9版本中引入的docker volume create命令,可以没有它与任何特定的容器创建卷。 我们将使用这个命令来添加一个名为量DataVolume1
docker volume create --name DataVolume1
将显示名称,表示命令成功。
DataVolume1
为了使用量,我们将创建一个从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
[
    {
        "Name": "DataVolume1",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/datavolume1/_data",
        "Labels": null,
        "Scope": "local"
    }
]
注意:我们甚至可以看一下数据在主机上列为安装点的路径。但是,我们应该避免更改它,因为它可能导致数据损坏,如果应用程序或容器不知道更改。 接下来,让我们开始一个新的容器,并附加DataVolume1
docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu
cat /datavolume1/Example1.txt
Example1
让我们退出容器。
exit
在此示例中,我们创建了一个卷,将其附加到容器,并验证其持久性。

2 - 创建在容器删除时持续存在的卷

在下一个示例中,我们将创建一个与容器同时的卷,删除容器,然后将卷附加到一个新容器。 我们将使用docker run命令创建使用基地的新容器ubuntu形象。 -t会给我们一个终端, -i将使我们能够与它进行交互。 为了清楚起见,我们将使用--name识别容器。 接下来,我们将添加-v标志创建 我们将使用-v来创建新卷。 我们将其命名为DataVolume2 。 我们将使用冒号将此名称与卷应该在容器中加载的路径分开。 最后,我们将指定基Ubuntu的形象,并依靠在默认的命令Ubuntu的基本图像的Docker文件bash ,我们放到一个外壳。
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与主机没有任何关系。
欲了解更多关于从主机bindmounting的目录,请参阅如何Docker容器和主机之间的数据共享 当我们在那里,我们将写一些数据到卷:
echo "Example2" > /datavolume2/Example2.txt
cat /datavolume2/Example2.txt
Example2
我们将退出容器:
exit
当我们重新启动它,卷将自动安装。
docker start -ai Container2
让我们验证该卷是否已安装,并且我们的数据仍然存在:
cat /datavolume2/Example2.txt
Example2
最后,让我们退出并清理。
exit
Docker不会让我们删除一个容器引用的卷。让我们看看当我们尝试时会发生什么:
docker volume rm DataVolume2
该消息告诉我们,卷仍在使用,并提供容器ID的长版本:
Error response from daemon: Unable to remove volume, 
volume still in use: remove DataVolume2: volume is in use - 
[719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c]
我们可以使用ID来删除容器:
docker rm 719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c
719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c
删除容器不会影响卷。我们可以看到,它仍然存在于系统中通过列出与卷docker volume ls
docker volume ls
DRIVER              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目录的所有内容复制到卷中,我们可以将该卷挂载到新容器中。这一次,而不是依赖于基础映像的默认bash命令,我们会发出我们自己的ls命令,该命令将显示卷的内容,而无需输入外壳:
docker run --rm -v DataVolume3:/datavolume3 ubuntu ls DataVolume3
DataVolume3具有的基本映像的内容拷贝/var目录:
backups
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挂载数据卷

从Container4创建Container5和装载卷

我们将创建Container5,并从Container4挂载卷:
docker run -ti --name=Container5 --volumes-from Container4 ubuntu
cat /datavolume4/Example4.txt
This file is shared between containers
现在让我们从第二个容器中添加一些文本:
echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt
最后,我们将退出容器:
exit
接下来,我们将检查我们的数据是否仍然存在Container4。

查看Container5中所做的更改

让我们通过重新启动Container4来检查由Container5写入数据卷的更改:
docker start -ai Container4
cat /datavolume4/Example4.txt
cat /DataVolume4/Example4.txt
This 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
rm: cannot remove '/datavolume4/Example4.txt': Read-only file system
最后,我们将退出容器并清理我们的测试容器和卷:
exit
现在我们完成了,让我们清理我们的容器和卷:
docker rm Container4 Container5 Container6
docker volume rm DataVolume4
在本例中,我们已经展示了如何使用数据卷在两个容器之间共享数据,以及如何以只读方式挂载数据卷。

结论

在本教程中,我们创建了一个数据卷,允许通过删除容器来保留数据。我们已经在容器之间共享了数据卷,需要注意的是,应用程序需要设计为处理文件锁定以防止数据损坏。最后,我们展示了如何以只读模式挂载共享卷。如果你有兴趣了解容器和主机系统之间共享数据,请参阅如何将Docker容器和主机之间的数据共享