Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留
文章目录
- [Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留](#Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留)
-
- 一、为什么需要卷
- 二、卷到底在解决什么问题
- 三、最常见的写法
- 四、先用最小实验理解卷
- 五、这两个实验到底证明了什么
- 六、卷是独立于容器的吗
- 七、卷到底是不是宿主机上的一个文件夹
- 八、容器路径和宿主机路径不是同一个概念
- 九、挂载到底是什么意思
- 十、卷、路径和挂载关系
-
- [1. Named volume](#1. Named volume)
- [2. Bind mount](#2. Bind mount)
- 十一、如何查看和管理卷
- 十二、这一部分学完后应该掌握什么
- 十三、这一部分最值得记住的一句话
- 十四、下一步要学什么
本专栏文章导航
- 第 01 篇:Docker 入门学习笔记 01:它到底解决了什么问题,镜像和容器又是什么
- 第 02 篇:Docker 入门学习笔记 02:基础命令、前后台运行,以及 attach、logs、exec 的区别
- 第 03 篇:Docker 入门学习笔记 03:端口映射到底是什么,为什么容器启动了却访问不到
- 第 04 篇:Docker 入门学习笔记 04:环境变量到底在做什么,为什么很多容器都依赖它
- 第 05 篇:Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留
- 第 06 篇:Docker 入门学习笔记 06:用一个可复现的 Python 项目真正理解 Dockerfile
- 第 07 篇:Docker 入门学习笔记 07:用一个多服务案例真正理解 Docker Compose
这一篇进入 Docker 运行要素里的另一块核心内容:
卷
如果说环境变量解决的是:
配置怎么传进去
那么卷解决的就是:
数据怎么留下来
这一步非常关键,因为后面只要涉及:
- 数据库
- 上传目录
- 日志目录
- 缓存目录
- 应用持久化文件
几乎都会碰到卷。
一、为什么需要卷
先看一个最直接的问题:
为什么容器删掉以后,数据通常也会一起丢?
原因很简单:
- 容器本质上是一个运行实例
- 它可以被停止、删除、重新创建
- 如果数据只存在容器内部,就会和容器生命周期绑在一起
这样会带来一个明显问题:
- 程序删了可以重建
- 但数据通常不应该跟着一起消失
这也是为什么"容器适合运行程序,但不适合默认直接承载需要长期保存的数据"。
二、卷到底在解决什么问题
Docker 卷,英文是 Volume。
可以先把它理解成一句话:
卷的作用,是把需要持久化的数据从容器生命周期里分离出来。
也就是说:
- 程序仍然在容器里跑
- 但重要数据不再只依赖容器本身
- 即使容器被删除,只要卷还在,数据通常就还在
这是卷最核心的价值。
三、最常见的写法
Docker 里最常见的卷写法是:
bash
-v 卷名:容器内路径
例如:
bash
-v mydata:/data
这表示:
- 使用一个叫
mydata的卷 - 把它挂载到容器里的
/data
所以容器里访问 /data 时,实际上访问的是这块卷。
四、先用最小实验理解卷
理解卷最好的方式不是先背定义,而是先做一个最小实验。
第一步:在挂载目录里写入一个文件
执行:
bash
docker run --rm -v mydata:/data ubuntu bash -c 'echo hello-docker > /data/test.txt && cat /data/test.txt'
如果成功,通常会看到:
text
hello-docker
这说明:
- 容器成功在
/data/test.txt写入了内容 /data这个目录背后挂载的就是卷mydata
第二步:换一个全新的容器再去读这个文件
继续执行:
bash
docker run --rm -v mydata:/data ubuntu cat /data/test.txt
如果仍然看到:
text
hello-docker
那就证明了一件非常重要的事:
虽然前一个容器已经没了,但数据还在。
这就是卷存在的意义。
五、这两个实验到底证明了什么
第一个命令证明
bash
docker run --rm -v mydata:/data ubuntu bash -c 'echo hello-docker > /data/test.txt && cat /data/test.txt'
它证明:
- 容器可以往挂载目录里写数据
- 卷可以承载这些数据
第二个命令证明
bash
docker run --rm -v mydata:/data ubuntu cat /data/test.txt
它证明:
- 即使换了一个新的容器
- 只要挂载的是同一个卷
- 数据依然可以被读到
所以最值得记住的是:
卷保存的是数据,容器只是临时使用这份数据。
六、卷是独立于容器的吗
是的。
这正是卷最重要的特征。
例如前面的实验里:
- 第一个容器执行完就被
--rm删除了 - 但第二个容器仍然能读到同一个文件
这说明:
- 容器已经没了
- 卷还在
- 数据也还在
所以可以把这句话记住:
卷的生命周期不等于容器的生命周期。
七、卷到底是不是宿主机上的一个文件夹
从 Linux 下的实际落地形态看,可以近似理解成宿主机上的一个目录。
例如执行:
bash
docker volume inspect mydata
可以看到类似下面的输出:
json
[
{
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/mydata/_data",
"Name": "mydata",
"Scope": "local"
}
]
这里最值得关注的是:
text
/var/lib/docker/volumes/mydata/_data
这说明:
mydata这个卷最终确实落在宿主机文件系统上- Docker 默认把这块数据管理在自己的 volume 目录里
所以入门阶段可以先这样理解:
卷本质上是一块由 Docker 管理、通常落在宿主机上的持久化存储。
八、容器路径和宿主机路径不是同一个概念
例如:
bash
-v mydata:/data
这里的三部分分别是:
mydata:卷名/data:容器里的挂载点/var/lib/docker/volumes/mydata/_data:宿主机上的实际数据目录
其中:
/data是容器里的路径_data是 Docker 对 named volume 的内部目录命名方式
所以 _data 不是因为容器里写了 /data 才生成的。
也就是说,即使挂载点改成:
bash
-v mydata:/app/files
Docker 在宿主机上的 volume 数据目录通常仍然会是类似:
text
/var/lib/docker/volumes/mydata/_data
所以这两者不是命名映射关系,而是两个不同层面的路径。
九、挂载到底是什么意思
这里最关键的一点是:
挂载不是复制一份数据到容器里,而是把这块存储接到容器里的某个路径上。
例如:
bash
-v mydata:/data
这意味着:
- 容器访问
/data - 实际上就是在访问卷
mydata
所以挂载的核心不是"拷贝",而是:
让容器通过一个目录路径直接使用这块独立存储。
十、卷、路径和挂载关系
可以部分把卷理解成"共享空间",但更准确的说法是:
谁把卷挂载到自己的目录里,谁就能通过这个目录访问这块存储。
所以它不是"天然共享",而是:
- 卷本身独立存在
- 容器通过挂载使用它
- 多个容器也可以挂载同一个卷
最稳妥的理解方式是:
一条挂载声明 = 一个卷 -> 一个容器路径
例如:
bash
-v mydata:/data
这表示:
- 卷:
mydata - 挂载点:
/data
这是一条一对一的挂载关系。
在实际使用里,最常见的几种情况是:
- 一个卷挂到一个路径:
-v mydata:/data - 一个容器里多个路径挂多个卷:
-v appdata:/data -v applogs:/logs - 多个容器挂同一个卷:多个容器都使用
-v mydata:/data
而"一个路径同时对应多个卷"不是常规理解方式,在一个容器里,同一个挂载点最终只能对应一个生效的挂载结果。
另外,挂载路径并不是固定的。Docker 不要求只能挂载少数特殊目录,关键是要挂到程序真正会读写数据的路径上。例如:
bash
-v mydata:/data
-v mylogs:/app/logs
-v pgdata:/var/lib/postgresql/data
所以最该记住的是:
- 常规模型:一个卷 -> 一个挂载路径
- 常见扩展:多个路径 -> 多个卷
- 常见共享:多个容器 -> 同一个卷
这里还需要顺手区分两种非常常见的挂载方式:
1. Named volume
例如:
bash
-v mydata:/data
这里左边的 mydata 是卷名,不是宿主机真实路径。
这时 Docker 会自己管理这块存储,数据通常会落到类似:
text
/var/lib/docker/volumes/mydata/_data
2. Bind mount
例如:
bash
-v "$(pwd)/output:/app/output"
这里左边的 $(pwd)/output 是宿主机真实路径。
这时 Docker 不会再把数据放到 /var/lib/docker/volumes/.../_data,而是直接把宿主机这个目录挂到容器里的 /app/output。
所以最简单的区分方式是:
- 左边是卷名:Docker 自己管理,通常落到
.../_data - 左边是宿主机路径:数据就直接写到那个宿主机目录
十一、如何查看和管理卷
查看卷时,最常用的是这三个命令:
bash
docker volume ls
docker volume inspect mydata
docker inspect <container_name>
它们分别用来做什么:
docker volume ls:看所有卷docker volume inspect:看某个卷的详细信息,例如驱动、卷名、宿主机存储位置docker inspect <container_name>:看某个容器挂了哪些卷,重点关注Mounts
例如:
text
DRIVER VOLUME NAME
local mydata
对于多项目场景,一个项目里通常不止一个卷,例如:
- 数据库数据卷
- 日志卷
- 上传文件卷
- 缓存卷
所以更合理的方式通常是:
不同职责的数据,用不同的卷。
例如:
shop_mysql_datashop_app_logsshop_uploads
卷一多确实会乱,所以命名最好从一开始就带上项目语义和用途语义。比较推荐:
项目名 + 用途项目名 + 服务名 + 用途
例如:
blog_mysql_datablog_uploadscrm_postgres_datacrm_app_logs
实际工作里,一般也不靠直接查看 /var/lib/docker/volumes/ 来管理卷。因为那个目录只告诉你底层有这些数据目录,但不直接告诉你:
- 这个卷属于哪个项目
- 哪个容器在使用它
- 它是做什么的
更常见的做法是:
- 用
docker volume ls看全局卷列表 - 用
docker volume inspect看卷详情 - 用
docker inspect 容器名看容器和卷的挂载关系 - 用统一命名规则减少混乱
如果项目再复杂一些,更推荐把卷交给 Docker Compose 统一管理。
十二、这一部分学完后应该掌握什么
如果这一部分真正掌握了,应该能清楚表达这些内容:
- 卷用于让数据独立于容器生命周期存在
-v 卷名:容器路径表示把卷挂载到容器目录- 挂载的本质不是复制,而是直接使用这块独立存储
- 即使容器被删除,只要卷还在,数据通常就还在
- 容器里的挂载路径和宿主机上的实际存储路径不是同一个概念
- 常规理解是一条挂载声明对应一个卷和一个容器路径
- 一个项目里通常会有多个卷,分别负责不同类型的数据
docker volume ls可以查看所有卷docker volume inspect可以查看卷的详细信息docker inspect可以查看容器和卷的挂载关系
十三、这一部分最值得记住的一句话
如果只记一句话,最值得记住的是:
卷不是容器内部自带的数据目录,而是被 Docker 挂进容器里的独立持久化存储。
十四、下一步要学什么
卷理解清楚之后,下一步最自然的延伸就是:
Dockerfile
因为前面的内容主要在讲"怎么运行一个现成镜像",
而后面就要开始进入:
怎么把自己的服务做成镜像。
本专栏文章导航
- 第 01 篇:Docker 入门学习笔记 01:它到底解决了什么问题,镜像和容器又是什么
- 第 02 篇:Docker 入门学习笔记 02:基础命令、前后台运行,以及 attach、logs、exec 的区别
- 第 03 篇:Docker 入门学习笔记 03:端口映射到底是什么,为什么容器启动了却访问不到
- 第 04 篇:Docker 入门学习笔记 04:环境变量到底在做什么,为什么很多容器都依赖它
- 第 05 篇:Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留
- 第 06 篇:Docker 入门学习笔记 06:用一个可复现的 Python 项目真正理解 Dockerfile
- 第 07 篇:Docker 入门学习笔记 07:用一个多服务案例真正理解 Docker Compose