Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留

Docker 入门学习笔记 05:卷到底是什么,为什么容器删了数据却还能保留

文章目录

本专栏文章导航

这一篇进入 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_data
  • shop_app_logs
  • shop_uploads

卷一多确实会乱,所以命名最好从一开始就带上项目语义和用途语义。比较推荐:

  • 项目名 + 用途
  • 项目名 + 服务名 + 用途

例如:

  • blog_mysql_data
  • blog_uploads
  • crm_postgres_data
  • crm_app_logs

实际工作里,一般也不靠直接查看 /var/lib/docker/volumes/ 来管理卷。因为那个目录只告诉你底层有这些数据目录,但不直接告诉你:

  • 这个卷属于哪个项目
  • 哪个容器在使用它
  • 它是做什么的

更常见的做法是:

  1. docker volume ls 看全局卷列表
  2. docker volume inspect 看卷详情
  3. docker inspect 容器名 看容器和卷的挂载关系
  4. 用统一命名规则减少混乱

如果项目再复杂一些,更推荐把卷交给 Docker Compose 统一管理。

十二、这一部分学完后应该掌握什么

如果这一部分真正掌握了,应该能清楚表达这些内容:

  • 卷用于让数据独立于容器生命周期存在
  • -v 卷名:容器路径 表示把卷挂载到容器目录
  • 挂载的本质不是复制,而是直接使用这块独立存储
  • 即使容器被删除,只要卷还在,数据通常就还在
  • 容器里的挂载路径和宿主机上的实际存储路径不是同一个概念
  • 常规理解是一条挂载声明对应一个卷和一个容器路径
  • 一个项目里通常会有多个卷,分别负责不同类型的数据
  • docker volume ls 可以查看所有卷
  • docker volume inspect 可以查看卷的详细信息
  • docker inspect 可以查看容器和卷的挂载关系

十三、这一部分最值得记住的一句话

如果只记一句话,最值得记住的是:

卷不是容器内部自带的数据目录,而是被 Docker 挂进容器里的独立持久化存储。

十四、下一步要学什么

卷理解清楚之后,下一步最自然的延伸就是:

Dockerfile

因为前面的内容主要在讲"怎么运行一个现成镜像",

而后面就要开始进入:

怎么把自己的服务做成镜像。

本专栏文章导航

相关推荐
chools2 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第四期】(Tool + MCP)
java·人工智能·学习·spring
世人万千丶2 小时前
Flutter 框架跨平台鸿蒙开发 - 数独游戏应用开发文档
学习·flutter·游戏·华为·harmonyos·鸿蒙
xuhaoyu_cpp_java2 小时前
Maven学习(一)
java·经验分享·笔记·学习·maven
恼书:-(空寄2 小时前
Docker 进阶核心实战:自定义镜像 + Dockerfile + Docker Compose
docker·容器·docker compose
AI_零食3 小时前
开源鸿蒙跨平台Flutter开发:研究生科研贡献雷达矩阵架构
学习·flutter·ui·华为·矩阵·开源·harmonyos
●VON3 小时前
本地大模型部署实录:从Docker环境搭建到Open WebUI公网访问
运维·docker·容器·大模型
迷路爸爸1803 小时前
Docker 入门学习笔记 03:端口映射到底是什么,为什么容器启动了却访问不到
笔记·学习·docker
xinzheng新政3 小时前
Javascript·深入学习基础知识
前端·javascript·学习
chushiyunen3 小时前
textCnn笔记
笔记