虚拟机docker迁移想做到不中断业务、不丢数据,核心就一条:先把状态冻结,再平滑搬移,最后灰度切换。听起来有点绕,但拆开看其实不复杂,很多朋友把迁移想成了“拷文件”,结果要么容器起不来,要么数据库丢了一大截,今天这篇就把虚拟机里docker迁移的全流程讲透,重点说清楚怎么保数据、怎么减中断。
迁移前必须搞清楚的三个问题
你搬的是“容器”还是“数据”
docker迁移这个词太宽泛了,有人只是想换个宿主机,把镜像拉过去就行;有人是把整个虚拟机从A平台搬到B平台,比如从VMware迁到KVM,这两种情况的处理方式完全不同,前者只要搞定镜像和容器配置,后者还得考虑操作系统层、网络、存储的适配。
迁移前先问自己三个问题:
- 容器内的应用是无状态的还是有状态的?
- 数据是存在容器可写层、数据卷里,还是宿主机目录里?
- 迁移期间业务能接受多久的不可用?
这三个问题决定了后续所有操作策略,无状态服务怎么迁都行,有状态服务比如MySQL、Redis、ES这类,核心全在数据一致性上。
目标机器的环境是否就绪
很多迁移失败不是迁移本身的问题,而是目标机器环境没准备好,docker版本、内核参数、存储驱动、网络模式,这四样但凡有一样对不上,容器起来就是各种奇奇怪怪的报错。
动手迁移之前,先把目标机器的环境清单列出来:
- docker版本和源机器保持一致,或者至少兼容
- 检查overlay2存储驱动是否启用
- 确认需要映射的端口没有被占用
- 如果有自定义网络,提前在目标机器上建好
迁移方案的选择依据
行业共识认为,没有一套方案能同时满足“零中断”和“零丢失”,关键在于取舍,纯离线迁移简单可靠,但业务会有一段时间完全不可用;在线迁移能缩短中断时间,但复杂度成倍上升。
选择方案时主要看两点:业务对连续性的要求有多高,以及数据量有多大,几十GB的数据和几个TB的数据,策略完全不在一个量级上。
最稳妥的离线迁移流程:慢但不出错
第一步:用docker commit保存容器状态
对于运行中的容器,执行docker commit命令可以生成一个镜像文件,这个命令的含义是把当前容器的文件系统状态,连同运行时的环境变量、启动命令等一起固化为镜像。

docker commit -m "backup before migration" -a "ops" 容器ID myapp:backup
这里有一个关键细节:commit出来的镜像包含了容器可写层的数据,但不包含通过-v或--mount挂载的数据卷内容,这是个极其常见的坑,后面会专门说。
第二步:镜像导出与传输
commit完成后,用save命令把镜像导出为tar包:
docker save -o /backup/myapp.tar myapp:backup
传输到目标机器的工具有很多,内网用scp,外网用rsync,都行,数据量大的时候可以考虑先压缩再传输:
tar -czf myapp.tar.gz myapp.tar
传输完成后在目标机器上执行加载:
docker load -i myapp.tar
第三步:容器重建与参数还原
镜像加载成功只是第一步,还需要用原来的参数重新创建容器,这里建议提前把所有容器的配置信息导出备份:
docker inspect 容器ID > container_info.json
创建新容器时,重点检查三块:端口映射的-p参数、环境变量的-e参数、数据卷挂载的-v参数,这些信息都在inspect输出里,对着抄就行了。
docker数据卷迁移:最容易丢数据的一环
为什么数据卷不能靠commit搞定
前面说了,docker commit不管数据卷,原因是数据卷的设计理念就是解耦的,它不属于镜像的文件系统,很多朋友在迁移时只打了镜像包,结果数据库数据全丢,就是这个原因。
数据卷的迁移需要单独处理,主要用docker run --volumes-from配合tar命令完成,这个操作被称为“数据卷备份三连”:
docker run --rm --volumes-from 容器名 -v $(pwd):/backup ubuntu tar cvf /backup/data.tar 数据卷路径
在目标机器上恢复:
docker run --rm --volumes-from 新容器名 -v $(pwd):/backup ubuntu tar xvf /backup/data.tar
数据量大于100GB时的处理建议
数据量大时,tar打包再传输的方式效率很低,业内专家指出,这类场景下用rsync做增量同步比一次性tar更实用,具体做法是先全量同步一次,再根据数据变化情况做增量同步,最后在停机窗口做最后一次增量,然后把数据卷挂载到新容器上。

