Docker 外挂的数据库文件丢失之迷分析
线上服务器的数据库(MySQL、MongoDB、Redis)全部运行在 Docker 中,数据库文件是通过卷挂载到宿主机 /data 目录中实现持久存储的,配置如下:
mongo: image: mongo container_name: mongo restart: always logging: *default-logging ports: - 127.0.0.1:27017:27017 volumes: - /data/mongo:/data redis: image: redis container_name: redis restart: always logging: *default-logging ports: - 127.0.0.1:6379:6379 volumes: - /data/redis:/data
今天弄项目环境时,一个误操作不小心让 MongoDB 和 Redis 的数据库文件消失了(/data/mongo 和 /data/redis 俩目录突然为空),吓得我以为生产环境中的数据库被我删了。
赶紧刷新了下项目系统页面,却还能正常显示数据。于是出现了很奇怪的一幕,执行下面命令看到在容器里有数据文件:
sudo docker exec -it mongo ls /data sudo docker exec -it redis ls /data
但宿主机上 /data/mongo 和 /data/redis 目录却是空的。
回想发现问题前的最后一次操作是用 docker-compose 新启了个 MongoDB 和 Redis 的容器,检查了下 docker-compose.yaml,原来是把数据挂载到了 /data 目录,原因显而易见了,新的容器卷把 /data/mongo 和 /data/redis 给覆盖掉了,思考下,所谓的覆盖就是把这俩目录分配了新的 inode 号并指到新的数据块,而原来运行中的 Redis 和 MongoDB 容器之所以还能看到数据,是因为旧的数据块暂未释放,它们仍读写的旧的数据块。
我用运行的 ElasticSearch 容器来验证这个猜想:
$ sudo docker exec elasticsearch ls -i /usr/share/elasticsearch/data 44564482 nodes $ ls -i /data/elasticsearch/ 44564482 nodes
可以看到宿主机和容器里,数据文件都是同一个 inode 号;再看下 MongoDB 的:
$ sudo docker exec mongo ls -i /data/ 813490 configdb 813492 db $ sudo ls -i /data/mongo/ 116129795 configdb 116129794 db
可见 inode 号不一致,所以我猜想是对的。这种情况,应当在数据还能读时,立即将给导出来做好备份,一旦重启 Docker 什么的导致资源释放,数据就没了。