04入门:数据持久化

2509 字
13 分钟
04入门:数据持久化

第 4 关 数据持久化#

本关的产出:三种「把数据放在容器外面」的方式,一套选择它们的判据,以及把一份数据搬走再搬回来的能力。

4.1 数据为什么会丢,以及三种不丢的思路#

第 1 关埋过一句话:容器删掉之后,写进它自己文件系统里的改动一并消失。第 2 关把原因讲清楚了——容器没有自己的磁盘,它看到的那个根目录是拼出来的,只有自己改过的那一点点是独占的,一删就没。

对「跑一次就完」的东西来说这没问题。但对持有数据的东西——数据库、上传的文件、日志——这是致命的。

所以这一关只解决一件事:让数据住在容器外面。

「外面」有三种,这就是本关的骨架:

  1. 卷:数据放在容器外面一块 Docker 自己管的地方。位置不用你操心,用名字指它。
  2. 绑定挂载:数据放在容器外面一个你自己指定的目录里。位置你说了算。
  3. tmpfs:数据存在内存里,不落磁盘。容器一停就没了——它是专门用来「让东西不要留下来」的。

第 2 关用的一直是第二种(-v ~/docker/mysql-data:/var/lib/mysql)。当时刻意只讲「把外面的东西接进来」这一个动作,现在把三种一次讲清。

4.2 卷:Docker 自己管的那块地方#

先建一个:

docker volume create demo-vol
docker volume ls

docker volume ls 会把所有卷列出来。看到一长串随机十六进制字符的名字不要慌,那是匿名卷,4.4 讲。

想知道它到底在哪儿:

docker volume inspect demo-vol

输出很短,里面有一项是:

"Mountpoint": "/var/lib/docker/volumes/demo-vol/_data"

这里要回扣第 2 关。 /var/lib/docker 是那台 Linux 虚拟机的文件系统里 Docker 的地盘。所以卷的位置在虚拟机内部——它不是宿主机上的一个目录。这正是它和绑定挂载(4.3)的根本差别。这一点往下会反复用到。

然后用它:

docker run --rm -v demo-vol:/data nginx:latest sh -c 'echo 你好,我住在卷里 > /data/hello.txt'

注意 -v 的第一段:demo-vol。 它不以 / 开头、也不以 ./ 开头,按第 2 关那条规则,它会被当成卷的名字。第 2 关的一条报错里写着「那是另一套东西,第 4 关讲」——就是现在这个。

换个容器读一遍(上一个容器已经 --rm 掉了):

docker run --rm -v demo-vol:/data nginx:latest cat /data/hello.txt

要观察:第二个容器和第一个没有任何关系,可它读到了第一个写进去的东西。数据的寿命跟容器解绑了。

再走一步——把容器全删光,卷还在:

docker volume ls | grep demo-vol

这就是这一关要的东西:容器是消耗品,卷不是。

三条延伸事实,都有用:

  1. 一个卷可以同时挂给多个容器。 数据是共享的,不是每个容器各一份。
  2. 卷在用的时候删不掉。 docker volume rm demo-vol 会报 volume is in use,报错里还带着用它的那个容器 ID。
  3. 删容器不会碰到卷。 docker rm 不碰它,docker system prune 默认也不碰(那个命令只管容器、网络和悬空镜像)。

4.3 绑定挂载:你自己指定的那个目录#

就是第 2 关一直在用的那个:

docker run --rm -v ~/docker/mysite:/data nginx:latest sh -c 'echo 我在你自己的目录里 > /data/note.txt'

~/docker/mysite 是个普通目录,以 / 或 ~ 开头,所以被当成宿主机上的路径。

和卷的差别,一张表说清:

卷绑定挂载
位置谁定Docker你
住在哪虚拟机的磁盘上、Docker 的地盘里你指定的那个路径
长什么样一个名字(demo-vol)一个路径(~/docker/mysite)
能直接用编辑器改吗不能,它在虚拟机里面能,它就是个普通目录
备份得开一个临时容器去打包(4.6)直接拷目录就行
换台机器还认得吗认得,名字是逻辑的不认得,那是个本机路径

怎么选:

  1. 开发的时候用绑定挂载:改宿主机上的文件,容器里立刻能看到;嫌它会乱改,加 :ro 只读(第 2 关讲过)。
  2. 要交付、要让别人跑出一样的结果,用卷:名字不绑死在某台机器的某个路径上。
  3. 数据库这类由程序自己管文件布局的东西,用卷更省心——那些文件长什么样你根本不用操心。

易踩的坑:

  • 绑定挂载的路径必须是绝对路径,或者 ./、../ 开头。写 mysite:/data 会被当成卷名,东西悄悄跑进一个卷里,你以为在改本地文件却什么也没发生。
  • macOS 上绑定挂载比卷慢一截:宿主机和容器之间隔着一层,读写都要穿过去。卷在虚拟机内部,不跨那一层。对慢这件事敏感的东西,优先用卷。

4.4 匿名卷:只写一个路径的时候#

-v 只写一边会怎样:

docker run -d --name anon-demo -v /data nginx:latest

