在生产环境中使用 Docker 容器,往往需要对数据进行持久化保存,或者多个容器需要共享数据。
这时就会使用到容器数据卷,通过容器数据卷管理容器数据是一项使用容器的基本技能。
我将会用两篇文章,来详细介绍容器数据卷的使用及其相关内容。
容器技术使用 rootfs 机制与 Namespace,构建出与宿主机隔离开的文件系统。在用户使用 Docker 容器的时候,会产生一系列的数据文件。这些数据文件在 Docker 容器关闭时就会消失,但是其中部分数据是用户希望能够保存的。
Docker 将应用与运行环境打包成容器进行运行,用户希望在运行过程中产生的部分数据是可以持久化的,并且容器之间能够实现数据互通。这正是容器数据卷(Docker Volume)要解决的问题。
Docker 中的数据可以存储在类似于虚拟机磁盘的介质中,这种介质在 Docker 中称为数据卷。
数据卷以目录的形式呈现给 Docker,不仅可用来存储 Docker 应用的数据,还可以支持多个容器间数据共享,并且修改数据卷文件也不会影响镜像。在 Docker 中使用数据卷,就是在系统中挂载一个文件系统。
Docker 数据卷所使用的挂载技术,就是 Linux 的绑定挂载。其主要作用是,允许用户将一个目录或文件(并非整个设备),挂载到一个指定的目录上。
用户在该挂载点上进行的任何操作都只发生在被挂载的目录或文件上,而原挂载点的内容则会被隐藏起来且不受任何影响。容器利用数据卷与宿主机进行数据共享,从而实现容器间的数据共享与交换。
Docker 数据卷默认存储在宿主机的 /var/lib/Docker/volumes/
目录下,也可以指定挂载到任意位置。这种挂载仅存储在宿主机的内存中,永远不会写入宿主机的文件系统。如图所示
数据卷的特点如下
在 docker create 或 docker run 命令中,使用 -v 为容器增加一个数据卷,示例代码如下:
以上示例使用 docker run 命令运行了一个容器,并通过 -v 参数为容器添加了一个数据卷。
下面通过命令查看挂载信息,示例代码如下:
从以上示例中可以看到,宿主机已经在 /var/lib/Docker/volumes/
下自动生成了挂载目录。
下面通过命令行手动指定宿主机挂载目录,示例代码如下:
以上示例在后台运行了一个被命名为 test 的 Nginx 容器,并为它挂载数据卷。
下面查看容器 test 的状态及挂载数据信息,示例代码如下:
上述示例中,Mount 信息包含了上面创建的容器的详细挂载信息,Source 指定了本机路径,Destination 指定了容器内部的路径。
下面通过示例观察数据卷共享机制,会在宿主机与容器端之间多次切换,建议开启两个终端,示例代码如下:
以上示例创建了一个名为 test 的 Nginx 容器,并将容器内的 /app
目录挂载至宿主机的 /web/webapp
路径下。
下面分别查看宿主机与容器的根目录下的文件,示例代码如下:
从以上示例中可以看到,宿主机的根目录下新建了一个 web 目录,而该目录下没有任何文件。
下面在宿主机的 /web
目录下创建文件,并返回容器观察目录内容,示例代码如下:
从以上示例中可以看到,在宿主机的挂载目录下创建的文件也会在容器中出现。
下面在容器中创建文件,并返回宿主机观察目录内容,示例代码如下:
从以上示例中可以看出,Docker 数据卷能够实现 Docker 容器与宿主机间的数据共享,并且能够将容器中产生的数据永久保存下来,随时在宿主机查看与修改。
在生产环境中,容器服务的配置文件通常采用数据卷的方式挂载至容器内。为了防止容器内的误操作修改配置文件,在挂载时可以进行权限设置。这样就可以达到在宿主机修改代码,在容器内查看修改结果的目的。
下面创建容器并为数据卷设置权限,示例代码如下:
以上示例创建了一个名为 volume 的 Nginx 容器,并将挂载数据卷权限设置为 ro 只读。
下面查看数据卷是否挂载成功,示例代码如下:
从以上示例中可以看到,数据卷已经挂载成功,且宿主机挂载目录中没有任何文件。
下面为宿主机目录中添加文件,并返回容器中查看效果,示例代码如下:
从以上示例中可以看到,在容器挂载目录中可以查看文件内容。
下面在容器挂载目录下的文件中修改内容,示例代码如下:
以上示例中,在容器挂载目录中修改文件时发生报错,这是因为在容器中文件为只读类型,无法进行修改。
下面在容器挂载目录中创建新的文件,示例代码如下:
以上示例中,在容器挂载目录中创建文件时发生报错,这是因为在容器中挂载目录同样为只读类型,无法对其进行实质性的操作。
用户创建镜像时,通常会在 Dockerfile 文件中加上 VOLUME[/date] 来创建含有数据卷的镜像,并使用该镜像创建包含数据卷的容器。
Dockerfile 可以创建多个数据卷,与使用 docker run 命令创建数据卷不同,Dockerfile 中的数据卷不能映射到已经存在的本地目录。
在启动容器时,才会创建 Dockerfile 中指定的数据卷,并且以 Docker 中指定的名称命名。运行同样镜像的容器创建的数据卷是不一样的(可以看到不同容器的数据卷地址也是不一样的)。当容器中的数据卷地址不一样时,容器之间就无法共享数据了。
下面使用 Dockerfile 中的 VOLUME 选项来指定挂载数据卷,首先创建 Dockerfile 并添加内容,示例代码如下:
以上示例创建了一个 Dockerfile 文件并添加了内容,即挂载三个数据卷。
下面通过该 Dockerfile 创建新镜像,示例代码如下:
以上示例中,镜像已经构建完成,并且执行了挂载三个数据卷的命令。
运行容器时宿主机会随机生成挂载目录,无法保持目录地址一致,所以无法实现容器间的数据共享。
数据卷容器可以有效地解决这个问题:讲已命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器叫做数据卷容器。
数据卷容器挂载了一个宿主机目录,其他容器连接数据卷容器来实现数据的共享。如图所示
下面启动一个名为 volume-container 容器,此容器包含两个数据卷 /volume1
和 /volume2
(这两个数据卷目录是在容器中的,运行容器时会自动创建),示例代码如下:
上述示例中,容器 volume-container 已经创建完成,数据卷也挂载完成。
下面使用 Ctrl+P+Q 组合键退出当前容器终端,查看挂载信息并在宿主机中为数据卷添加文件,示例代码如下:
上述示例在宿主机中分别为两个数据卷添加文件及文件内容。
下面创建容器 test1-container 容器,用 --volumes-from
参数挂载 volume-container 中的数据卷,示例代码如下:
从以上示例中可以看出,两个容器实现了数据共享。
下面将初始数据卷容器删除,观察数据卷还能否正常工作,示例代码如下:
从以上示例中可以看到,即使删除了初始的数据卷容器 volume-container 或其他容器,只要有容器在使用该数据卷,里面的数据就不会丢失。
下面删除所有容器,再观察数据卷,示例代码如下:
以上示例中,即使删除了所有容器,数据卷也保留在宿主机中,这大大保证了数据的安全性。
企业中,业务数据不容有失,人们通常会对数据进行一次备份或多次备份,以保证数据的安全性。
下面创建一个名为 data-volume 的 CentOS 容器,准备对其挂载的两个数据卷/ var/volume1
和 /var/volume2
进行备份操作。示例代码如下:
以上示例中,容器已经创建完成,数据卷也成功挂载。
下面在容器挂载目录中创建文件并添加内容,示例代码如下:
以上示例分别在容器挂载目录 volume1 与 volume2 中创建了 a.txt 与 b.txt 两个文件,并在文件中添加了内容。
简而言之,备份数据卷就是使用 -volumes-from
参数来创建一个挂载数据卷的容器,从宿主机挂载要存放备份数据的目录到容器的备份目录,并备份数据卷中的数据。
完成后使用 --rm
参数删除容器,此时备份数据已经保存在当前的目录下。
示例代码如下:
以上示例成功将容器中 /var/volume1/a.txt
文件备份到宿主机中,并在备份之后将容器删除。
宿主机中 /root/backup/backup1.tar
文件就是备份数据,只是该文件经过了压缩。
下面以同样的方式对容器中 /var/volume2/b.txt
文件进行备份,示例代码如下:
此时,两份文件都已备份到宿主机中。
下面解压备份文件,并查看其目录结构,示例代码如下:
在以上示例中,仍可以看到备份之前的路径。
另外,也可以使用一条命令完成多个文件备份,示例代码如下:
以上示例中仅通过一条命令就成功备份了两份数据,通常在需要备份多份数据时将会使用此方式。