做OpenStack运维的人,迟早会跟Nova的几十条命令打交道。控制节点挂了可以重建,网络节点挂了可以恢复,但计算节点上的每一台实例,出了问题以后能不能救、怎么救,全靠Nova这一层的操作是否熟练。我刚上手OpenStack那会儿,最头疼的就是实例状态和操作命令对不上号——明明想暂停,却执行了挂起;明明只是关机,却把实例数据弄丢了。后来把Nova的常见操作按“状态-动作”画成一张图之后,整个思路才清晰起来。这篇文章就把我整理过的Nova 16种核心操作完整拆一遍,顺便把每个操作背后的原理、适用场景和踩过的坑都交代清楚。无论你是刚接触OpenStack的运维新人,还是已经扛着几十台计算节点的老兵,这份清单都能帮你少走弯路。
1. 先看全景:Nova实例状态机与16种操作的关系图
1.1 Nova实例的状态:先认识这几个关键节点
很多人用Nova的时候,习惯直接敲命令,根本不看实例当前处于什么状态。这就是问题所在。Nova的每个操作几乎都是“状态依赖”的,实例处于ACTIVE时你能做的大部分操作,到了ERROR状态就全被拒绝。想要真正理解这张操作图,必须先认识几个核心状态。
- BUILD:实例正在创建,调度器刚把它交给某个计算节点,libvirt正在准备磁盘、网络和CPU资源,几秒到几十秒内会转到ACTIVE或ERROR。
- ACTIVE:正常运行,guest OS已经跑起来,对外提供服务。这是实例一生中停留最久的状态。
- SHUTOFF:实例被关机(stop)或者刚从开机状态关机。注意,SHUTOFF不代表实例被删除,它的磁盘文件、内存配置都还在计算节点上。
- PAUSED:暂停,虚机的CPU被冻结,但内存还在物理内存里。这种状态很少见,一般只在排障或临时腾资源时用。
- SUSPENDED:挂起,内存已经被写到宿主机本地磁盘,虚机完全冻结,CPU和内存资源全部释放。
- SHELVED:“搁置”,虚机关机后把系统盘快照上传到Glance,计算节点上的本地文件会被清理,实例几乎不占用计算资源。
- RESCUED:救援模式,原来的系统盘被挂成数据盘,虚机改用救援镜像启动。
- ERROR:出错了。可能是调度失败、镜像拉取失败、磁盘空间不足、网络创建失败等等,一句话,需要人工介入。
- VERIFY_RESIZE:变配后的等待确认状态,需要执行confirm或revert确认变更。
- MIGRATING:迁移进行中,虚机还在源节点或目标节点上运行,但调度和复制流程已经启动。
查看实例状态最直接的方式就是openstack server show <server_id>,里面会显示OS-EXT-STS:vm_state和OS-EXT-STS:task_state两个字段。vm_state是稳定状态,task_state是当前正在执行的操作,这两个字段配合status字段一起看,才能判断实例当前到底在干嘛。
1.2 16种操作与状态转换对应总表
我把最常见的Nova操作按照“操作目的”归成16类,每一类都对应一条或一组命令。下面的表格可以当作日常速查卡来用,也是整篇文章的“地图”。
| 操作分类 | 典型命令 | 状态迁移效果 | 适用场景 |
|---|---|---|---|
| 创建实例 | openstack server create | BUILD → ACTIVE/ERROR | 上线新业务、扩容 |
| 删除实例 | openstack server delete | 任意状态 → 无 | 释放资源、下线业务 |
| 开机 | openstack server start | SHUTOFF → ACTIVE | 恢复关机实例 |
| 关机 | openstack server stop | ACTIVE → SHUTOFF | 计划维护、释放CPU内存 |
| 软重启 | openstack server reboot --soft | ACTIVE → REBOOT → ACTIVE | guest OS可响应时的重启 |
| 硬重启 | openstack server reboot --hard | ACTIVE → HARD_REBOOT → ACTIVE | 系统无响应、内核卡死 |
| 暂停/恢复 | openstack server pause/unpause | ACTIVE ↔ PAUSED | 短时冻结,不落盘 |
| 挂起/恢复 | openstack server suspend/resume | ACTIVE ↔ SUSPENDED | 宿主机休眠级维护 |
| 搁置/恢复 | openstack server shelve/unshelve | ACTIVE ↔ SHELVED | 长期释放计算资源 |
| 锁定/解锁 | openstack server lock/unlock | 状态不变 | 防止误操作 |
| 创建快照 | openstack server image create | 状态不变 | 备份、模板复刻 |
| 重建 | openstack server rebuild | ACTIVE/ERROR → REBUILD → ACTIVE | 系统盘被搞坏时恢复 |
| 救援/取消救援 | openstack server rescue/unrescue | ACTIVE ↔ RESCUED | 进救援系统修配置 |
| 变配 | openstack server resize | ACTIVE → VERIFY_RESIZE | 调整CPU/内存规格 |
| 迁移 | openstack server migrate | ACTIVE/SHUTOFF → MIGRATING | 宿主机维护、负载均衡 |
| 疏散 | openstack server evacuate | ERROR → ACTIVE | 计算节点故障时救命 |
这16类操作基本覆盖了日常运维90%以上的场景。下面我会按“存亡与电源管理”“冻结类操作”“恢复与重建”“迁移与变配”“外部资源联动”五个维度逐个深入拆解。
2. 实例的存亡与电源管理:创建、删除、开关机、重启
2.1 create与delete:实例从哪来,到哪去
创建实例是所有操作的起点。执行openstack server create --flavor <flavor> --image <image> --network <net> <name>后,Nova会先把请求交给调度器(Scheduler),调度器根据flavor的资源约束、AZ(可用域)、宿主机负载等策略,选出一个合适的计算节点,然后通知该节点上的nova-compute去准备虚拟化资源。底层实际操作者是libvirt,它根据镜像和flavor配置生成域(domain)定义,创建虚拟磁盘、虚拟网卡,最后通过KVM/QEMU把虚机拉起来。
创建实例时最容易踩的坑有三个。第一个是flavor的磁盘大小和镜像实际大小不匹配,如果系统盘是qcow2稀疏文件,明明看镜像只有几百MB,创建后却可能膨胀到几十GB,磁盘配额不足就会导致创建卡在BUILD状态。第二个是网络选择,如果指定了错误的网络,虚机起来后网卡一直处于DOWN状态,业务根本无法访问。第三个是忘记指定keypair或密码注入方式,导致创建完成后登不进去。
删除实例openstack server delete远比创建看起来简单,但有几个细节很容易被忽略。默认情况下,删除实例会连同它的系统盘一起清理,如果系统盘是临时盘(ephemeral),数据彻底丢失,无法找回。Cinder数据卷不会自动删除,除非卷创建时设置了delete_on_termination=True。这个属性在创建实例时指定,很多运维习惯把所有数据都放到临时盘上,删完才发现数据全没了,这种教训我见过太多次。生产环境中,删除前一定要先确认是否需要保留系统盘快照,不放心的话就先做个snapshot再删。
2.2 start与stop:关机不等于删除
很多刚接触OpenStack的人会把“关机”理解为“停止服务”,但Nova里的stop操作实际是向实例发送ACPI关机信号,让guest OS正常走系统关机流程。执行openstack server stop后,实例的vm_state会从ACTIVE变成SHUTOFF,CPU和内存资源被释放,宿主机可以腾出资源给其他实例。注意,这只是释放计算资源,实例的磁盘文件仍然留在计算节点的/var/lib/nova/instances目录下。
start操作则是在同一个计算节点上,基于原有的磁盘文件重新把虚机拉起来。它比创建新实例快得多,因为不需要重新准备磁盘和网络,只需要启动QEMU进程加载既有磁盘即可。
这里有一个很容易被误解的点:关机再开机,实例的IP地址会不会变?正常情况下不会。因为实例的虚拟网卡信息和端口(port)是绑定在实例上的,只要实例没被删除,网卡端口就不会被释放,IP也就保持不变。但有一种情况例外:如果实例所在的计算节点故障,你通过evacuate把实例迁移到别的节点,那IP地址虽然在OpenStack层面没变,但guest OS里的网络配置可能对不上,需要手工调整。
关机虽然简单,但有个实际问题:很多应用没有优雅处理ACPI关机的机制,或者guest OS内的acpid服务意外退出了,这时候执行stop,状态会长时间停留在ACTIVE或者任务超时。解决办法是加--os-stop-hard强制关机,或者到计算节点上直接执行virsh destroy <instance>。但virsh destroy属于“暴力断电”,客人机的文件系统可能损坏,用之前一定要确认业务已经停止。
2.3 reboot:软硬之间怎么选
重启是运维用得最频繁的操作。Nova的重启分为软重启和硬重启,命令分别是openstack server reboot --soft和openstack server reboot --hard。
软重启的本质是让guest OS走操作系统自身的重启流程。实现上,libvirt会向虚机发送ACPI reset信号,内核收到信号后正常关闭所有服务、卸载文件系统、然后重新启动。整个过程和你在机器上执行reboot命令几乎一样。它适合系统还能正常响应、需要清理内存缓存或者应用状态时使用。
硬重启则完全不同。它不等guest OS响应,直接由hypervisor层把虚机电源切断再重新通电。QEMU进程被强制重启,CPU状态重新初始化,guest OS会经历一次非正常的断电-通电过程。这种模式适合guest OS完全卡死、网络不可达、软重启一直超时的情况下应急。
选择软硬重启有个基本原则:能软不硬。硬重启有极小概率导致文件系统损坏,特别是在有大量未落盘写入的时候。我遇到过几次实例重启后起不来,最后检查发现是mysql的binlog没落盘,硬重启导致数据文件不一致,只能做InnoDB恢复。所以如果你的实例跑的是数据库这类对一致性敏感的服务,尽量先软重启,软重启超时后再考虑硬重启。Nova默认的重启策略是软重启,如果你确实需要硬重启,记得显式加--hard参数。
3. 冻结类操作:暂停、挂起、搁置、锁定
3.1 pause与unpause:最轻量的冻结
暂停(pause)是一个被很多人忽略但实际很有用的操作。执行openstack server pause后,libvirt调用QEMU的pause命令,把虚机的vCPU停止调度,但整个虚机的内存仍然保留在物理内存中。实例进入PAUSED状态,从guest OS的视角看,就像时间被冻结了,所有进程原地停滞。
这种冻结非常轻量,恢复也极快,unpause之后虚机立刻从上次暂停的位置继续执行。它适合临时让一个高占CPU的服务让出资源,或者做短时间的计算资源腾挪。比如你有几台实例在跑跑批任务,白天占用大量CPU,但不希望直接关机,因为任务进度还在内存里,这时候pause一下,等夜间再恢复,非常合适。
但pause有一个必须注意的致命缺陷:PAUSED状态不会被持久化。如果计算节点突然断电或者nova-compute服务重启,宿主机上的libvirt会重新接管虚机,PAUSED状态大概率会丢失,虚机可能直接恢复运行,也可能进入ERROR。一旦节点故障,你甚至无法确定虚机现在处于什么状态。所以涉及长时间冻结,优先考虑suspend而不是pause。另外,pause不支持跨节点迁移,它是纯粹基于本机物理内存的冻结。
3.2 suspend与resume:内存写到本地盘
挂起(suspend)和暂停(pause)看起来很像,但底层原理完全不同。执行openstack server suspend后,nova-compute会调用libvirt的save操作,把虚机的完整内存状态写入宿主机本地磁盘(默认路径通常是/var/lib/libvirt/qemu/save/),写入完成后虚机进程被终止,资源完全释放。实例状态变为SUSPENDED。
因为内存镜像写到了磁盘上,所以suspend状态是持久的。只要宿主机磁盘没坏,不管nova-compute重启多少次,实例都能恢复。恢复动作openstack server resume直接读取内存镜像文件,加载到内存后继续运行。恢复速度虽然比pause慢(需要读盘),但比reboot快得多,因为磁盘状态完全不用重新初始化。
suspend最适合的场景是宿主机计划内重启。比如你要对某台物理机做内核升级、换硬件、调整BIOS,先把上面所有实例suspend掉,等机器重启完成后再批量resume。我之前维护一批计算节点时,就是用脚本批量遍历实例执行suspend,节点重启后再批量resume,整个过程业务中断时间只有几分钟,远比一台台关系统再开系统快。
使用suspend有个隐藏成本:内存镜像文件占的磁盘空间约等于实例的物理内存大小。如果一台宿主机上跑了20台16GB内存的虚机,同时挂起的话,宿主机本地磁盘会瞬间多出320GB的占用。所以批量挂起前一定要检查宿主机磁盘余量,否则挂了一半磁盘写满,剩下的实例直接卡在SUSPENDING状态。
3.3 shelve与unshelve:数据上传Glance,资源释放
搁置(shelve)是这三种冻结方式里最“狠”的。执行openstack server shelve后,Nova会把实例的系统盘做成镜像上传到Glance,然后把计算节点上的实例文件清理掉,虚机进程和本地磁盘全部消失,只保留镜像和实例元数据。如果还执行了shelve_offload,连计算节点上残留的快照文件也会被清掉,实例在计算节点上的占用量接近于零。
恢复搁置实例要执行openstack server unshelve。Nova会从Glance读取shelve时创建的镜像,重新调度计算节点,重新创建虚机。整个过程相当于用“备份镜像”重新创建了一台同名同ID的实例。
shelve最大的价值在于长期释放计算资源。比如一批测试机周末没人用,直接shelve掉,等周一再unshelve回来,能省下大量内存和CPU资源。如果测试机很多,这个操作可以显著降低宿主机资源压力。
但shelve有个大坑:实例的临时盘(ephemeral)和本地数据盘不会保留。shelve时只上传系统盘,临时盘的内容会丢失。如果你的应用把数据写在本地盘上,shelve之后这些数据就没了。因此,生产实例除非确认所有数据都已经落到Cinder卷或对象存储,否则不建议shelve。另外,unshelve之后实例的IP地址在网络层面会保留(端口还在),但guest OS的配置可能需要重新检查,特别是自定义了网卡配置的镜像。
3.4 lock与unlock:防手滑的保险
锁定(lock)是一个很不起眼但非常实用的操作。执行openstack server lock后,实例处于LOCKED状态,所有修改型操作(reboot、resize、delete、stop、start、rebuild等)都会被Nova拒绝,只有只读操作和网络查看操作可以执行。
这个操作特别适合生产环境。我见过不止一次因为误操作把线上数据库实例给删了或者重启了。在OpenStack里加上lock之后,即使有人拿着管理员的OpenRC环境变量误敲了delete,API也会直接返回错误,相当于给实例上了一道保险。
解锁也很简单,openstack server unlock。如果你是管理员,普通用户锁定的实例你也可以用openstack server unlock --force强制解锁。
但要注意,lock保护的是OpenStack API层面的操作,它挡不住管理员直接登录计算节点执行virsh destroy。换句话说,lock是给“正常人”用的锁,不是给铁了心想搞破坏的人用的。不过在常规运维流程中,给核心数据库、核心业务实例加上lock就是个好习惯,成本几乎为零。
4. 恢复与重建类操作:快照、重建、救援
4.1 snapshot:操作前先留后路
创建快照(snapshot)是Nova里最值得养成的习惯之一。执行openstack server image create --name <snapshot_name> <server>会把当前实例的系统盘制作成一个镜像,上传到Glance,之后可以用这个镜像创建新实例,也可以作为rebuild的底子。
快照的底层原理对qcow2系统盘来说是一个在线镜像合并操作。libvirt会发起一个live snapshot,先创建一个新的qcow2覆盖层(overlay),然后把当前系统的所有写入引导到新层,同时后台将旧层的所有数据合并上传到Glance。这个过程对运行中的虚机影响很小,业务基本无感知。但有一种情况需要注意:如果你的系统盘数据量特别大,比如200GB的数据库系统盘,快照过程会持续很久,期间磁盘IO会明显上升,应用写入性能可能受到影响。
为了保证快照一致性,数据库类实例建议先短暂pause,快照完成后再unpause。这样能避免系统盘在快照过程中有未落盘的脏页。当然,pause会影响业务,需要和业务方确认停机窗口。如果没有停机窗口,也建议在业务低峰期做快照。
快照还有一个非常常见的用途——成为“模板”。我经常通过快照把一台配置好的环境复制成多台测试机,比如初始化好的Web环境、装好agent的监控环境,快照比从头创建省时间得多。快照占用的存储空间和系统盘实际使用量接近,存储不够的话快照很容易失败,所以Glance存储的容量规划也要提前做好。
4.2 rebuild:保留实例身份的系统盘重置
重建(rebuild)是Nova里最“神奇”的操作之一。执行openstack server rebuild --image <image> <server>后,实例会用指定的镜像重新生成系统盘,但实例的ID、IP地址、数据卷保持原样,guest OS收到“系统盘被整个替换”的处理方式。
rebuild的本质是:Nova把实例的元数据保留,重新创建一块新的系统盘(使用指定的镜像),然后把旧系统盘丢弃,虚机用新盘重新启动。这和删除重建完全不同——删除重建会换ID、换IP,而rebuild不会,所以对业务感知来说更像是一次“系统重装”。很多运维会用rebuild来快速恢复被恶意篡改的系统、修复损坏的系统文件、解决启动障碍。
rebuild有一个很关键的参数:--preserve-ephemeral。如果实例有ephemeral盘,默认情况下rebuild会把临时盘也一起清掉,加上这个参数后临时盘数据会保留。但即使加了保留临时盘,系统盘上所有改动也会丢失,所以rebuild前确认系统盘上有没有需要保留的文件,如果没有备份,先做快照再rebuild。
rebuild是最适合“系统盘中毒、配置全乱、内核损坏”这类场景的武器。比如某个实例被勒索软件加密了系统盘,直接rebuild回镜像初始化状态,比慢慢杀毒修复快得多。只要你的数据都在Cinder卷上,rebuild就没什么负担。
4.3 rescue:进救援模式修系统
救援(rescue)是Nova里最被低估的操作。执行openstack server rescue后,Nova会把当前实例关机,然后用一个指定的救援镜像(默认使用实例原本的镜像)启动一个“救援实例”,原实例的系统盘作为数据盘挂载到救援实例上。此时原实例状态变为RESCUED,你可以通过VNC或者SSH登录救援实例,然后挂载原系统盘,去修复里面的文件。
这个场景太适合“启动不了”的实例了。比如某个实例开机直接进入紧急模式,grub损坏,或者关键系统服务起不来,你可以rescue进去,mount原盘,删掉有问题的配置文件、修复fstab、重装引导,然后执行openstack server unrescue,原实例会恢复为ACTIVE状态并再次尝试启动。
rescue的默认行为有几个细节需要注意。第一,rescue后的救援实例和原实例共享同一个计算节点,网卡是新建的,IP会变,你需要通过openstack server show查看救援实例的IP地址,再去登录。第二,原实例的系统盘挂载为数据盘,路径通常是/dev/vdb或/dev/vdc,具体可以执行lsblk查看。第三,如果实例处于ERROR状态,有时无法直接rescue,需要先确认是否能被nova-compute识别。
救援模式是我在OpenStack排障里用得最多的功能之一。很多人遇到实例启动失败,第一反应是删了重建,但这样会丢失IP和本地配置。其实先用rescue进去看一眼,十有八九能救回来。
5. 迁移与疏散类操作:migrate、evacuate、resize
5.1 migrate:冷迁移与热迁移的取舍
迁移(migrate)是OpenStack运维绕不开的话题。openstack server migrate默认执行冷迁移,它要求实例处于SHUTOFF状态,Nova会将实例的磁盘文件从源计算节点复制到目标计算节点,然后在目标节点重新启动。整个过程业务是中断的,但数据完整性最有保障。
热迁移(live migrate)则完全不同,命令是openstack server migrate --live <target-host>(或者通过nova live-migration操作)。热迁移基于KVM原生的live migration能力,先把源节点的实例内存状态持续复制到目标节点,当双方内存数据达到同步后,瞬间切换网络和磁盘IO,业务几乎无感知。整个过程中虚机不关机、不中断,非常适合数据库、在线交易这类不能停的服务。
热迁移的工作机制是:QEMU通过内存预复制(pre-copy)流程,循环迭代地把源节点虚机的内存页面复制到目标节点,同时跟踪脏页,直到剩余脏页足够小,再执行停机拷贝(stop-and-copy),最后在目标节点恢复运行。如果你的实例内存写入非常频繁(比如每分钟几百MB的写入),脏页迭代可能永远追不上,热迁移直接挂在MIGRATING状态。
热迁移还有两个关键前提。第一,源节点和目标节点必须能访问同一个系统盘文件,最典型的就是共享存储(Shared Storage),比如把系统盘放在Ceph或者NFS共享目录上。没有共享存储时,Nova会启用块迁移(block migration),把本地磁盘也一并复制过去,复制大磁盘会让迁移时间变得不可控。第二,网络必须互通,虚机的虚拟网卡需要能在目标节点上正常挂载到同一张网桥或虚拟交换机。
我做热迁移时最深的体会是:迁移前一定先压测或观察实例的内存写频率。如果instance的脏页率太高,热迁移很可能永远完不成,这时候宁可停机做冷迁移,也别一个任务挂在MIGRATING上熬到半夜。另外,热迁移完成后记得检查实例的新宿主,有时因为目标节点资源不足,虚机被调度到意料之外的节点,业务架构的拓扑就变了。
5.2 evacuate:计算节点宕机时的救命操作
疏散(evacuate)是所有OpenStack运维最希望永远用不上、但必须熟练掌握的操作。当某台计算节点物理宕机或网络隔离时,它上面运行的所有实例都会进入ERROR状态,里面的虚机实际上已经“死亡”了。普通Cold Migrate和热迁移都要求源节点能正常通信,一旦源节点失联,唯一恢复实例的方法就是evacuate。
openstack server evacuate --host <target-host> <server>会通知nova-scheduler在其他可用计算节点上重新启动该实例。注意,evacuate不是迁移,它默认不会保留原来的系统盘数据——除非你用了共享存储。如果系统盘放在Ceph等共享存储上,新节点可以直接挂载同一份数据,业务恢复后数据完好无损。如果系统盘是本地盘,源节点都挂了,本地数据根本读不出来,evacuate后实例会从镜像重新创建系统盘,本地数据等于全部丢失。
这就是OpenStack架构设计里一个非常重要的取舍:生产环境的系统盘和数据盘到底放本地还是共享存储。我的建议是,核心业务一定要走共享存储在Ceph上,因为只有这样才能在计算节点故障时做到快速恢复。如果为了省钱把系统盘全放本地,一旦宿主机坏了,只能和不完整的数据说再见。evacuate的恢复时间取决于镜像大小、目标节点资源、网络复制速度,但一般来说比重建快得多,操作得当几分钟内就能恢复服务。
evacuate有一个常见的坑:如果实例配置了admin_pass或者自定义了密码注入,evacuate后可能需要重新通过VNC设置密码,新节点上的实例可能和旧实例的guest OS状态不一致。所以每次evacuate后,建议第一时间检查实例的启动日志(console log)和网络连通性,确认服务真正恢复,再切换流量。
5.3 resize:变配不只是改flavor
变配(resize)是Nova里最容易出问题的操作之一。openstack server resize --flavor <new_flavor> <server>会把实例迁移到能承载新flavor的计算节点(也可能是同一台节点),然后重新定义虚机的CPU、内存和磁盘大小。如果新flavor的磁盘比原来大,系统盘会被扩容;如果比原来小,系统盘文件不变但可能会有多余空间无法利用。
resize完成后,实例会进入VERIFY_RESIZE状态,你需要手动执行openstack server resize confirm确认变更,或者openstack server resize revert回滚到原来的flavor。这里有个非常关键的时间窗口:如果设置了resize_confirm_window(比如24小时),超过该时间后Nova会自动confirm;如果没有设置,实例可能一直停留在VERIFY_RESIZE状态,直到你手动确认。
resize最坑的一点是:默认情况下,resize会执行冷迁移,意味着实例会关机,业务中断时间可能在几分钟到几十分钟不等。你需要在变配窗口内完成全部操作。如果业务不允许中断,可以考虑支持在线变配的版本或使用专门的缩扩容方案,但OpenStack社区版本默认不提供CPU/内存热插拔。变配之前,强烈建议先做快照,因为resize过程如果失败,原实例的磁盘文件可能被重新调度到别的节点,恢复起来非常麻烦。
我在生产环境变配时有一个固定流程:先看新flavor的磁盘大小是否足够,再看实例当前所在宿主机的资源是否满足新flavor(有时候Nova不会迁移,直接原地调整),最后执行resize,等VERIFY_RESIZE状态后检查虚机状态和业务,确认无误再confirm。如果resize后业务异常,就revert回滚。这个流程虽然保守,但从来没出过事故。
6. 与外部资源联动的操作:卷挂载与浮动IP
6.1 attach与detach volume:数据盘怎么接
实例只有一块系统盘很多时候不够用,Cinder卷(云硬盘)就派上用场了。Nova这里对应的操作是attach和detach。命令分别是openstack server add volume <server> <volume>和openstack server remove volume <server> <volume>。
把Cinder卷挂到实例上之后,它在guest OS里的表现就是一块新的块设备,比如/dev/vdb或/dev/vdc。系统不会自动分区、不会自动格式化、也不会自动挂载到某个目录,这一切都需要你进入实例手动完成。很多新手挂载完卷之后以为直接就能用了,结果lsblk一查发现根本没出现,这是因为块设备需要在系统层面做分区、格式化、挂载。我会在卷挂载完成后用lsblk确认设备已识别,然后mkfs.ext4 /dev/vdb格式化(如果新卷),再mkdir /data && mount /dev/vdb /data,如果需要开机自动挂载还要配置 /etc/fstab。
如果挂载的是启动卷(bootable volume),你可以直接用这个卷作为实例的系统盘启动。这种情况在需要从快照卷恢复数据或者更换系统盘时特别有用。
detach操作看起来简单,其实是个危险动作。如果guest OS还在读写这个卷,直接detach会导致文件系统损坏或者IO错误。正确的流程是先登录实例,执行umount卸载挂载点,确认没有进程占用该设备,然后再在OpenStack侧执行remove volume。如果实在无法登录实例,但又要强制下线数据盘,需要在Nova侧强制解绑,这种做法有数据损坏风险,不到万不得已不要用。
卷挂载还有一个常见问题:挂载了多块卷之后,设备名在重启后可能发生漂移。比如 /dev/vdb 重启后变成了 /dev/vdc。这是因为Linux内核枚举设备的顺序不完全固定。生产环境我建议通过UUID或者标签(LABEL)来挂载设备,不要直接写死/dev/vdb,这样可以避免启动后挂载失败的尴尬。
6.2 浮动IP的关联与解绑
浮动IP(Floating IP)是OpenStack里让外部网络访问实例的标准方式。实例默认可能只有内网IP,外部无法直接访问。执行openstack server add floating ip <server> <floating_ip>就把一个公网IP绑定到实例上,解绑是openstack server remove floating ip <server> <floating_ip>。
浮动IP的本质是iptables的DNAT规则,在Neutron的路由节点或虚拟路由器上,把浮动IP的流量映射到实例的内网IP。实例自身感知不到浮动IP的存在,它的网卡配置依然是内网IP。所以解绑浮动IP后,实例的内部网络、内网服务完全不受影响,只是外部无法再通过那个公网IP访问它。
浮动IP关联和云主机本身的“多网卡配置”不要混淆。如果你需要多块网卡、多个内网IP,应该创建多个端口(port)然后附加到实例上。浮动IP只是外网出入口的映射,和网卡数量无关。
实际运维中,我经常用浮动IP做“故障切换”。比如一台Web实例挂了,我可以先把浮动IP从故障实例解绑,再绑定到备用实例上,实现秒级切换。这个操作比改DNS快得多,非常适合对中断时间敏感的场景。要注意的是,浮动IP解绑后,原来实例的外网连接会立即断开,如果业务里有长连接(比如数据库的外网连接池),全部重连的成本也要考虑到。
7. 实战速查:故障场景操作组合与高频坑位
7.1 典型运维场景的操作组合
Nova的16种操作很少单独使用,实际运维中经常是组合拳。我挑几个高频场景,把操作串联起来讲一遍。
场景一:宿主机计划维护。比如要对计算节点做内核升级。正确做法是先把该节点上所有实例都执行openstack server migrate --live热迁移出去,如果热迁移条件不满足或者实例状态不健康,就改成suspend,等节点维护完再批量resume。这里的决策顺序是:先看共享存储是否可用,再看实例是否允许短时间暂停。热迁移最推荐,因为它对业务影响最小;没有共享存储时才退而求其次用suspend。
场景二:实例系统盘被入侵或损坏严重。首先执行openstack server image create创建快照留作证据或后续分析,然后执行openstack server rebuild --image <原镜像>快速恢复。如果rebuild后还是起不来,再考虑openstack server rescue进入救援模式修复。这个顺序比较重要,先保存现场,再快速恢复,最后才深入修复。
场景三:实例负载持续增长,需要扩大规格。执行openstack server resize --flavor <大规格>,实例进入VERIFY_RESIZE状态后,检查业务是否正常,确认无误再执行openstack server resize confirm。如果异常,执行openstack server resize revert回滚。变配之前先做snapshot,整个过程避免在业务高峰期进行。
场景四:计算节点宕机。先确认节点确实失联,然后用openstack server evacuate --host <其他节点> <server>把实例一一疏散。如果系统盘在共享存储上,数据不会丢;如果系统盘在本地,要有数据丢失的心理准备。疏散完成后,检查实例的console log和网络连通性,确认服务恢复后再把流量切回。整个过程中,可以利用浮动IP解绑/绑定做流量切换。
7.2 高频问题排查速查表
| 故障现象 | 可能原因 | 排查与解决办法 |
|---|---|---|
| 实例一直停留在BUILD状态 | 镜像过大、资源不足、调度失败 | 检查计算节点的可用内存/CPU,查看nova-compute日志,确认镜像下载是否完成 |
| 关机(stop)后状态仍为ACTIVE | guest OS没有响应ACPI关机信号 | 检查guest内acpid服务,或者用--os-stop-hard强制关机 |
| 实例状态为ERROR但task_state为空 | 磁盘空间不足、镜像损坏、网络插件失败 | 查看nova-compute和neutron日志,确认是否磁盘满,清理空间后重置状态 |
| pause之后计算节点重启,实例状态异常 | PAUSED状态不持久化 | 之后尽量用suspend替代pause做长时间冻结 |
| resize后忘记confirm,实例卡在VERIFY_RESIZE | resize_confirm_window未设置或还没到超时 | 手动执行openstack server resize confirm,或者根据业务情况revert |
| 热迁移长时间卡在MIGRATING | 内存脏页率过高,迭代无法收敛 | 停止高写入负载,等待迁移完成;或者取消迁移,改为冷迁移 |
| evacuate后实例数据丢失 | 系统盘放在本地盘而非共享存储 | 排查源节点是否能恢复数据,不能恢复只能从镜像重建,后续建议系统盘迁移到共享存储 |
| 挂载卷后实例内看不到设备 | 卷挂载成功,但guest OS未识别或未分区 | 进入实例执行lsblk检查,新卷需要分区、格式化、挂载 |
| 快照成功但新实例创建失败 | 快照时系统盘不一致或镜像元数据损坏 | 检查Glance镜像状态,尝试重新创建快照,或者基于快照做rebuild验证 |
| lock之后还能被强制删除 | 管理员用了--force解锁 | 生产环境通过RBAC权限控制,限制普通用户对核心实例的管理权限 |
7.3 最后再分享两个经验
写到这里收尾之前,我还是想多说几句个人体会。第一个是关于操作习惯。我见过太多人在OpenStack上直接敲命令,完全不管当前实例状态,结果就是各种奇奇怪怪的操作冲突。其实Nova的命令设计得很“讲道理”:大多数操作都有前置状态要求,你只要在操作前执行openstack server show看一眼状态,绝大多数事故都能避免。我自己现在养成了习惯,凡是生产实例,操作前必看状态和task_state,宁可多花五秒查看,也不愿花五小时处理误操作。
第二个是关于镜像和备份的执念。Nova再强大,也挡不住存储层面的物理故障和逻辑错误。我的原则是:所有核心实例至少保留最近一份快照,所有数据卷定期做Cinder备份,所有配置变更前先出快照。这个习惯救了我太多次,甚至有一次整台计算节点的磁盘阵列故障,我硬是靠前一天晚上的快照把十几台实例全部恢复到了可用状态。Nova的16种操作只是工具集,真正的安全垫永远是备份意识和纪律性。希望这篇整理能帮你把工具用熟,也把备份的习惯刻进肌肉记忆里。