Docker 镜像导出与导入:save/load 与 export/import 深度对比
在 Docker 离线环境、镜像迁移和容器快照场景中,镜像的导出与导入是核心操作。Docker 提供了两套语义完全不同的工具:save/load 和 export/import。理解它们的原理与差异,是避免镜像数据丢失、层膨胀以及元数据混乱的关键。
一、save/load:镜像的完整保存与恢复
1.1 操作对象与原理
- 操作对象 :Docker 镜像(Image)。
- save:将一个或多个镜像的完整层数据、元数据(标签、环境变量、CMD 等)序列化为一个 tar 归档文件。该归档包含每个层的 contents、JSON 描述以及 manifest.json。
- load :从 save 生成的 tar 文件中读取并重新创建镜像,包括所有历史层和标签。
1.2 特性
- 保留镜像的所有层(Layers),恢复后层结构不变,有利于缓存复用。
- 保留所有元数据(标签、历史、入口点等)。
- 可以将多个镜像保存到一个 tar(通过
docker save image1 image2),load 时会全部导入。 - 产生的 tar 文件通常较大,因为包含完整的分层和重复文件(层间可能重复)。
1.3 典型流程
docker save
传输到离线服务器
docker load
本地镜像
my-java-app:1.0
my-java-app.tar
含所有层+元数据
my-java-app.tar
离线环境镜像
my-java-app:1.0
层结构相同
使用场景:
- 完整的镜像迁移(包含历史、标签),例如将 CI 构建的镜像传递到无网络的部署环境。
- 需要保留镜像的分层结构以利用构建缓存。
- 备份或归档特定版本镜像。
二、export/import:容器文件系统的快照
2.1 操作对象与原理
- 操作对象 :Docker 容器(Container)。
- export:将容器的文件系统(当前状态,不包含卷)导出为一个 tar 归档。它是一个扁平的、非分层的文件系统快照,不记录任何 Docker 镜像元数据(如历史、环境变量、CMD 等)。
- import :从一个 export 的 tar 文件(或 URL)创建一个新的 Docker 镜像。这个镜像只有单一层,没有历史记录,标签需重新指定,并且需要重新定义 CMD、ENV 等。
2.2 特性
- 丢失镜像分层结构,最终导入的镜像仅一个层,体积可能较小(因为文件系统快照已经合并)。
- 丢失原镜像的所有元数据(标签、CMD、ENV、WORKDIR 等),导入后这些都需要手动重新设置。
- 导出的容器文件系统是容器运行后的状态,可能包含运行中产生的临时文件或修改,因此常用于创建"基础快照"或删除敏感数据后重新分发。
- 适用于将一个容器当前修改的状态(如安装调试工具、修改配置)保存为新镜像,但不关心历史。
2.3 典型流程
docker export
传输
docker import
重新设置 CMD/ENV
运行中的容器
my-app-container
container-fs.tar
扁平文件系统
container-fs.tar
新镜像
my-app-custom:latest
单层, 无历史
可用镜像
使用场景:
- 基于运行容器创建一个轻量级的"最终"镜像(例如去掉敏感文件后分发)。
- 从容器快照生成一个精简的基础镜像,手动控制文件内容。
- 与 save/load 不同,不能用于完整传递层结构。
三、核心区别对比
| 维度 | docker save / load | docker export / import |
|---|---|---|
| 操作对象 | 镜像(Image) | 容器(Container) |
| 导出内容 | 镜像所有层、元数据、标签 | 容器文件系统的扁平快照 |
| 镜像层数 | 保留原分层结构 | 导入后合并为单层 |
| 元数据保留 | 完整保留(CMD, ENV, WORKDIR, 标签等) | 丢失,需手动重设 |
| 体积 | 较大(多层,可能重复文件) | 较小(文件系统已合并) |
| 历史记录 | 保留(docker history 可查) | 无历史记录 |
| 多镜像支持 | 可一次保存多个镜像到同一个 tar | 一次只能导出一个容器文件系统 |
| 典型用途 | 镜像迁移、离线部署、备份 | 容器快照、生成精简基础镜像、清理敏感数据 |
| 恢复后的标签 | 自动恢复原标签(若无冲突) | 需要手动指定新的标签 |
| 与 Dockerfile 关系 | 保留构建历史,可重建相同层 | 无历史,无法回推 Dockerfile 指令 |
四、文件结构与内容对比(通过思维导图)
镜像导出/导入
save/load
对象: 镜像
导出内容
所有层的 tar 包
层关系
标签/版本信息
CMD, ENV等
结果
保持层结构
历史完整
适合迁移与缓存
export/import
对象: 容器
导出内容
容器文件系统快照
没有层
没有元数据
结果
单层镜像
无历史记录
需手动配置
五、实战决策流程图
如何选择导出/导入方式?可以参考以下决策逻辑:
是
是
否,仅要文件系统合并后的小镜像
否,目标是容器当前文件状态
需要导出什么?
目标是镜像?
需要保留层和元数据?
使用 docker save / load
考虑从运行容器 export
使用 docker export / import
手动添加 CMD/ENV 等配置
离线迁移 / 备份完成
六、常见错误与注意事项
- 将 save/load 用于容器 :
docker save不支持容器,必须提交成镜像后再保存(docker commit)。 - import 后镜像元数据丢失 :如果通过 export/import 生成的镜像运行容器时发现没有设置 CMD,容器会直接退出。因此必须通过
docker run命令行或后续docker commit重新设置指令。 - 体积误解:save 的 tar 可能比实际镜像总大小更大,因为分层可能包含重复或未压缩的数据;export 的 tar 接近容器实际使用的磁盘空间。
- 层冲突:load 导入时,如果本地已有相同的层(通过摘要匹配),不会重复存储;export 总是生成新层。
七、总结
- 镜像迁移、保留完整历史、多标签 → 使用 save/load。
- 基于容器快照生成新镜像、减少层数或清理敏感信息 → 使用 export/import。
- 两者不可混用:save 的文件只能 load,export 的文件只能 import。它们产生的 tar 格式虽然都是 tar,但内部结构完全不同,不可互换。
在 Java Docker 实战中,比如要将一个 Spring Boot 应用镜像转移到无网络的生产环境,通常使用 save/load 以保证环境变量、入口点等配置不丢失;如果想基于一次调试后的容器状态创建一个新的基础镜像,则会使用 export/import 并手动补充启动命令。掌握这些区别和原理,是高级 Docker 用户的重要技能。