Skip to content

Docker 环境跨主机迁移指南

Docker

将一台服务器上的 Docker 镜像、Compose 配置、自定义网络与数据卷完整迁移至另一台服务器,覆盖导出、传输、导入、重建网络、按序启动的全流程,并记录镜像标签丢失、端口冲突、upstream 解析失败三类典型故障的排查与修复

标签:
Docker Docker Compose 迁移 运维
发布于 2026年8月21日

迁移概述

适用场景:将一台服务器(B 机器)上的 Docker 镜像、容器配置、数据卷完整迁移至另一台服务器(A 机器)。

本次迁移包含以下核心资产:

资产类型说明
Docker 镜像mysql:latestredis:latestnginx:latestportainer/portainer-ce:stsdocker-yidong-app:latestosrf/ros:noetic-desktop
Docker Compose 配置各服务的 docker-compose.yaml 及相关挂载目录
自定义网络prod-net(external 网络,用于容器间通信和固定 IP)
数据卷/挂载目录各容器的持久化数据目录

B 机器(源端)导出操作

导出镜像

正确做法:使用「镜像名:标签」导出,保留标签信息:

bash
docker save -o all-images.tar \
  mysql:latest \
  redis:latest \
  nginx:latest \
  portainer/portainer-ce:sts \
  docker-yidong-app:latest \
  osrf/ros:noetic-desktop

注意:严禁使用镜像 ID 导出,否则会导致标签信息丢失(详见踩坑记录 #1)。

打包配置文件与挂载目录

bash
tar -czf docker-projects.tar.gz \
  ~/practical/portainer/ \
  ~/practical/nginx/ \
  ~/practical/mysql/ \
  ~/practical/redis/ \
  ~/practical/app/

导出自定义网络信息

bash
docker network inspect prod-net > prod-net-config.json

传输至 A 机器

bash
scp all-images.tar docker-projects.tar.gz prod-net-config.json user@A机器IP:~/practical/

A 机器(目标端)导入与恢复操作

导入镜像

建议使用 DOCKER_BUILDKIT=0 避免兼容性问题:

bash
DOCKER_BUILDKIT=0 docker load -i ~/practical/all-images.tar

导入后验证镜像标签是否完整:

bash
docker images

解压配置文件

bash
cd ~/practical
tar -xzf docker-projects.tar.gz

重建自定义网络

创建 external 网络,指定子网(根据 prod-net-config.json 中的信息):

bash
docker network create --driver bridge \
  --subnet 172.18.0.0/16 \
  prod-net

依次启动服务

建议按依赖顺序启动:基础设施 → 应用 → 网关:

bash
cd ~/practical/portainer && docker compose up -d
cd ~/practical/redis     && docker compose up -d
cd ~/practical/mysql     && docker compose up -d
cd ~/practical/app       && docker compose up -d
cd ~/practical/nginx     && docker compose up -d

验证服务状态

检查所有容器是否正常运行:

bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

踩坑记录与故障排查

问题 #1:镜像导入后标签全部丢失(显示 <none>:<none>

现象docker load 输出只有镜像 ID,docker images 中标签全部为 <none>

Loaded image ID: sha256:dbbb94208ca8...
Loaded image ID: sha256:914b9d749556...
REPOSITORY   TAG       IMAGE ID       SIZE
<none>       <none>    dbbb94208ca8   263MB
<none>       <none>    914b9d749556   934MB

原因:在 B 机器执行 docker save 时,使用了镜像 ID 而不是镜像名:标签。使用 ID 导出时,tar 包中的 manifest.json 不包含 RepoTags 字段,导致 docker load 无法恢复标签。

解决方案

步骤 1:从 tar 包中提取原始标签映射关系:

bash
tar -xf all-images.tar -O manifest.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data:
    tags = item.get('RepoTags', [])
    config = item.get('Config', '')
    if tags:
        print(f'{config} -> {tags}')
"

步骤 2:根据映射关系手动打标签,格式:docker tag <镜像ID> <仓库名>:<标签>

bash
docker tag dbbb94208ca8 docker-yidong-app:latest
docker tag 914b9d749556 mysql:latest
docker tag 329b288189cd portainer/portainer-ce:sts
docker tag 2481edb3016c redis:latest
docker tag 9f33606b3685 nginx:latest

问题 #2:Portainer 启动失败 — 端口冲突

现象

Error response from daemon: failed to set up container networking:
failed to bind host port 0.0.0.0:9000/tcp: address already in use

原因:A 机器上 9000 端口已被其他服务占用。Portainer CE 新版的 Web UI 已通过 9443 (HTTPS) 端口提供,9000 端口(旧版 HTTP)已不再需要。

解决方案:编辑 docker-compose.yaml,移除多余的 9000 端口映射:

bash
sed -i '/- 9000:9000/d' docker-compose.yaml
docker compose up -d

修改后的端口配置应为:

yaml
ports:
  - 9443:9443   # Web UI (HTTPS)
  - 8008:8000   # Edge Agent
  # 9000:9000 已移除

问题 #3:Nginx 启动失败 — upstream 主机名解析失败

现象

nginx: [emerg] host not found in upstream "portainer" in /etc/nginx/conf.d/default.conf:18

容器反复崩溃重启。

原因:Nginx 在启动时立即解析 upstream 中的主机名。如果此时目标容器尚未启动完毕或不在同一 Docker 网络中,解析失败会导致 Nginx 直接退出。

解决方案(推荐:使用变量 + Docker DNS 实现延迟解析):修改 Nginx 配置,将 proxy_pass 改为变量形式。

原配置(启动时立即解析,失败即崩溃):

nginx
location /portainer/ {
    proxy_pass https://portainer:9443;
}

修改后(请求时才解析,支持动态重试):

nginx
location /portainer/ {
    resolver 127.0.0.11 valid=10s ipv6=off;
    set $upstream_portainer https://portainer:9443;
    proxy_pass $upstream_portainer;
    proxy_ssl_verify off;
}

说明:127.0.0.11 是 Docker 内置 DNS 服务器,配合变量使用时支持动态解析。

最佳实践总结

推荐做法

  • 镜像导出:始终使用「镜像名:标签」而非镜像 ID
  • 网络配置:使用 external: true 网络,提前在目标机创建
  • 服务依赖:在 compose 中使用 depends_on 声明启动顺序
  • Nginx 反代:使用 resolver + 变量实现延迟 DNS 解析
  • 端口规划:迁移前检查目标机端口占用情况(ss -tlnp

避免做法

  • 用镜像 ID 导出:丢失所有标签信息,恢复极其麻烦
  • 硬编码 upstream:Nginx 启动时目标不可用则直接崩溃
  • 忽略 external 网络:容器无法获取预期 IP,容器间通信失败
  • 同时启动所有服务:基础设施未就绪导致应用层启动失败

快速验证清单

迁移完成后,按以下清单逐项确认:

头像由 PixelMe (xsgames.co/pixelme) 生成

CRUDClass BLOG

分享技术笔记和日常随笔

联系我

Copyright © 2026 CRUDClass BLOG

蒙ICP备2021004379号