1. 迁移前的认知与选型1.1 迁移到底在解决什么问题做OpenStack平台运维的人几乎都会遇到这样的场景某台计算节点要下线维护或者某台宿主机负载告警甚至只是单纯想调整资源分布这时候你就需要把上面的云主机实例挪走。Migrate Instance 就是干这件事的。它本质上是把一台宿主机上的虚拟机在不中断业务或者短暂中断的前提下移动到另一台宿主机上继续运行。我在实际维护中用到迁移最多的是两类场景一类是宿主机维护比如升级内核、更换硬件、修复RAID这种物理层面的操作必须让实例先跑路另一类是资源重平衡某个节点CPU或者内存被打满而另外几个节点闲得慌手工迁移几台实例过去比触发自动伸缩更可控。还有一类不算高频但很头疼的就是宿主机出现硬件异常征兆比如反复报EDAC错误、磁盘出现大量坏道这时候立刻迁移实例是降低故障影响面的有效手段。在动手之前必须要清醒一点迁移这个动作本身是有风险的。网络闪断、磁盘IO阻塞、CPU兼容性问题、内存脏页速率过高任何一个环节出问题都会造成迁移失败甚至实例异常。我在早期管理OpenStack集群时对迁移这件事掉以轻心结果吃过不小的亏——一台负载很高的实例在做热迁移时卡住目标主机上的实例启动后网络不通来回排查了很久才发现是网卡中断绑核问题和CPU型号不一致导致的。所以这篇内容我会把迁移的底层原理和实际操作结合起来讲尽量把每一个可能踩坑的环节都说明白。1.2 冷迁移和热迁移到底怎么选迁移在OpenStack体系里大体分为两类冷迁移和热迁移也叫在线迁移。**冷迁移Cold Migration / Migrate**通常指nova migrate命令触发的迁移。这个过程会先把实例关机或者保持关机状态把磁盘数据复制到目标宿主机然后在目标宿主机上重新启动实例。如果实例当时是运行中的nova服务会先做一次优雅关机默认走ACPI shutdown等实例彻底停止后再搬运数据。冷迁移期间业务中断是必然的但胜在逻辑简单、稳定性高对底层硬件环境的要求也比较宽松。**热迁移Live Migration**对应的是nova live-migration命令。它不需要关机通过libvirt在源主机和目标主机之间同步内存状态和磁盘变更业务几乎无感知。这个听起来很美好但实际对条件的要求相当苛刻需要共享存储或者块迁移支持需要源目标主机CPU指令集兼容需要网络带宽充足还需要nova和libvirt版本配合到位。任何一个条件不满足热迁移要么根本起不来要么跑到一半就失败。在选型上我个人的习惯是这样的能接受短时间中断的内部业务系统优先用冷迁移省心核心生产业务、不允许中断的实例优先热迁移但必须在迁移窗口做充分预检。不要为了炫技强行热迁移有些老旧的实例镜像、特殊的直通设备场景冷迁移反而是更稳的选择。2. 核心逻辑nova是怎么完成一次迁移的2.1 从命令行到虚拟机重建的完整链条要把迁移操作做好你得先知道背后发生了什么。这里我以冷迁移为例完整拆解一遍各组件之间的协作流程。当你执行openstack server migrate instance之后请求会先到达nova-api经过Keystone鉴权和quota检查之后nova-api把请求写入消息队列。nova-conductor从队列中取出这个迁移请求开始走调度逻辑——它要去数据库里查实例当前的host、节点信息然后调用nova-scheduler做过滤和权重计算选出一个可以放置的目标节点。这里有个容易被忽略的细节nova-conductor在做冷迁移调度时默认会把实例当前所在的源节点排除掉这个行为由allow_resize_to_same_host参数控制如果该参数没有显式开启你是没法把实例迁移回原主机的。目标节点选定之后nova-conductor发送消息给目标节点上的nova-compute让它准备接收实例。目标节点的nova-compute会创建实例的基础目录/var/lib/nova/instances/instance_uuid/然后从镜像服务或者快照中准备系统盘。如果是块迁移block migration的场景数据这块会有不同的处理方式这个下面细说。等数据到位之后目标节点的nova-compute通过libvirt定义虚拟机的XML配置开始启动实例。启动成功后nova-compute上报状态到数据库nova-conductor更新instance的host、node字段同时通知源节点上的nova-compute清理本地实例数据。整个流程走完你会在nova list里看到实例的host已经变了。整个链路中消息队列起到异步解耦的作用所以你在命令行敲完迁移指令响应返回快不代表迁移完成。一定要用nova migration-list或者openstack server migration list去跟踪迁移状态。2.2 热迁移背后的数据同步机制热迁移比冷迁移复杂的地方在于它要一边保持源实例运行一边把数据同步到目标。libvirt底层通过QEMU的迁移机制来实现过程中会把源实例的内存页、CPU状态、设备状态序列化传输到目标QEMU进程。这里有个很重要的阶段叫预拷贝Pre-copy。第一次先把全部内存数据传过去之后持续迭代传输那些在上一轮传输期间被写脏的内存页。跑得快的场景下几轮迭代后脏页速率降下来了就进入**停机拷贝Stop-and-copy**阶段短暂挂起源实例把所有剩余内存状态和CPU状态一次性传过去然后目标实例接管源实例被销毁。需要注意如果实例内存太大或者内存写入速率特别快——比如跑着大型数据库或者高并发缓存——脏页产生速度可能赶不上传输速度那就进入无限循环迁移永远无法收敛最后libvirt会强制中止迁移。平时很多号称“迁移失败”的故障其实根本不是网络不通而是内存脏页速率过高导致迭代无法收敛。这个在实战排查里非常关键。磁盘数据处理有两条路线如果用了共享存储比如Ceph、NFS、LVM共享卷磁盘文件本身就在目标主机上可见不需要额外拷贝只要同步内存就完事如果没有共享存储只能用块迁移block migrationlibvirt会在迁移过程中把源实例的磁盘全部复制到目标主机同时持续处理新写入的块数据。这种情况下磁盘越大、数据变化越快迁移时间越长网络和磁盘IO的压力也越大。3. 实操准备迁移前必做的检查清单3.1 计算节点资源和网络状况体检我每次做批量迁移都会提前半天把涉及的主机全面体检一遍。这个体检不是随便看一眼load average而是要落到实处先看CPU。用virsh capabilities查看宿主机CPU的型号和特性集对比源主机和目标主机的输出确保QEMU能支撑的CPU模型是兼容的。如果源和目标不在同一代CPU架构上热迁移可能会直接报target CPU does not match之类的错误。解决思路要么给实例设置明确的CPU模型要么在nova-compute配置里开启cpu_mode host-model让虚拟机的CPU模型跟随宿主机再用libvirt的CPU热插拔兼容策略做兜底。再看内存和磁盘。free -g看物理内存余量df -h看实例存储目录的空间余量。这里有个容易被忽视的点冷迁移的块迁移模式下目标主机的存储空间需求是源实例磁盘的实际用量而不是虚拟磁盘的分配大小。如果你用du -sh /var/lib/nova/instances/instance_uuid/这个命令查看才能看到真实占用。我就碰到过一次裸金属规格的实例虚拟磁盘定义是200GB实际数据只有20GB但是目标主机剩余空间只有30GB按分配大小去判断空间不够按实际用量却刚刚好结果没查清楚差点误判。网络方面重点看节点之间的带宽和质量。迁移数据走的是管理网络还是存储网络不同架构不一样但最好确认源和目标主机之间能直接互通且防火墙没有拦截libvirt的迁移端口默认是TCP 49152到49215这个段。还要检查Open vSwitch或者Linux Bridge的配置在目标是正确的同一个实例的安全组规则和网络端口在目标主机上能够正常映射。3.2 实例自身状态和配置约束不是每个实例都适合迁移。直通了PCI设备的实例比如GPU透传、SR-IOV网卡大部分版本OpenStack默认禁止热迁移这是出于设备状态无法迁移的考虑。如果确定要迁只能走冷迁移并且要确保目标主机有同样的PCI设备可直通。还有一类特殊情况是实例有本地磁盘。如果实例使用了ephemeral磁盘并且没有共享存储支持就必须依赖块迁移。块迁移对后端驱动有要求libvirt的qemu驱动基本都支持但如果你用的是比较老旧的libvirt版本或者使用了非标准存储后端有可能触发未实现的BUG。碰到这种情况我的建议是不要死磕块迁移直接把实例做成镜像或者快照再在目标节点上重建反而更省事。安全组和浮动IP也要检查。迁移本质上不会修改实例的网络拓扑浮动IP还是绑定在原instance上不会因为换主机而变化。但如果你用的是基于主机名或者主机IP的内部通信架构迁移后可能会因为IP变化导致依赖它的服务全部需要重连。这点在微服务架构里尤其要提前评估迁移窗口尽量选在业务低峰期。最后看实例当前状态。openstack server show instance里的OS-EXT-STS:power_state如果是RUNNING冷迁移会走关机流程如果是SHUTOFF迁移只是搬运数据。注意如果实例是SHELVED状态迁移前要先unshelve否则nova会直接拒绝操作。4. 实操过程从冷迁移到热迁移的命令级演示4.1 冷迁移标准操作流程与参数选择假设环境是Ocata及以上版本可直接用OpenStackClient的命令行。先确认实例基本信息openstack server show test-vm-001输出里重点看properties里的host、status、OS-EXT-STS:power_state这些字段。确认无误后执行冷迁移openstack server migrate test-vm-001 --wait加了--wait参数命令会阻塞等待迁移完成返回...直到看到状态更新为ACTIVE或SHUTOFF。不加--wait则立即返回你需要自己轮询迁移状态openstack server migration list --server test-vm-001输出里有一列Status正常流程为queued-preparing-migrating-post-migrating-done。如果看到error就要进入排查流程了。冷迁移默认行为是什么说起来有点绕openstack server migrate在nova里对应的操作是resize它会把实例调度到新主机并执行“重新创建”过程。默认情况下它不会复制本地磁盘所以如果实例没有共享存储命令实际会报错或者实例在目标主机无法启动。你必须显式加上块迁移参数openstack server migrate test-vm-001 --block-migrate --wait--block-migrate这个参数就是告诉nova把本地磁盘数据也搬过去。如果你的实例后端存储是Ceph或者NFS这类共享存储那么不加--block-migrate反而更快因为数据不需要拷贝只是改一下数据库里的host字段。还有第三个冷迁移参数容易被忽略叫--instance-disks-overcommit配合--block-migrate使用。它表示在调度计算目标主机磁盘占用时按实例虚拟磁盘的分配大小去算而不是按实际占用去算。默认情况下nova的调度是按实际配额去评估的开了这个参数会让调度器认为实例会占满整个虚拟磁盘大小从而避免把实例放到一个“实际空间够但按分配大小不够”的主机上。冷迁移完成后我一般会做三件事第一登录实例确认业务正常第二openstack server show test-vm-001确认host已变化第三检查网络连通性和浮动IP绑定状态。确认无误后再去清理源节点上遗留的旧数据。正常情况下nova会自动清理但偶尔会出现清理失败的情况你会在源节点的/var/lib/nova/instances/下看到残留目录确认无引用后手动删除即可。4.2 热迁移在线迁移的关键命令与手段热迁移命令更直接openstack server migrate test-vm-001 --live --wait等等这个写法在老的OpenStackClient版本里是不可用的只有在新版本中openstack server migrate支持--live参数。在大多数生产环境里还在用旧命令的不少所以更通用的写法是openstack server migrate test-vm-001 --live-migration --wait或者直接用nova命令nova live-migration test-vm-001三种写法本质上都调用同一个API但参数行为不太一样。--live-migration默认走共享存储即不拷贝磁盘如果是非共享存储需要加--block-migrate参数openstack server migrate test-vm-001 --live --block-migrate --wait热迁移过程会持续一段时间期间你可以开另一个终端观察迁移进度nova migration-list --instance-uuid uuid列表里能看到源主机和目标主机以及迁移任务的状态。对于热迁移来说状态流转和冷迁移基本一致但在migrating阶段停留的时间长得多因为内存传输需要时间。热迁移过程中如果压力太大想尽一切办法保住业务可以考虑临时降低实例内存写入压力。比如业务方把批处理任务暂停、关闭大的分析任务、收缩缓存让脏页速率降下来迁移就能更快收敛。这种“治标不治本”的操作在紧急恢复场景里很管用。还有一个所有生产环境都必须知道的自定义迁移命令——强制迁移到指定主机。在部分故障场景下自动调度选出的目标节点不理想你想手动指定目标节点。冷迁移里可以这么干先配置nova的allow_resize_to_same_host无关它主要是限制同主机的手动指定主机其实是靠forced_host参数。用OpenStack API的方式是调用servers.py里的_action_resize并且传入host在OpenStackClient里目前没有优雅的命令参数那么直接但你用nova命令时会发现它支持--host选项nova migrate --host target-host-02 test-vm-001nova migrate的--host选项目前在很多发行版仍然有效。不过要注意老版本的nova并不支持在resize操作中强制指定目标主机新版本部分支持具体看你平台的版本。如果官方命令行不支持你有两个变通思路一是临时把其他候选节点禁用nova service-disable只留目标节点逼调度器选中目标二是通过API调用直接改OS-EXT-SRV-ATTR:host但我不推荐直接改数据库太危险。5. 常见问题与排查技巧实录5.1 迁移失败场景与日志定位迁移失败是家常便饭关键是你要知道去哪里看日志。大体日志分布是这样的nova-conductor.log记录调度和迁移状态流转nova-scheduler.log记录过滤和权重计算的细节nova-compute.log记录本节点上所有虚拟机的创建和迁移操作libvirt/qemu日志在/var/log/libvirt/qemu/instance_uuid.log下能告诉你QEMU级别的具体错误。实际中我遇到最多的一类报错是Live migration failed: internal error: process exited while connecting to monitor这种通常不是QEMU的问题而是目标主机的libvirt连接问题。检查目标主机libvirtd服务是否正常、TCP端口是否可达、SELinux/AppArmor是否放行。有些系统上nova-compute没有权限访问libvirt的socket也会出现类似情况。解决方法就是确保nova-compute用户对/var/run/libvirt/libvirt-sock有访问权限。第二类高频率报错是CPU不兼容Target CPU does not provide the required features这个属于硬约束不可强行迁移。解决的临时手段是修改实例的hw_cpu_model为兼容型号后重启实例再尝试迁移长期手段是规划好计算节点分组让相同CPU型号的机器在同一个主机聚合组里并且配置nova的cpu_shared_set或者用host aggregate的cpu_model做匹配。还有一种思路是全局打开libvirt的cpu_modehost-model但要注意这样做之后每台主机的CPU特性会稍微暴露给客户机有一定风险。第三类是经典的热迁移无限循环Migration is stuck for a long time due to memory pressure这种从日志上看可能没有任何报错但迁移卡在migrating阶段几个小时不动。你需要到源主机用virsh migrate --live --verbose手动测试或者用virsh domjobinfo instance查看迁移进度。如果发现memory processed和memory remaining差值一直很大说明脏页速率太高了。处理手段前面已经说过降低业务压力、增加网络带宽、调整QEMU迁移参数比如增大migrate-cache-size、调整downtime-limit、开启multifd。5.2 迁移完成后的隐性坑迁移“成功”不等于万事大吉有几个隐性坑我是在实战中栽过跟头之后才整理出来的。第一个是迁移后网络流量方向不对。这个情况在做VM直接迁移到另一个二层网络节点时容易出现。nova的自动化网络配置一般会把端口绑定到新主机但如果你用的是手动配置的Linux Bridge或者OVS流表没有被正确刷新目标主机上新启动的实例可能还在用旧的网关信息导致外部访问异常。排查手段是登录实例检查网卡配置再到宿主机上brctl show或者ovs-vsctl show看端口绑定状态。第二个是NFS共享存储的缓存不一致。如果你的共享存储是NFS并且启用了一些NFS客户端的cache功能迁移后源主机对同一份文件系统的缓存可能还没释放会导致文件锁问题尤其数据库这类对文件锁敏感的应用。最稳妥的做法是迁移前通知业务方做一次优雅停止有条件的话直接做数据库在线备份宁可做双保险。第三个坑是CPU和内存的QoS参数丢失。老版本nova在迁移时如果实例定义了quota或QoS规格迁移后部分参数可能没同步到目标节点的flavor匹配上导致CPU绑定或者内存预留失效。处理办法是迁移完成后跑一遍openstack server show核对之前的规格重点看hw:cpu_policy、hw:mem_page_size这些extra specs再用virsh vcpupin或virsh memtune对比确认。6. 避坑心得与效率建议6.1 批量迁移的节奏控制单个实例迁移不算复杂批量迁移才是真正考验人的环节。我有一个坚持了很久的原则同时迁移的实例数限制在宿主机CPU核数的一半以内并且每批次之间等待至少10分钟。这个节奏不是凭感觉定的一方面是要给nova-conductor和libvirt留出处理余量另一方面是不要在故障发生时把多个实例同时搞挂。批量迁移还有一种策略很实用叫“滚动迁移”。把所有实例分成几组每组先迁移一两台验证目标节点正常再批量跟进。源节点上只保留最后几台实例等前几台在目标节点稳定运行了再迁最后的。这样即使目标节点有问题影响面也只在一个批次内。6.2 权限控制与操作审计在多团队共用一套OpenStack的环境里迁移权限要控制好。不是所有人都能对任意实例执行迁移操作尤其是热迁移这种可能影响业务连续性的动作。我的建议是在Keystone里单独建一个migration_admin角色只给负责基础设施的同事授权业务团队有迁移需求必须走工单流程。这样出了问题可以随时审计是谁在什么时间迁了哪台实例。审计方面除了sar、systemd journal这些基础记录也可以定期用API收集迁移历史。在这里我总是会在月末巡检时跑一遍所有实例的迁移记录看有没有异常的高频迁移。如果发现某台实例经常被迁移来迁去通常是调度策略有问题或者节点负载不稳定需要提前治理。6.3 从迁移到容灾更高阶的延伸迁移做熟了以后可以把思维再往外延展一下。比如OpenStack本身就提供了基于cinder的卷迁移能力以及基于openstack server rebuild的故障恢复能力。在做容灾方案时迁移是你最常用的武器但不能把它当成唯一方案。真正的容灾需要组合使用实例快照定期备份到不同AZ、数据库主从复制跨节点、关键业务配置自动故障转移。我在管理过的一个集群里除了常规的主机维护迁移还会每季度做一次“演练迁移”——故意模拟一个节点彻底失效在这个节点上的所有实例能不能在目标节点顺利拉起。这个演练会暴露很多参数配置问题比如实例依赖的镜像还在源节点本地而没放进统一镜像服务、或者某些自定义配置没有打进config drive。多演练几次之后你会发现整个平台的健壮性上了一个台阶。迁移动手之前记得先想清楚一个原则能共享存储就别搞本地盘能热迁移就别动冷迁移能批量滚动就别一窝蜂上。技术方案的选型比具体执行更考验功底。做运维最怕的不是迁移失败而是从来不敢迁移——真的到了故障要来临才临时抱佛脚那时候手忙脚乱带来的风险会成倍放大。迁移这件事平时多练、多演、多总结真正上战场时才不会慌。