数据库类应用的迁移,强烈建议使用自带工具,比如MySQL用mysqldump或xtrabackup,PostgreSQL用pg_dump,Redis用持久化文件加AOF重放,用文件级备份去迁数据库,在数据一致性上始终存在风险。
减少服务中断时间:在线迁移思路
先扩容再缩容的滚动策略
如果业务架构允许,一种非常实用的做法是在目标机器上先把新容器跑起来,然后通过负载均衡把流量逐步切过去,这个方案不需要修改任何docker数据,纯粹是应用层的流量调度,中断时间几乎为零。
具体操作路径:
- 在目标机器上完整部署一套环境,导入数据
- 用负载均衡配置新后端,权重设低
- 观察运行状态正常后逐步调整权重
- 旧容器全部摘除后回收资源
这套方案适合无状态或做了主从复制的应用,对单机有状态服务不适用,因为同一份数据不能同时被两个实例写。
停机窗口内的“伪实时”迁移法
对于无法在线迁移的场景,只能用最短的停机窗口完成操作,流程是:先同步数据,再停服务,做最终增量同步,然后启动新机器。
比如迁一个docker里的MySQL:
- 先用mysqldump全量导出,传到目标机器并导入
- 开启binlog记录,记录导出后的binlog位置
- 停机窗口内,将新增binlog同步到目标机器
- 确认数据一致后启动新服务,切换流量
这种方案的中断时长取决于增量数据的大小,通常能控制在分钟级,对多数中小业务来说,已经是可接受的范围了。
网络配置与IP地址的迁移陷阱
docker默认网络模式的影响
docker安装时会创建三个默认网络:bridge、host、none,默认情况下容器跑在bridge网络,IP是由docker自动分配的,这个IP在容器重建后会变化,如果应用里写死了旧IP,迁移后就会出问题。
更严重的是,如果用了docker自带的overlay网络或者自定义网络,迁移后需要重建网络,容器IP大概率会变,应用间的相互调用最好用的方式是用容器名或服务名来访问,而不是IP,这样迁移后不会受影响。

需要保持固定IP的场景怎么办
有些老应用不方便改配置,必须保持固定的容器IP,这种情况下,在目标机器上需要先创建自定义网络,然后在启动容器时指定静态IP:
docker network create --subnet=172.20.0.0/24 mynet docker run --network mynet --ip 172.20.0.10 myapp:backup
虚拟机层面如果用的是桥接网络,容器IP会直接绑定在宿主机网络上,这种情况下还要注意目标机器的物理网卡配置、网关设置是否一致。
端口冲突与防火墙策略
目标机器上如果还有其他服务在用同一个宿主机端口,容器启动时就会报端口冲突,迁移前用ss -lntp检查一下端口占用情况,比启动失败再排查高效得多。
防火墙是另一个容易被忽视的环节,尤其是云服务器,安全组策略不放开端口,新机器上的服务外部根本访问不了,迁移前把端口放行规则完整梳理一遍,别让配置问题变成了“迁移失败”的替罪羊。
迁移后的完整性验证清单
启动级的验证项
容器启动只是开始,启动成功不代表迁移成功,要验证的方面包括:
- 容器状态是否为Up,重启策略是否生效
- 日志里有没有error或exception记录
- 端口监听是否正常,所需端口都能telnet通
- 容器内进程数是否和迁移前一致
数据层面的校验方法
数据完整性校验是迁移质量的核心,最直接的方式是对比迁移前后数据库行数、关键表的总记录数,如果是文件类数据,可以对比文件数量和总大小。
业务允许的情况下,可以做一次全链路演练,模拟一次用户请求,看从入口到存储的完整服务链路是否工作正常,这一步能发现很多单点验证时发现不了的问题。
回滚预案不能省
再严谨的迁移也有失败的可能,迁移前务必保留源机器上的所有数据,确认新环境稳定运行一段时间后再清理,这个时间周期建议至少是7天,如果业务流量有明显周期性,最好跨过一个完整周期再清理。
docker迁移的复杂度取决于你对自身业务状态的掌握程度,无状态应用轻松迁,有状态应用谨慎迁,数据库应用专业迁,把镜像、数据卷、网络三件事分开处理,按流程走,你会发现迁移没那么可怕。