刚接触 Docker 的人,十个有八个分不清「镜像」和「容器」:以为删了容器数据就没了,或者备份时只打包了镜像、结果数据全丢。一句话区别:镜像(image)是只读的模板,容器(container)是镜像跑起来的活实例。下面把两者关系、该备份谁、怎么迁移一次讲清,少踩几个数据丢失的坑。
镜像和容器到底差在哪
镜像像是「安装光盘 + 配置清单」,是一层层只读的文件系统叠加(基础系统层、依赖层、你的应用层),本身不运行、不变动。你 docker run 一个镜像,Docker 在它上面盖一层可读写的「容器层」,进程在这个层里读写——这就是容器。
| 维度 | 镜像 image | 容器 container |
|---|---|---|
| 是否运行 | 否,静态模板 | 是,活进程 |
| 能否改动 | 只读 | 可写(容器层) |
| 删了会怎样 | 只是少个模板,可重拉 | 没挂卷就丢数据 |
| 怎么造 | docker build / pull | docker run 镜像 |
该备份哪一个
结论先给:真正要备份的是「数据」,不是镜像也不是容器。镜像能从 Dockerfile 或仓库重拉,容器删了能重 run,但数据库里的订单、用户上传的文件——这些一旦没挂卷就彻底没了。
所以备份的正确姿势:
- 数据卷(volume):
docker volume ls看卷,用docker run --rm -v 卷名:/data -v $(pwd):/backup busybox tar czf /backup/卷名.tar.gz /data打包卷内容。 - bind mount 的目录:直接打包宿主机上的那个目录即可。
- 镜像:
docker save 镜像名 > img.tar导出,方便没网时恢复,但优先级低于数据。
踩坑实录
有次服务器要迁移,我图省事只 docker commit 把运行中的容器存成了镜像、docker save 导出,心想「连环境带数据一锅端」。结果到新机器 docker load 跑起来,MySQL 里空空如也——commit 只存了容器层的差量,而数据库数据写在挂载的 volume 里,根本没进镜像。最后靠旧机器上的 volume 备份才挽回。从此牢记:commit 不是备份,volume 才是。
怎么迁移到新机器
标准流程(数据优先):
# 1. 旧机器:导出数据卷
docker run --rm -v 我的数据卷:/data -v $(pwd):/b busybox \
tar czf /b/data.tar.gz /data
# 2. 旧机器:导出镜像(可选)
docker save 我的镜像 > img.tar
# 3. 新机器:载入镜像
docker load < img.tar
# 4. 新机器:建同名卷并恢复数据
docker run --rm -v 我的数据卷:/data -v $(pwd):/b busybox \
tar xzf /b/data.tar.gz -C /
# 5. 用同样的 compose 起容器,挂回该卷
如果一开始就用 docker compose 且数据都挂了命名卷,迁移基本就是「拷卷 + 拷 compose.yml + up -d」。
常见误区
| 误区 | 真相 |
|---|---|
| docker commit 当备份 | 只存容器层差量,漏掉卷数据 |
| 删容器数据就没了 | 挂了卷就不丢,删容器无所谓 |
| 镜像要天天备 | 能重拉/重 build,数据才要紧 |
常见问题
容器重启后数据没了?
大概率数据写在容器可写层没挂卷。用 docker inspect 容器名 --format '{{json .Mounts}}' 看挂载,挂了卷的目录重启不丢。更多排障见 Docker 容器一直重启或退出怎么办。
docker save 和 export 有啥区别?save 导镜像(含层历史),export 导容器文件系统(扁平、丢历史)。备份镜像用 save。
volume 和 bind mount 选哪个?
生产用命名 volume(Docker 管理、易备份、跨平台路径一致);想直接编辑宿主机文件才用 bind mount。
迁移后容器连不上数据库?
多半是卷没恢复或 compose 网络名变了。确认卷名一致、docker compose up -d 重建网络后再连。
你平时是怎么备份 Docker 数据的?欢迎到 fenij.com 分享你的脚本。