没写宿主那半边。Docker 的动作是:替你新建一个卷,名字是一长串随机的十六进制,然后挂上去。

docker volume ls

demo-vol 旁边多出来一个看不出是什么的名字,就是它。这种卷叫匿名卷——它有名字,只是名字没有含义。

它最容易留下的后果:容器删了,它不走。我实测的时候起了一个用匿名卷的容器,docker rm -f 之后,docker volume ls 里就多出那么一个——一次疏忽就攒一个。攒多了就是一堆看不出用途、也不敢删的东西。

匿名卷还有一个来源:有些镜像在构建的时候就声明了某个目录是卷。那样即使你在 docker run 里一个 -v 都不写,也会自动生成一个匿名卷。

清理:

docker volume prune

它只删没被任何容器用的卷。

易踩的坑:

  • docker volume prune 不问你,直接把没在用的卷全删掉。跑之前先 docker volume ls 看一眼,确认里面没有你还要的东西。
  • docker system prune 默认不碰卷,要连卷一起清得显式加 --volumes。这两个命令的默认行为不一样,容易记混。

4.5 tmpfs:只要内存,不要磁盘#

第三种跟前两种用途完全不同:东西不要落到磁盘上。

docker run -d --name tmp-demo --tmpfs /scratch nginx:latest

--tmpfs /scratch 把一块内存挂到容器里的 /scratch。往里写的东西待在内存里,容器一停就没了。

什么时候要它:

  1. 临时文件:程序跑的时候需要一个地方写中间结果,跑完就该消失,落盘反而还要清理。
  2. 不该留在磁盘上的东西:口令、令牌、解密后的私钥。落到磁盘上就可能被人从磁盘上捞走;只在内存里,容器一停就散。

要观察:docker inspect tmp-demo 里翻 Mounts,你会发现 /scratch 不在里面。它在另一处:

"Tmpfs": {
"/scratch": ""
},

翻了一遍 Mounts 没找到、就以为没挂上——这是个很容易踩空的地方。

三种摆在一起:

数据住哪容器删了主要用途
卷虚拟机的磁盘,Docker 管还在要长期留住的数据
绑定挂载你指定的目录还在开发时改文件、要能直接看到
tmpfs内存没了临时文件、不该落盘的东西

4.6 备份与迁移#

先把卷的代价说清楚:它在虚拟机里,你没法用文件管理器打开它。 这是它换来「位置不用你操心」的代价。

所以备份得开一个临时容器去做——让一个容器同时看到「要备份的卷」和「宿主机上的一个目录」,在里面打包:

docker run --rm \
-v demo-vol:/data:ro \
-v /tmp/backup:/backup \
nginx:latest tar czf /backup/data.tar.gz -C /data .

拆开看:

  1. -v demo-vol:/data:ro:把要备份的卷挂进来,:ro 只读——备份不该改原数据。
  2. -v /tmp/backup:/backup:把宿主机上一个目录挂进来,备份文件要落到这里。少了这半边,包会跟着临时容器一起消失。
  3. tar czf ... -C /data .:进到 /data 再打包。用 -C 是为了让包里的路径是相对的,恢复时不会带上一串绝对路径。最后那个 . 就是「当前目录」。

要观察:这条命令里没有在跑任何服务——它就是拿容器当了一个一次性的命令行环境。手头没有某个工具时,用一个装了它的镜像跑一次就完了,不用在自己机器上装。

恢复是反过来,解到一个新卷里:

docker volume create restore-vol
docker run --rm \
-v restore-vol:/data \
-v /tmp/backup:/backup \
nginx:latest tar xzf /backup/data.tar.gz -C /data

要观察:恢复出来的是一份完全独立的副本。实测里把原卷里的文件删掉之后,新卷里那份还在。

迁移就是「备份 + 在新机器上恢复」:把 data.tar.gz 拷到新机器,在新机器上建卷、解包。这也是为什么卷比绑定挂载更适合交付——绑定挂载换台机器就不认了(4.3 那张表)。

易踩的坑:

  • 备份那条命令里两个 -v 少一个都不行:少了卷那半边没东西可备,少了宿主机那半边包会跟着容器一起消失。
  • 忘了 -C /data,打进去的就是容器里的路径结构,恢复出来位置全不对。
  • 绑定挂载不需要这一套:它本来就在你的磁盘上,直接拷目录。

这一关的产出:三种挂载的取舍判断、一个自己的卷、以及把数据搬走再搬回来的能力。下一关换个方向——这些容器现在各跑各的、互相看不见(第 2 关「三连」留的钩子),要让它们互相说话。

本关命令速查#

卷与各类挂载,见 命令速查表 · 卷与数据。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
04入门:数据持久化
https://hecloud.top/posts/tech/docker/04数据持久化/
作者
贺小云
发布于
2026-09-20
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
贺小云
热爱技术与折腾的博客 Dancing with uncertainty. 与不确定性共舞
公告
出于成本考虑,贺云已全面转型Serverless架构
分类
标签
碎碎念
站点统计
文章
91
分类
5
标签
44
总字数
103,192
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0