Docker记录:误删宿主机挂载目录导致 PostgreSQL 数据丢失
- 1. Docker启动PostgreSQL服务
- 2. 我删了什么?
- 3. Ooops!问题来了
- 4.问题核心:为什么删掉宿主机目录后,容器数据就访问不了?
- 5.总结
最近在使用 Docker 部署 PostgreSQL 时踩了一个坑:因为对 -v 参数理解不够深入,误删了宿主机上的挂载目录,结果容器内的数据“凭空消失”了。
这个过程中,还存在其他操作错误,这篇博客记录一下这个问题的来龙去脉,希望能帮助其他朋友避开同样的坑。
1. Docker启动PostgreSQL服务
我的项目分为一期和二期,代码文件夹名暂且叫做period1和period2。一期开发的时候,我用docker启动postgreSQL服务。
代码目录在/home/root/period1/
sudodockerrun-d\--namedb_ai\-ePOSTGRES_PASSWORD=123456\-ePOSTGRES_DB=db1\-ePOSTGRES_USER=postgre_db\-v/home/root/period1/postgres_data:/var/lib/postgresql\-p5432:5432\postgres:latest命令本身没问题,服务也正常启动。我在这个postgres docker容器中,建立了一期的数据表。
二期开发的时候,我建了period2文件夹来存二期的代码,使其不影响一期服务。代码目录在/home/root/period2。并且,我在docker中另建了一张二期的数据表,将数据在软件层面隔离。并且认为,只要我不删这个docker容器,数据就会妥妥的一直在里面。
2. 我删了什么?
二期顺利上线了,该清清一期的旧数据了。我做了两件事:
- 我进入postgres docker容器,将Period1 data表删除;
- 我将一期的代码删除,代码目录在/home/root/period1/;
3. Ooops!问题来了
我用rm -rf删掉了一期整个代码目录:/home/root/period1
然后悲剧发生了:二期的代码服务中,postgres数据库无法正常使用,查postgres docker状态已经自动退出。
我想着,退出就退出吧,重启一下服务就行了。结果发现重启后,之前没删的二期数据库、表都不存在了!
回到上一节,我删了什么?第1删表不会有问题,那问题就出现在2删了代码目录。我使用的docker启动postgreSQL服务的命令中,-v /宿主机路径:/var/lib/postgresql实际上是将docker容器内路径映射到宿主机路径中,数据实际存储在宿主机目录/home/root/period1/postgres_data中,二期使用的同一个docker,相当于二期的数据也在这个目录中。当我执行rm -rf删除/home/root/period1等于直接删了数据库的所有物理文件(表数据、索引、事务日志等)。当我重启容器后,容器发现找不到/home/root/period1/postgres_data,会自动执行initdb初始化一个新的空白数据库实例。
赶紧尝试恢复文件!时间一分一秒过去,要知道,当我删了这个目录,相当于计算机中的一块内存被释放,那么其他服务、日志在磁盘的写入都有可能会覆盖这块内存,就无力回天了。AI告诉我:赶紧卸载这块分区,阻止任何新的写入操作,避免已删除的数据块被覆盖!
但问题又出现了,卸载分区失败。WHY?/home/root/并不是独立的数据分区,而是系统根分区!
意味着,系统本身在不断写入日志、临时文件等,导致卸载失败。如果我在这个分区上直接强行运行恢复工具,可能导致系统崩溃。遂放弃恢复,只能重传数据。
4.问题核心:为什么删掉宿主机目录后,容器数据就访问不了?
这又要讲到docker的挂载方式,我使用的是Bind Mount挂载方式,容器不保存数据的副本!
类比:把一个U盘(宿主机目录)插在电脑上(docker容器)。电脑里的文件(数据库数据)实际存储在U盘上,我把U盘拔掉并格式化(rm -rf),电脑里的“文件快捷方式”还在,但指向的物理储存已经没了,所以电脑打不开这些文件。如果我当时用Docker volume或容器内部存储,情况都会不一样。
Docker volume挂载示例:
# 创建 volumesudodockervolume create postgres_data# 使用 volumesudodockerrun-d\--namepostgres_ai\-ePOSTGRES_PASSWORD=123456\-ePOSTGRES_DB=ai\-vpostgres_data:/var/lib/postgresql/data\-p5432:5432\postgres:latestDocker内部挂载示例:
sudodockerrun-d\--namepostgres_ai\-ePOSTGRES_PASSWORD=123456\-ePOSTGRES_DB=ai\-p5432:5432\postgres:latest但Docker内部挂载的话,如果把容器删了数据也会丢,不推荐。
5.总结
- Bind Mount是共享而非复制,删了宿主机目录,容器虽然活着,但数据没了。
- 数据相关的文件不要放在项目代码中,并需要设独立分区。这样即使误删了,也有恢复的可能性。
- 服务器上每个项目相关的文件夹都要备份!确保备份数据和服务器上数据一致。