Docker 数据卷是一个可供容器使用的特殊目录,它绕过了容器的文件系统,直接将数据存储在宿主机上。

这样可以实现以下几个重要的目的:

  • 数据持久化:即使容器被删除或重新创建,数据卷中的数据仍然存在,不会丢失。

  • 数据共享:多个容器可以同时挂载同一个数据卷,实现数据的共享和交互。

  • 独立于容器生命周期:数据卷的生命周期独立于容器,不受容器的启动、停止和删除的影响。

一、数据卷简介

1.1 引言

docker的镜像是由多个只读的文件系统叠加在一起形成的。当我们在我启动一个容器的时候,docker会加载这些只读层并在这些只读层的上面(栈顶)增加一个读写层。这时如果修改正在运行的容器中已有的文件,那么这个文件将会从只读层复制到读写层。该文件的只读版本还在,只是被上面读写层的该文件的副本隐藏。当删除docker,或者重新启动时,之前的更改将会消失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。

为了很好的实现数据保存和数据共享,Docker提出了Volume这个概念,简单的说就是绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。又被称作数据卷。

1.2 什么是数据卷

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过UFS,可以提供很多有用的特性: - 数据卷可以在容器之间共享和重用 - 对数据卷的修改会立马生效 - 对数据卷的更新,不会影响镜像 - 数据卷默认会一直存在,即使容器被删除 Docker中提供了两种挂载方式,-v和-mount

1.3 为什么使用数据卷

Docker的镜像是由一系列的只读层组合而来,当启动一个容器的时候,Docker加载镜像的所有只读层,并在最上层加入一个读写层。这个设计使得Docker可以提高镜像构建、存储和分发的效率,节省了时间和存储空间,然而也存在如下问题。

  • 容器中的文件在宿主机上存在形式复杂,不能在宿主机上很方便的对容器中的文件进行访问

  • 多个容器之间的数据无法共享

  • 当删除容器时,容器产生的数据将丢失

为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在一个或多个容器中的特定文件或文件夹,这个目录能够独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久提供一下便利。

  • volume在容器创建时就初始化,在容器运行时就可以使用其中的文件

  • volume能在不同的容器之间共享和重用

  • 对volume中的数据的操作会马上生效

  • 对volume中数据操作不会影响到镜像本身

  • volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除

docker提供了两种卷:

  • bind mount

  • docker managed volume

二、bind mount 数据卷

  • 是将主机上的目录或文件mount到容器里。

  • 使用直观高效,易于理解。

  • 使用 -v 选项指定路径,格式 <host path>:<container path>

  • -v选项指定的路径,如果不存在,挂载时会自动创建

2.1 示例

[root@docker ~]# docker run  -it --rm \
-v /tmp/data1:/data1 \
-v /tmp/data1:/data2:ro \         # 指定/data2目录为只读属性
-v /etc/passwd:/data/passwd:ro \  # 指定/data/passwd文件为只读属性
busybox

/ # tail -n 3 /data/passwd
lee:x:1000:1000:lee:/home/lee:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
nginx:x:1001:1001::/home/nginx:/sbin/nologin
/ # touch  /data1/leefile1
/ # touch /data2/leefile1    # 尝试在/data目录创建文件,但是被拒绝
touch: /data2/leefile1: Read-only file system

三、docker manager 数据卷

  • bind mount必须指定host文件系统路径,限制了移植性

  • docker managed volume 不需要指定mount源,docker自动为容器创建数据卷目录

  • 默认创建的数据卷目录都在 /var/lib/docker/volumes 中

  • 如果挂载时指向容器内已有的目录,原有数据会被复制到volume中

3.1 示例

# 运行mysql容器
[root@docker volumes]# docker run  -d --name mysql -e  MYSQL_ROOT_PASSWORD='123' mysql:5.7

# 查看mysql容器的数据卷
[root@docker volumes]# ls -l /var/lib/docker/volumes
总用量 0
drwx-----x 3 root root 19  8月 20 16:34 ad74662b8d6bb6fdcc6e82925ae9942b94bac5f9da4bd52b0a14ac451ae9ef75

# 在数据卷下创建文件
[root@docker volumes]# touch ad74662b8d6bb6fdcc6e82925ae9942b94bac5f9da4bd52b0a14ac451ae9ef75/_data/leefile

# 进入容器查看文件是否存在
[root@docker volumes]# docker exec  -it mysql  bash
bash-4.2# cd /var/lib/mysql
bash-4.2# ls leefile
leefile

清理未使用的 Docker 数据卷的命令

[root@docker ~]# docker volume prune

注意:

1、在执行 docker volume prune 命令之前,请确保你确实不再需要这些数据卷中的数据,因为该操作是不可逆的,一旦删除数据将无法恢复。

