Docker 数据卷

发布于 2022-07-16 18:22:50 更新于 2022-07-17 14:43:00

一、什么是数据卷?

简单来说,数据卷是一个可供一个或多个容器使用的特殊目录,用于持久化数据以及共享容器间的数据,它以正常的文件或目录的形式存在于宿主机上。 另外,其生命周期独立于容器的生命周期,即当你删除容器时,数据卷并不会被删除。

二、为什么需要数据卷?

Docker 镜像由多个文件系统(只读层)叠加而成。Docker 会加载只读镜像层,并在镜像栈顶部添加一个读写层。当运行容器后,如果修改了某个已存在的文件,那么该文件将会从下面的只读层复制到上面的读写层,同时,该文件在只读层中仍然存在。当我们删除 Docker 容器,并通过镜像重新启动容器时,之前的更改的文件将会丢失。

那么,要如何解决数据持久化的问题呢?于是,Docker 提出了 Volume 数据卷的概念。

三、数据卷特性

  • 数据卷可以在容器之间共享和重用;
  • 对数据卷的修改会立刻生效;
  • 更新数据卷不会影响镜像;
  • 数据卷默认一直存在,即使容器被删除;

四、挂载数据卷

Docker 提供了 3 种不同的方式将数据从宿主机挂载到容器中。

Docker 挂载数据卷

4.1 volume (最常用的方式)

volume : Docker 管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录下, 也是最常用的方式。

Docker 查看本地数据卷

看上图,所有的 Docker 容器数据都保存在 /var/lib/docker/volumes 目录下。若容器运行时未指定数据卷, Docker 创建容器时会使用默认的匿名卷(名称为一堆很长的 ID)。

注意:Mac 系统中, Docker 是基于虚拟机的 ,必须登录到虚拟机里面,登录后在目录 /var/lib/docker/volumes/ 下即可找到数据卷。

4.2 bind mount(比较常用的方式

bind mount: 意为可以存储在宿主机中的任意位置。需要注意的是,bind mount 在不同的宿主机系统时不可移植的,比如 Windows 和 Linux 的目录结构是不一样的,bind mount 所指向的 host 目录也不一样。这也是为什么 bind mount 不能出现在 Dockerfile 中的原因所在,因为这样 Dockerfile 就不可移植了。

4.3 tmpfs mount (一般不用这种方式)

tmpfs mount : 挂载存储在宿主机的内存中,而不会写入宿主机的文件系统,一般不用此种方式。

五、Volume 使用

5.1 创建一个数据卷

执行如下命令创建数据卷:

docker volume create test-vol

Docker 创建数据卷

5.2 查看所有的数据卷

docker volume ls

Docker 查看当前已创建的数据卷

5.3 查看数据卷信息

执行如下命令,可以查看指定的数据卷信息:

# 查看数据卷名为 test-vol 的信息
docker volume inspect test-vol

Docker 查看数据卷信息

5.4 运行容器时挂载数据卷

数据卷 test-vol创建成功后,我们运行一个 Nginx 容器,并尝试挂载该数据卷,挂载命令支持两种:

  1. -v
docker run -d -it --name=test-nginx -p 8011:80 -v test-vol:/usr/share/nginx/html nginx:1.13.12

参数说明:

  • -d : 后台运行容器;
  • --name=test-nginx : 指定容器名为 test-nginx;
  • -p 8011:80 : 将容器的 80 端口挂载到宿主机的 8011 端口;
  • -v test-vol:/usr/share/nginx/html : 将 test-vol 数据卷挂载到容器中的 /usr/share/nginx/html 目录上;
  1. --mount
docker run -d -it --name=test-nginx -p 8011:80 --mount source=test-vol,target=/usr/share/nginx/html nginx:1.13.12

参数说明:

  • --mount source=test-vol,target=/usr/share/nginx/html : 将 test-vol 数据卷挂载到容器中的 /usr/share/nginx/html 目录上;

-v--mount 有什么区别?

都是挂载命令,使用 -v 挂载时,如果宿主机上没有指定文件不会报错,会自动创建指定文件;当使用 --mount时,如果宿主机中没有这个文件会报错找不到指定文件,不会自动创建指定文件。


容器运行成功后,进入到 /var/lib/docker/volumes 目录下,验证数据是否挂载成功:

验证数据卷是否挂载成功

可以看到已经有了 50x.htmlindex.html 两个 Nginx 页面相关数据,说明数据卷挂载成功了。挂载成功后,我们不论是修改 /var/lib/docker/volumes 下的数据,还是进入到容器中修改 /usr/share/nginx/html 下的数据,都会同步修改对应的挂载目录,类似前端开发中双向绑定的作用。

下面,我们停止并删除刚刚运行的 Nginx 容器, 看看数据卷中的数据是否会跟着被删除:

删除容器,验证数据卷是否还存在

可以发现数据卷相关数据都还在,表明数据卷的生命周期独立于容器。另外,若下次再创建 Nginx 容器,还可以复用这个数据卷,复用性以及扩张性都非常不错。

删除数据卷

由于数据卷的生命期独立于容器,想要删除数据卷,就需要我们手动来操作, 执行命令如下:

docker volume rm test-vol
  1. 如果你需要在删除容器的同时移除数据卷,请使用 docker rm -v 命令。

  2. 对于那些没有被使用的数据卷,可能会占用较多的磁盘空间,你可以通过如下命令统一删除:

docker volume prune

bind mount 使用

通过 bind mount 模式可以挂载到宿主机的任意位置,示例如下:

docker run -d -it --name=test-nginx -p 8011:80 -v /docker/nginx1:/usr/share/nginx/html nginx:1.13.12

参数说明:

  • -v /docker/nginx1:/usr/share/nginx/html : 将宿主机中的 /docker/nginx1 目录挂载到容器中的 /usr/share/nginx/html 目录;

容器运行成功后,进入容器中:

docker exec -it test-nginx /bin/bash

docker 进入容器

从上图可以看到,与 volume 不同,bind mount 这种方式会隐藏目录中的内容(非空情况下),这里的 /usr/share/nginx/html 目录下的 html 文件被隐藏了,所以我们看不到。

但是,我们可以将宿主机中该目录中的文件立刻挂载到容器中,下面验证一下:

  1. 新建一个 index.html:

创建 index.html 文件

  1. 再次进入容器,查看挂载目录内容:

进入 docker 容器