在日常运维里,Docker 的"导入导出"需求非常常见,通常集中在以下几类场景:
- 需要对服务做一次 全量备份
- 需要在新机器上做 完整恢复
- 目标机器不能联网,要做 离线迁移
- 需要把某个容器、镜像、数据卷打包后带到另一台服务器
- 需要在升级、迁移、重装系统前保留业务状态
但很多人第一次接触时,很容易把下面几组命令混淆:
docker save/docker loaddocker export/docker importdocker cpdocker volumedocker compose
这篇文章的目标不是罗列命令,而是用 Cookbook 的方式告诉你:
面对具体场景时,到底该用哪一组命令。
先说结论:Docker 的"完整迁移"到底要备份什么
如果你要迁移的是一个"可运行的服务",而不是一个临时容器快照,那么完整备份通常应包括以下 3 类内容:
- 镜像
- 编排与配置
- 数据
对应到 Docker 世界里,就是:
- 镜像 :
docker save/docker load - 编排与配置 :
compose.yaml、.env、配置文件目录 - 数据:named volume 或 bind mount 的真实数据目录
所以最推荐的全量方案其实是:
- 导出镜像
- 备份 Compose 文件和配置
- 打包 Volume 或宿主机挂载目录
- 在目标机器上恢复镜像、配置和数据
- 再用 Compose 拉起服务
一句话总结:
不要把"导出一个容器"误认为"完整备份一个 Docker 服务"。
一张表看懂:到底该用哪个命令
| 目标 | 推荐命令 | 是否包含 Volume 数据 | 是否适合服务迁移 |
|---|---|---|---|
| 导出镜像 | docker save |
否 | 是 |
| 导入镜像 | docker load |
否 | 是 |
| 导出容器文件系统 | docker export |
否 | 否 |
| 导入为镜像 | docker import |
否 | 否 |
| 复制单个文件 | docker cp |
部分 | 辅助用途 |
| 备份 named volume | docker run -v volume:/data ... tar |
是 | 是 |
| 备份 bind mount | tar 宿主机目录 |
是 | 是 |
核心概念:save/load 和 export/import 到底有什么区别
这是最容易混淆的一组。
docker save / docker load
这组命令用于 镜像级别 的导出导入。
适用场景
- 离线传输镜像
- 内网环境部署
- 多台机器之间复制镜像
- 不能访问公网镜像仓库时分发镜像
特点
- 保留镜像层信息
- 保留 tag
- 适合正式迁移和离线部署
- 不包含容器运行状态
- 不包含 volume 数据
示例:
bash
docker save -o nginx.tar nginx:latest
docker load -i nginx.tar
docker export / docker import
这组命令用于 容器文件系统快照 的导出导入。
适用场景
- 临时导出某个容器当前文件系统内容
- 制作一个简单的新镜像基底
- 调试、留档、分析
特点
- 导出的是容器当前文件系统
- 不保留镜像历史层
- 不保留 Compose 配置
- 不保留 volume 数据
- 不适合完整迁移服务
示例:
bash
docker export my-container -o my-container.tar
cat my-container.tar | docker import - my-image:latest
最重要的结论
如果你的目的是"离线迁移服务"或"完整备份恢复",优先使用 save/load,而不是 export/import。
Recipe 1:离线导出和导入镜像
这是最常见的一道"配方"。
场景
目标服务器不能联网,但需要运行某个镜像。
导出镜像
bash
docker pull nginx:1.27
docker save -o nginx-1.27.tar nginx:1.27
如果要压缩:
bash
gzip nginx-1.27.tar
得到:
bash
nginx-1.27.tar.gz
传输到目标机器
可以使用:
scprsync- U 盘
- 内部文件分发系统
例如:
bash
scp nginx-1.27.tar.gz user@target:/opt/
目标机器导入
bash
gunzip /opt/nginx-1.27.tar.gz
docker load -i /opt/nginx-1.27.tar
验证
bash
docker images
Recipe 2:一次导出多个镜像
场景
一个服务栈包含多个镜像,例如:
nginxredismysqlmyapp
导出
bash
docker save -o app-stack.tar \
nginx:1.27 \
redis:7 \
mysql:8 \
myapp:1.0
导入
bash
docker load -i app-stack.tar
建议
如果你是在做一套离线交付包,推荐统一放到一个目录中:
bash
backup_bundle/
├── images/
│ └── app-stack.tar
├── config/
│ ├── compose.yaml
│ └── .env
└── volumes/
这样最清晰。
Recipe 3:导出容器文件系统,但不用于服务迁移
场景
想保留当前容器内部改动,或者分析容器现状。
导出
bash
docker export my-container -o my-container.tar
导入为镜像
bash
cat my-container.tar | docker import - my-container-snapshot:latest
什么时候可以用
可以用于:
- 保存实验环境
- 导出某次调试后的临时系统状态
- 生成一个轻量新镜像
什么时候不要用
不要用于:
- 完整备份业务服务
- 恢复数据库服务
- 迁移含 volume 的应用
- 替代 Compose 部署
因为它 不包含 volume 数据,也不保留运行编排信息。
Recipe 4:完整备份一个 Compose 服务
这是最实用、也最值得收藏的一道"配方"。
场景
你有一个通过 Docker Compose 运行的服务,需要:
- 全量备份
- 异机恢复
- 离线迁移
- 重装系统前保留所有业务内容
备份对象
完整备份至少包括:
compose.yaml.env- 应用配置文件
- 镜像
- volume 数据
第一步:准备备份目录
bash
mkdir -p backup_bundle/{images,config,volumes}
第二步:备份 Compose 文件和配置
bash
cp compose.yaml backup_bundle/config/
[ -f .env ] && cp .env backup_bundle/config/
如果还有配置目录,例如:
bash
cp -r ./config backup_bundle/config/
第三步:导出镜像
先找出 Compose 使用的镜像:
bash
docker compose config
然后导出镜像:
bash
docker save -o backup_bundle/images/app-images.tar \
nginx:1.27 \
redis:7 \
mysql:8 \
myapp:1.0
第四步:备份 named volume
为什么 volume 要单独备份
因为:
docker save不包含 volumedocker export也不包含 volume
数据卷必须自己单独打包。
查看 volume 列表
bash
docker volume ls
备份某个 volume
bash
docker run --rm \
-v myproj_mysql_data:/data \
-v $(pwd)/backup_bundle/volumes:/backup \
alpine \
tar czf /backup/mysql_data.tar.gz -C /data .
再比如 Redis 数据:
bash
docker run --rm \
-v myproj_redis_data:/data \
-v $(pwd)/backup_bundle/volumes:/backup \
alpine \
tar czf /backup/redis_data.tar.gz -C /data .
第五步:建议停服务后再做最终备份
如果应用正在写数据,备份出的数据可能不一致。
特别是以下类型服务:
- MySQL
- PostgreSQL
- Redis
- MongoDB
- Elasticsearch
推荐在最终备份前执行:
bash
docker compose stop
或者:
bash
docker compose down
然后再重新执行 volume 打包。
备份完成后再启动:
bash
docker compose up -d
Recipe 5:在目标机器上恢复完整服务
场景
你已经拿到了完整备份包,要在新机器恢复服务。
假设备份目录在:
bash
/opt/backup_bundle
第一步:导入镜像
bash
docker load -i /opt/backup_bundle/images/app-images.tar
第二步:恢复 Compose 文件
bash
mkdir -p /opt/myapp
cp /opt/backup_bundle/config/compose.yaml /opt/myapp/
[ -f /opt/backup_bundle/config/.env ] && cp /opt/backup_bundle/config/.env /opt/myapp/
如果有配置目录:
bash
cp -r /opt/backup_bundle/config/config /opt/myapp/
第三步:先创建 volume
进入项目目录:
bash
cd /opt/myapp
docker compose up -d
这一步的主要目的是让 Docker 把 Compose 中声明的 volume 创建出来。
然后停掉服务,准备恢复数据:
bash
docker compose stop
第四步:把备份数据恢复到 volume
恢复 MySQL 数据:
bash
docker run --rm \
-v myapp_mysql_data:/data \
-v /opt/backup_bundle/volumes:/backup \
alpine \
sh -c "cd /data && tar xzf /backup/mysql_data.tar.gz"
恢复 Redis 数据:
bash
docker run --rm \
-v myapp_redis_data:/data \
-v /opt/backup_bundle/volumes:/backup \
alpine \
sh -c "cd /data && tar xzf /backup/redis_data.tar.gz"
第五步:启动服务
bash
cd /opt/myapp
docker compose up -d
验证
检查:
- 容器是否正常启动
- 端口是否监听
- 业务是否可访问
- 数据是否存在
- 日志是否正常
bash
docker compose ps
docker compose logs -f
Recipe 6:如果你使用的是 bind mount,如何备份和恢复
很多服务不是使用 named volume,而是直接挂载宿主机目录,比如:
yaml
services:
app:
volumes:
- /data/app:/app/data
- /data/logs:/app/logs
这种情况下,备份方式反而更简单。
备份
bash
tar czf app-bind-data.tar.gz /data/app /data/logs
恢复
bash
tar xzf app-bind-data.tar.gz -C /
注意
使用 bind mount 时,完整备份内容就变成:
- 镜像
- Compose 文件
- 宿主机目录数据
而不是 Docker volume 本身。
Recipe 7:如何打包成一个可交付的离线安装包
如果你要把一套服务交付给客户、同事或另一台服务器,推荐把所有内容整理成一个标准目录。
推荐目录结构
bash
offline_bundle/
├── images/
│ └── app-images.tar
├── config/
│ ├── compose.yaml
│ ├── .env
│ └── config/
├── volumes/
│ ├── mysql_data.tar.gz
│ └── redis_data.tar.gz
├── scripts/
│ ├── restore.sh
│ └── backup.sh
└── README.md
打包
bash
tar czf offline_bundle.tar.gz offline_bundle
恢复时
只需:
- 解压
- 导入镜像
- 恢复配置
- 创建 volume
- 导入 volume 数据
- 启动服务
这是很适合企业内网、机房隔离区、生产环境交付的方式。
Recipe 8:用脚本实现一键备份
下面给一个通用思路的备份脚本模板。
bash
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR=/opt/myapp
BACKUP_DIR=/opt/backup_bundle
mkdir -p "$BACKUP_DIR/images" "$BACKUP_DIR/config" "$BACKUP_DIR/volumes"
cd "$PROJECT_DIR"
echo "==> 备份配置"
cp compose.yaml "$BACKUP_DIR/config/" || true
[ -f .env ] && cp .env "$BACKUP_DIR/config/" || true
[ -d config ] && cp -r config "$BACKUP_DIR/config/" || true
echo "==> 停止服务"
docker compose stop
echo "==> 导出镜像"
docker save -o "$BACKUP_DIR/images/app-images.tar" \
nginx:1.27 redis:7 mysql:8 myapp:1.0
echo "==> 导出 volumes"
docker run --rm \
-v myapp_mysql_data:/data \
-v "$BACKUP_DIR/volumes":/backup \
alpine \
tar czf /backup/mysql_data.tar.gz -C /data .
docker run --rm \
-v myapp_redis_data:/data \
-v "$BACKUP_DIR/volumes":/backup \
alpine \
tar czf /backup/redis_data.tar.gz -C /data .
echo "==> 启动服务"
docker compose up -d
echo "备份完成:$BACKUP_DIR"
Recipe 9:用脚本实现一键恢复
bash
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR=/opt/myapp
BACKUP_DIR=/opt/backup_bundle
mkdir -p "$PROJECT_DIR"
cp "$BACKUP_DIR/config/compose.yaml" "$PROJECT_DIR/" || true
[ -f "$BACKUP_DIR/config/.env" ] && cp "$BACKUP_DIR/config/.env" "$PROJECT_DIR/" || true
[ -d "$BACKUP_DIR/config/config" ] && cp -r "$BACKUP_DIR/config/config" "$PROJECT_DIR/" || true
echo "==> 导入镜像"
docker load -i "$BACKUP_DIR/images/app-images.tar"
cd "$PROJECT_DIR"
echo "==> 创建 volumes"
docker compose up -d
docker compose stop
echo "==> 恢复数据"
docker run --rm \
-v myapp_mysql_data:/data \
-v "$BACKUP_DIR/volumes":/backup \
alpine \
sh -c "cd /data && tar xzf /backup/mysql_data.tar.gz"
docker run --rm \
-v myapp_redis_data:/data \
-v "$BACKUP_DIR/volumes":/backup \
alpine \
sh -c "cd /data && tar xzf /backup/redis_data.tar.gz"
echo "==> 启动服务"
docker compose up -d
echo "恢复完成"
Recipe 10:只复制容器里的单个文件
有时候不需要全量备份,只想拿出一个文件,比如日志、配置、证书。
这时可以用 docker cp。
从容器拷贝到宿主机
bash
docker cp my-container:/etc/nginx/nginx.conf ./nginx.conf
从宿主机拷贝到容器
bash
docker cp ./nginx.conf my-container:/etc/nginx/nginx.conf
适用场景
- 临时导出配置
- 复制日志
- 调整证书
- 小范围修复
不适用
- 不适合作为完整迁移方案
- 不适合批量数据备份
常见误区
误区 1:docker export 可以完整迁移服务
不能。
它只导出容器文件系统,不包含:
- volume 数据
- Compose 配置
- 网络声明
- 环境变量
- 启动命令的完整上下文
误区 2:docker save 会连数据一起导出
不会。
docker save 只处理镜像,不处理运行时数据卷。
误区 3:docker compose down 会自动删除数据
默认不会。
只有加上 -v 才会删除相关 volume:
bash
docker compose down -v
这条命令非常危险,执行前一定确认。
误区 4:直接备份 /var/lib/docker 就行
虽然理论上可以做整机级灾备,但通常不推荐作为常规迁移方案,因为它:
- 强依赖 Docker 版本
- 强依赖存储驱动
- 可移植性差
- 跨机器恢复失败风险高
常规迁移还是应该采用:
- 镜像导出
- 配置保存
- volume 数据单独打包
这种标准做法。
最佳实践清单
1. 服务迁移优先用 save/load
而不是 export/import。
2. Volume 必须单独备份
镜像不等于数据。
3. Compose 文件必须纳入备份
没有编排文件,恢复后的服务往往不可复现。
4. 数据库类服务备份前尽量停写
避免产生不一致数据。
5. 离线交付建议做成统一目录包
镜像、配置、数据、脚本放在一起最利于维护。
6. 恢复流程要提前演练
真正有故障时,第一次恢复往往最容易出错。
7. 给备份包加版本号和日期
例如:
bash
backup_bundle_2025-02-16_v1.tar.gz
这样便于追溯。
一套推荐的标准方案
如果你问我:Docker 如何做"全量备份恢复 + 离线迁移"的最佳方案?
我的标准答案是:
标准备份包应包含
compose.yaml.env- 配置文件目录
docker save导出的镜像包- 所有 volume 的压缩归档
- 备份和恢复脚本
- 一份 README
标准备份动作
- 停止服务或冻结写入
- 导出镜像
- 打包 volume
- 备份 Compose 和配置
- 统一归档
标准恢复动作
- 导入镜像
- 恢复 Compose 和配置
- 创建 volume
- 恢复 volume 数据
- 启动服务
- 做健康检查
这套方案的优点是:
- 可离线
- 可迁移
- 可复现
- 可审计
- 可脚本化
- 不依赖原机器环境过深
总结
Docker 的导入导出不能只盯着一个命令看,而应该根据对象来区分:
- 镜像 :
docker save/docker load - 容器文件系统快照 :
docker export/docker import - 单个文件 :
docker cp - 数据卷 :
tar+ 临时容器 - 服务定义 :
compose.yaml、.env、配置目录
如果你的目标是:
- 全量备份
- 完整恢复
- 离线迁移
- 跨主机部署
那么最稳妥的方式永远是:
镜像 + 配置 + 数据 三者一起备份与恢复。
这才是 Docker 世界里真正意义上的"完整迁移"。