把容器从一台服务器搬到另一台服务器,最直观的想法就是把它打包带走。Docker 提供的 docker export 和 docker import 正是用来干这件事的一对命令:前者把容器导出成 tar 压缩包,后者把 tar 包重新导入成镜像。看起来很简单,但很多人操作完发现导入的镜像启动不了,或者体积和原来对不上,这其实是对这两个命令的工作机制理解不够。本文把 export 与 import 的原理、用法和注意事项一次讲透。

一、docker export 与 docker import 的基本原理
docker export 处理的对象是容器,不是镜像。它会把容器当前运行时的文件系统快照打包成一个 tar 文件,注意这里导出的只有文件系统本身,也就是容器最上层可写层加上下面所有只读层合并后的最终视图。镜像的元数据信息,比如每一层的构建历史、环境变量、CMD、ENTRYPOINT、EXPOSE 这些配置,统统不会包含在导出文件里。
正因为丢弃了分层信息,export 出来的 tar 包通常比对应的镜像小不少。镜像里每一层的 diff 记录、历史日志都被剥离了,剩下的就是一台机器上实实在在的文件和目录。这也解释了一个常见现象:一个几百 MB 的镜像导出后可能只有一两百 MB,因为很多层里被删除的文件在合并视图里已经不存在了。
docker import 则是反向操作,它读取一个 tar 包(或直接从 URL 拉取),把里面的文件系统内容作为一个全新的层导入,生成一个新的镜像。import 允许你在导入时通过参数补上元数据,例如 --change 可以追加 Dockerfile 指令,--message 可以添加提交说明。基本用法如下:
# 把容器 myweb 导出为 tar 文件 docker export myweb > myweb.tar # 或者用 -o 参数指定输出文件 docker export -o myweb.tar myweb # 从 tar 文件导入为镜像,并补回启动命令 docker import myweb.tar myweb:latest \ --change 'CMD ["nginx", "-g", "daemon off;"]' \ --change 'ENTRYPOINT ["/docker-entrypoint.sh"]' # 直接从一个 URL 导入 docker import http://192.168.0.1/backup/myweb.tar myweb:imported
可以看到,import 之后得到的是一个新镜像,需要再用 docker run 基于它创建新容器,而不是直接还原出原来的容器。容器相关的运行参数,例如端口映射、挂载卷、重启策略,这些都不在 export 的能力范围内,需要在启动新容器时重新指定。
二、与 docker save、docker load 的本质区别
很多人分不清 export/import 和 save/load 这两组命令,它们的差别可以用一句话概括:前者操作的是容器的文件系统快照,后者操作的是完整的镜像。docker save 导出的是镜像本身,包含所有分层结构和元数据配置;docker load 加载后得到的镜像和原来的镜像 ID 一致,可以直接运行,不需要额外补配置。
两者的差异具体体现在三个方面。第一是保留信息不同:save 会保留镜像的每一层、构建历史、CMD/ENTRYPOINT 等全部元数据,而 export 只保留最终的文件系统内容。第二是体积不同:save 出来的包通常更大,因为它包含了层的 diff 记录和被删除文件的痕迹。第三是适用场景不同:save/load 适合在不同机器之间分发同一个镜像,保证一致性;export/import 适合备份一个已经运行过、内部有数据改动的容器状态。
| 对比项 | docker export / import | docker save / load |
|---|---|---|
| 操作对象 | 容器 | 镜像 |
| 保留分层历史 | 不保留,压缩成一个层 | 完整保留 |
| 保留 CMD 等配置 | 不保留,需手动补 | 完整保留 |
| 文件体积 | 较小 | 较大 |
| 典型场景 | 备份容器内改动过的数据 | 分发标准化镜像 |
一个简单的判断标准:如果你要搬的是官方镜像或自己构建的镜像,本身没有运行时的数据改动,直接用 save 和 load 就够了,而且更可靠;如果容器里有你后来手动装的软件、修改过的配置文件,又没来得及整理成 Dockerfile,那 export 和 import 才是救急的手段。
三、完整迁移实操与常见坑
下面用一个完整例子演示跨主机迁移容器的全过程。假设源机器上有一个正在运行的容器,我们想在目标机器上恢复它:
# 1. 在源机器上导出容器 docker export -o app_backup.tar myapp # 2. 传输到目标机器 scp app_backup.tar root@192.168.0.2:/tmp/ # 3. 在目标机器上导入,补回启动命令和环境变量 docker import /tmp/app_backup.tar myapp:v2 \ --change 'ENV LANG=C.UTF-8' \ --change 'EXPOSE 8080' \ --change 'CMD ["java", "-jar", "/app/server.jar"]' # 4. 查看导入结果 docker images myapp # 5. 重新启动容器,补上原来的运行参数 docker run -d --name myapp-new -p 8080:8080 \ -v /data/app:/app/data --restart=always myapp:v2
第一个常见的坑是忘记补 CMD。import 生成的镜像如果没有指定启动命令,默认的 CMD 是 /bin/sh,用 docker run 启动时容器会立即退出,因为 sh 在非交互模式下没有任务可执行。如果不确定原镜像的启动命令是什么,可以在源机器上执行 docker inspect myapp --format '{{.Config.Cmd}} {{.Config.Entrypoint}}' 查看后照抄过来。
第二个坑是数据卷的内容不会被导出。export 只打包容器文件系统,挂载在 volume 里的数据是独立的,不在容器层内。迁移前需要单独备份卷目录,比如执行 docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar czf /backup/mydata.tar.gz /data,到目标机器后再解压还原到同名卷里。
第三个坑是把 import 当成长期方案。import 出来的镜像只有一层,没有任何构建历史,后续维护起来很不方便。正确的做法是把它当作临时救急:迁移完成后,尽快把容器内的改动整理成 Dockerfile,重新构建一个干净的镜像,让基础设施回到可追溯的状态。另外注意 import 不支持从标准输入读取管道数据时同时指定镜像名的部分老版本行为差异,生产环境中建议先在小范围验证导入结果再批量操作。
总结一下,docker export 和 docker import 组合解决的是容器文件系统快照的迁移问题,它的定位是轻量备份和应急迁移;而 save 和 load 负责完整镜像的分发。理解了 export 会丢弃分层和元数据这一核心特性,补配置、备卷、验启动这三步做扎实,容器迁移基本就不会翻车了。
docker exportdocker import容器镜像迁移修改时间:2026-09-09 23:30:47