2、如果有重要的数据存储在数据卷中,建议先进行备份,或者确保数据已经被妥善保存到其他地方。

3.2 自行建立个使用数据卷示例

3.2.1 场景 1:需要 Docker 自动管理持久化数据(推荐生产环境使用)

# 1. 创建命名卷(Docker 自动在 /var/lib/docker/volumes/ 下创建规范目录)
docker volume create mydata

# 2. 容器挂载(无需指定宿主机路径!)
docker run -v mydata:/var/lib/mysql mysql

优势:Docker 确保路径结构合规,支持 docker volume inspect 查看真实路径,避免手动操作风险。

3.2.2  场景 2:需要自定义宿主机路径(仅限开发/测试)

# 1. 在 **非 Docker 管理目录** 创建路径(如 /mnt/app-data)
mkdir -p /mnt/app-data

# 2. 容器挂载(标准 Bind Mount)
docker run -v /mnt/app-data:/var/lib/mysql mysql

优势:路径完全由你控制,不干扰 Docker 内部状态。

3.2.3 查看卷

[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     leevol1

四、数据卷容器(Data Volume Container)

数据卷容器(Data Volume Container)是 Docker 中一种特殊的容器,主要用于方便地在多个容器之间共享数据卷。

可以让两个运行的容器的数据保持完全一致

4.1 建立数据卷容器

[root@docker ~]# docker run  -d --name datavol \
-v /tmp/data1:/data1:rw \
-v /tmp/data2:/data2:ro \
-v /etc/resolv.conf:/etc/hosts busybox

4.2 使用数据卷容器

[root@docker ~]# docker run  -it --name test --rm --volumes-from datavol busybox
/ # ls
bin    data1  data2  dev    etc    home   lib    lib64  proc   root   sys    tmp    usr    var
/ # cat /etc/resolv.conf
# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.

nameserver 114.114.114.114
search timinglee.org

# Based on host file: '/etc/resolv.conf' (legacy)
# Overrides: []
/ # touch  data1/leefile1
/ # touch /data2/leefile1
touch: /data2/leefile1: Read-only file system
/ #

五、备份与迁移数据卷

5.1 备份数据卷示例

# 准备数据
# 运行容器
[root@docker-node1 ~]# docker run -d --name web -p 80:80 -v /data:/usr/share/nginx/html nginx:1.26 
550aac17773f95e3180cf79cb23f483ebc5e7effa859cff6a47b3192a5c80784

# 创建文件
# 进入容器内部
[root@docker-node1 ~]# docker exec -it web bash
root@550aac17773f:/# cd /usr/share/nginx/html/
root@550aac17773f:/usr/share/nginx/html# touch file{1..10}
root@550aac17773f:/usr/share/nginx/html# ls
file1  file10  file2  file3  file4  file5  file6  file7  file8	file9

# 数据备份
# 创建一个新容器
[root@docker-node1 ~]# docker run -it --rm --volumes-from web -v `pwd`:/backup busybox:latest 
# 备份
/ # tar zcf /backup/html.tar.gz /usr/share/nginx/html/

# 查看主机目录
[root@docker-node1 ~]# ls /root/html.tar.gz 
/root/html.tar.gz
# 可见备份成功

5.2 数据恢复示例

# 先删除数据
[root@docker-node1 ~]# rm -rf /data/*
[root@docker-node1 ~]# docker exec -it web bash
root@550aac17773f:/# ls /usr/share/nginx/html/

# 直接删除容器
[root@docker-node1 ~]# docker rm -f web 
web

# 重新运行一个容器
[root@docker-node1 ~]# docker run -d --name web -p 80:80 -v /data:/usr/share/nginx/html -v `pwd`:/backup nginx:1.26 
1923f2ceb0b4f670168b01a6459a83aa1c98e7a79eaab56e8abd5a4c4294c309

# 数据恢复
[root@docker-node1 ~]# docker exec  -it web bash
root@73002ccb070d:/# tar zxf /backup/html.tar.gz -C /
root@73002ccb070d:/# ls /usr/share/nginx/html/
file1  file10  file2  file3  file4  file5  file6  file7  file8	file9

六、bind mount 数据卷和docker managed 数据卷的对比

相同点:

  • 两者都是 host 文件系统中的某个路径

不同点

bind mountdocker managed volume
volume 位置可任意指定/var/lib/docker/volumes/...
对已有mount point的影响隐藏并替换为volume原有数据复制到volume
是否支持单个文件支持不支持,只能是目录
权限控制可设置为只读,默认为读写权限无控制,均为读写权限
移植性移植性弱,与host path绑定移植性强,无需指定host目录

更多推荐