这个系列写到第 64 篇,刚好走到 NFS Volume Provider 的第三部分。前面两部分我们把 NFS 后端的基本原理和第一遍跑通流程讲完了,这一篇我打算做两件事:一是把 NFS 后端在实际环境中会用到的进阶配置、性能调优思路和排障细节一次聊透,算是给这个主题收个尾;二是顺势把 Neutron 网络的基本概念梳理一遍。为什么把这两块放在一起?因为我自己的实践经验是:存储和网络在 OpenStack 里从来都是连着的,NFS 卷最终要挂载到云主机里用,而云主机能不能被访问、能不能正常对外提供服务,取决于网络怎么规划。存储决定了数据活不活,网络决定了数据通不通,这两件事必须放在同一个认知框架里。
如果你是刚接触 OpenStack 的运维新手,这篇文章可以当作从存储后端走向网络规划的一个过渡;如果你已经在维护生产环境,那第三部分里关于多后端、共享存储挂载参数和网络层排障的内容,应该能帮你补上一些平时容易漏掉的细节。
1. NFS Volume Provider 的核心逻辑与选型考量
1.1 NFS 后端到底在做什么
先回到最基础的问题:Cinder 的 NFS 后端,本质上就是把一台 NFS 服务器上的共享目录,伪装成块存储设备提供给云主机使用。听起来有点绕,但拆开看就清楚了。
虚拟机需要一个磁盘,这个磁盘在 OpenStack 里表现为一个卷(Volume)。普通的块存储后端,比如 LVM,是真的在存储节点上划出一块逻辑卷,通过 iSCSI 协议或者其他方式把这块逻辑卷暴露给计算节点,计算节点再把它当成一块硬盘挂给虚拟机。NFS 后端不是这样做的——它先走文件系统这层:Cinder 的 Volume 服务进程在挂载了 NFS 共享的目录里创建一个 qcow2 镜像文件,然后用 qemu 的抽像驱动去读取这个文件。对虚拟机来说,它看到的确实是一块完整的磁盘,但这块磁盘背后其实是一个文件。
用文件当磁盘,好处很多。创建卷特别快,因为它实际上是复制一个镜像文件;克隆和快照几乎不用额外写代码,cp 一下就能搞定;每个卷占用的空间按需增长,可以超量分配。但在生产环境里,NFS 后端的性能上限受制于网络和文件系统的吞吐能力,不像本地 LVM 那样能直接吃到物理磁盘的极限性能。理解这一点很重要,因为你决定用不用 NFS 后端,本质上是在做一笔取舍:要灵活、管理简单,还是极致性能。
1.2 三选一:NFS、LVM 还是 Ceph
| 对比维度 | LVM 后端 | NFS 后端 | Ceph 后端 |
|---|---|---|---|
| 部署复杂度 | 低,单机就能用 | 中,依赖共享文件系统 | 高,需要独立集群 |
| 扩容方式 | 加硬盘、扩 VG | 加共享目录即可 | 加 OSD 节点 |
| 性能表现 | 最好,本地块设备 | 取决于网络和文件系统 | 中高,多副本写放大 |
| 高级特性 | 快照、克隆支持有限 | 快照、克隆灵活 | 快照、克隆、分层齐全 |
| 适用场景 | 测试环境、单节点 | 小规模生产、多节点共享 | 大规模生产云平台 |
LVM 后端适合你只有一台控制节点兼存储节点的场景,装完就能用,性能最好,但卷无法跨节点共享,做不到“任意计算节点都能挂载同一块卷”这种效果。Ceph 是生产环境的长期方案,可靠性和性能都很好,代价是部署和维护成本很高,而且对网络要求非常苛刻。NFS 在这三者中间是一个微妙的平衡点:它几乎能模拟出共享存储的能力,多个计算节点都可以访问同一块卷,但配置难度比 LVM 稍高,性能又没到 LVM 的水平。
我在测试环境里通常会先用 NFS 后端,原因很朴素:我只需要一台存储服务器加一个共享目录,配置一个 NFS 服务,然后告诉 Cinder“这块区域归你托管”,就能获得跨节点共享存储能力。这在做虚拟机热迁移、卷在线迁移这类实验时特别实用,不用为了个实验去搭一套 Ceph 集群。
2. NFS 底层环境实操:服务端导出规则与客户端挂载
2.1 服务端目录规划与 export 规则
搭 NFS 后端,第一步是规划好共享目录。我建议把它单独放在一块数据盘上,不要和系统盘混在一起,因为 Cinder 卷文件会持续增长,一旦根分区被写满,系统会出大问题。
我的做法是在存储节点上挂载一块独立数据盘,格式化后建一个目录,比如 /data/cinder,然后编辑 /etc/exports:
/data/cinder 10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check)这里几个参数我逐个说明一下。rw 是读写权限,这个不用废话。sync 是同步写模式,也就是服务端要等数据真正落盘才返回确认,对虚拟化环境来说能减少数据丢失的风险,值得保留。no_root_squash 是最容易被新手忽略的参数——当你作为客户端去请求这个共享时,默认 NFS 会把 root 用户映射为匿名用户,导致 Cinder 的卷文件可能无法正常读写,所以必须禁掉 root squashing。no_subtree_check 能减少目录层级校验带来的性能损耗,在共享目录下有大量卷文件时尤其重要。
我踩过一次坑:某次配置时为了图方便,exports 写成了 *(rw,sync),没有限制网段。虽然实验室里没出事,但后来想想确实不合理。Cinder 节点和存储节点之间应该用独立的管理网络或者存储网络,exports 里要精确匹配这个网段,不要用通配全开放。
改完 exports 后执行 exportfs -rav 让配置生效,然后用 showmount -e 检查共享是否正常可见。这一步是在服务端侧验证,后续客户端挂载出问题也能先排除服务端因素。
2.2 客户端挂载参数逐个抠
Cinder 卷服务所在的节点(通常是控制节点或专门的存储节点)需要把这个共享目录挂到本地。Cinder 官方默认的挂载点是 /var/lib/cinder/mnt,但里面实际路径是由你配置的 nfs_mount_point_base 决定的。
我在客户端挂载时用的命令类似这样:
mount -t nfs 10.0.0.10:/data/cinder /var/lib/cinder/mnt/data -o rw,vers=4,nofail,noatime,hard,timeo=600,retrans=3挂载参数这里重点说两个。vers=4 是 NFSv4 协议,相比 v3 在多线程并发和锁处理上更好,是虚拟化环境更推荐的选择。noatime 可以关闭文件访问时间的更新,减少不必要的写操作,对性能有实际改善。hard 参数表示如果 NFS 服务端暂时不可用,客户端会不断重试而不是报错退出,这确保了虚拟机在存储抖动时不会直接崩溃。
nofail 也很重要,服务器重启后即使 NFS 没来得及挂上,系统也能正常启动,不会卡在开机阶段。为了让挂载持久化,要写进 /etc/fstab:
10.0.0.10:/data/cinder /var/lib/cinder/mnt/data nfs rw,vers=4,nofail,noatime,hard,timeo=600,retrans=3,_netdev 0 0注意 _netdev 参数,它告诉系统等网络就绪后再挂载,避免开机时因网络未初始化导致挂载失败。
挂载完成后用 df -h 确认,这里有个判断标准:如果挂载成功,Cinder 卷服务对这块目录的读写权限必须是完整的。我通常还会用一个简单的方法验证:在共享目录下创建文件、删除文件、修改属主,一套操作下来没问题再继续。
2.3 目录结构与多节点共享
如果你的环境中运行了多个 Cinder 节点,比如做控制节点高可用,每一个节点都必须独立挂载同一个 NFS 共享目录。挂载点路径要一致,否则卷信息会找不到。
Cinder 在 NFS 共享平台上按项目维护卷的目录结构。默认情况下,它会在共享根目录下创建以卷 ID 命名的目录,里面放卷文件和管理元数据。这种结构意味着多个节点共享同一个共享目录时,它们彼此能看到对方创建的卷,这正是共享存储的价值所在。
但这里有个容易踩坑的点:NFS 服务端如果启用了 root_squash,而多个节点挂载后,各自用 root 身份创建的文件拥有者会变成 nobody,时间一长会导致某个节点无法删除或覆盖其他节点创建的文件。所以一定要确保 no_root_squash 生效,并且如果 NFS 服务端有自己的匿名用户映射配置,最好统一一下 uid/gid。多节点部署时,建议在服务端为 NFS 目录设置一个固定的属主用户,所有客户端都使用 root 挂载,这样权限模型最简单,出问题也好排查。
3. Cinder 接入 NFS:配置项拆解与卷生命周期验证
3.1 cinder.conf 里的关键配置
当底层 NFS 就绪后,接下来要让 Cinder 认识这个后端。修改 /etc/cinder/cinder.conf,把启用后端、驱动名称、共享目录配置、挂载参数这些信息填进去。
配置如下:
[DEFAULT] enabled_backends = nfs volume_clear = zero volume_driver = cinder.volume.drivers.nfs.NfsDriver nfs_shares_config = /etc/cinder/nfs-shares.conf nfs_mount_point_base = /var/lib/cinder/mnt nfs_mount_options = vers=4,noatime,hard,timeo=600,retrans=3这里 volume_driver 指定了使用 NFS 驱动,nfs_shares_config 指向一个文件,里面按行列出所有允许使用的 NFS 共享,格式很简单,就一行一个 IP:/path。我自己会把这个文件命名为 nfs-shares.conf,内容如下:
10.0.0.10:/data/cindernfs_mount_options 里的值会传给系统 mount 命令。你会发现我在这里没有重复写 rw,因为 Cinder 会默认加上读写参数。把这些配置写完,重启 cinder-volume 服务:
systemctl restart cinder-volume重启后立刻检查日志,路径通常是 /var/log/cinder/cinder-volume.log。如果配置正常,你能看到类似“NFS driver is ready”或“Connected to NFS shares”的日志。如果出现挂载失败,就去看 /var/log/syslog 里的 mount 记录,这能帮你快速定位是网络问题还是权限问题。
3.2 多后端部署与卷类型绑定
当环境里同时有 NFS 和 LVM,甚至还有 Ceph 时,就需要启用多后端,并把不同后端映射到不同卷类型,这样用户创建卷时可以根据卷类型字面选择存储位置。
多后端配置方式是在 cinder.conf 里把 enabled_backends 写多个名字:
[DEFAULT] enabled_backends = nfs-fast,lvm-default [nfs-fast] volume_driver = cinder.volume.drivers.nfs.NfsDriver volume_backend_name = NFS ... [lvm-default] volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver volume_backend_name = LVM ...配置好后用 cinder-manage service list 查看服务状态,再用 openstack volume type 创建对应卷类型:
openstack volume type create nfs-fast openstack volume type set nfs-fast --property volume_backend_name=NFS这里的 volume_backend_name 必须和 cinder.conf 中对应后端的 volume_backend_name 完全一致,否则调度器会找不到匹配的后端。这个机制本质上是一种标签匹配:卷类型上贴了“NFS”标签,调度器就只把卷调度到贴有同样标签的后端上。
多后端的好处不止是后端隔离。你可以为不同业务提供不同性价比的存储:测试业务放在本地盘 LVM 上,高可用业务放在 NFS 共享上,冷数据放在更大容量的后端。生产环境里这种“按类型选后端”的模式几乎成了标配。
3.3 创建卷与挂载验证
配置完成后,执行创建卷操作验证整个链路:
openstack volume create --size 10 --type nfs-fast my-nfs-vol openstack volume show my-nfs-vol正常情况下,卷状态会从 creating 变成 available。此时到 NFS 服务端的共享目录里看一眼,能看到生成一个以卷 ID 命名的目录,里面有卷文件。如果卷一直停留在 creating,大概率是调度失败或挂载异常,需要去 cinder-volume 日志里找原因。
把卷挂载到云主机再验证读写:
openstack server add volume my-instance my-nfs-vol挂载完成后,到云主机里执行 lsblk 确认新磁盘存在,格式化并挂载文件系统,写入一个测试文件。如果能正常写入和读取,说明这块 NFS 卷已经真正进入了可用状态。
这里有个细节:NFS 卷通常默认是精简置备(thin provisioning)的,意味着创建 10G 卷不会立刻占满 10G 物理空间,而是按实际写入量增长。这种机制提升了存储利用率,但也带来一个风险——如果 NFS 共享空间不足,卷在使用过程中会因为磁盘空间耗尽而触发 I/O 错误。因此在生产环境里,必须给 NFS 共享设置容量监控,当使用量超过阈值时要提前扩容或清理。
4. Neutron 网络基本概念:从物理网卡到虚拟网络的抽象
4.1 Neutron 在网络栈里的位置
存储后端配置完了,接下来要面对另一个问题:云主机创建出来了,磁盘也有了,但它怎么上网?这就要靠 Neutron。
Neutron 在 OpenStack 里负责的是“软件定义网络”这一层。我们可以认为每个租户都拥有一套编排好的虚拟网络环境,这台虚拟网络环境里有网线、交换机、路由器、防火墙等组件,但它们全都是用软件模拟的。管理这些资源的组件,就是 Neutron。
Neutron 的核心价值在于多租户隔离。两个项目(租户)可以创建都叫“net1”的网络,它们互相完全不可见、二层三层都不会互通。这种隔离是通过网络命名空间、VXLAN/VLAN 等技术实现的。你可以把它想象成在同一栋大楼里施工了无数套互相独立的内部电话线路,每套线路只服务自己的租户。
Neutron 的组件不算少,但基本逻辑就三条线。核心插件管理网络资源;L2 Agent 负责在计算节点上实现二层通信;L3 Agent 负责路由和地址转换。这三条线解决了虚拟网络的“通、隔离、出网”三大问题。
4.2 Provider 网络与 Self-service 网络
最常被混淆的概念就是 Provider 网络和 Self-service 网络。我先用一句话概括:Provider 网络直接映射到物理网络,Self-service 网络是物理网络之上叠加的虚拟网络。
创建 Provider 网络时,需要指定 physical_network 名称,对应到计算节点上的物理网卡,还要指定 network_type,常见的是 flat 和 vlan。flat 类型意味着这个虚拟网络直接桥接到物理交换机的某个 VLAN 里,不设 VLAN ID;vlan 类型则需要指定 vlan_id,数据包在物理链路上就会打上对应 VLAN 标签。这种网络没有 DHCP 服务,也不经过 L3 Agent,它的 IP 地址是直接由外部 DHCP 或手动分配的。
Self-service 网络则是租户自己的私有网络,常见类型是 vxlan 或 gre。VXLAN 通过 VNI 来隔离,VNI 可以理解成比 VLAN 更大的 ID 空间,支持上千万个隔离网络。计算节点上跑着一个叫 L2 Agent 的进程,它会在每个计算节点上建立 VXLAN 隧道端点,把数据包封装成 UDP 报文在网络里传递。这个隧道机制让两个计算节点上的虚拟网卡看起来像是在同一个二层网络里,即使它们物理上在不同的交换机端口上。
这两类网络用不用隔离也可以描述清楚:Provider 网络不隔离租户,适合需要外部直通的场景;Self-service 网络租户间自动隔离,适合大多数云环境内部应用。二选一或两者混用都常见。
4.3 端口、子网与 DHCP 三件套
Neutron 网络体系里有三个基础对象:网络、子网、端口。网络是容器,子网是网段和地址池的集合,端口是网卡接入网络的接口。
创建子网时,必须指定网段和网关,例如 192.168.100.0/24,网关 192.168.100.1。Neutron 会从这个网段里自动分配 IP 给云主机,分配逻辑由 DHCP Agent 完成。DHCP Agent 在每个子网所在的网络里启动一个 dnsmasq 进程,这个进程负责响应 IP 地址租约请求。它分配的地址范围可以被地址池的 allocation_pool 限定,比如默认只分配 192.168.100.10 到 192.168.100.100,剩下地址保留给固定 IP 或特殊用途。
端口这个对象需要特别关注。当你创建云主机时,Neutron 会自动为它的网卡创建端口,并分配 MAC 地址和 IP 地址。端口上的安全组策略和负载均衡的成员绑定都是基于端口来做的。
关于 DHCP 有个常见疑问:云主机是虚拟机,怎么知道自己该向谁要 DHCP?答案就在于 Neutron 会在每个有子网的网络上放置一个 DHCP 命名空间,里面跑着 dnsmasq。计算节点上的虚拟网桥把 DHCP 请求广播到该命名空间,dnsmasq 响应后完成租约分配。
4.4 路由、安全组与南北向流量
Self-service 网络里的云主机要出外网,必须经过路由器。Neutron 中的路由器是一个虚拟路由器,概念上和家里用的路由器一样,但它运行在命名空间里,配置一条连接外部网络接口和内部子网接口,路由就完成了。
路由表的项由 Neutron 自动维护。当子网添加到路由器后,L3 Agent 会在路由命名空间里创建对应的接口,并从该子网中占用网关 IP。这意味着网关 IP 通常不可由云主机使用,配置子网时需要注意避免把网关 IP 划入地址池。
南北向流量处理的核心是 SNAT 和 FIP。当内部私有网络里的云主机访问外部时,路由器会执行源地址转换,把私有 IP 转换为外部网络上的 IP。因为外部不能直接访问内部私有 IP,就必须给云主机绑定一个浮动 IP。浮动 IP 是外部网络中的合法地址,它会被映射到云主机的私有地址,外部流量借此找到目标。这个过程有点像办公室内线电话通过总机拨出,外头只知道总机号码,不知道内部分机号。
安全组是另一个必须理解的重点。安全组是一组规则,类似云环境里的防火墙。它绑定在端口上,默认动作是拒绝所有入流量、放行所有出流量。你需要显式添加规则来开放端口,例如允许 80 端口的 TCP 入流量。安全组最容易被忽略的是它是有状态的:如果你主动向外发起了连接,那么这条连接的响应流量会被自动放行,不需要额外规则;但别人主动连入的连接则必须匹配入方向规则才允许通过。生产环境里最常见的网络故障之一,就是安全组忘了放行对应端口。
5. 实战中的坑:NFS 与 Neutron 排障速查
5.1 NFS 卷创建失败的典型场景
我在多次部署中遇到过几类 NFS 卷创建失败的典型情况,整理成一张排障表。
| 现象 | 可能原因 | 排查方式 | 处理建议 |
|---|---|---|---|
| 卷一直处于 creating | cinder-volume 服务未重启 | 看日志,执行 cinder-manage service list | 重启服务,确认后端起来 |
| 卷创建成功但无法挂载 | NFS 共享权限不足 | 检查共享目录是否 no_root_squash | 修正 exports 并重新挂载 |
| 卷挂载后 I/O 报错 | NFS 服务端空间耗尽 | df -h 查看共享目录使用率 | 扩容或清理 |
| 卷状态为 error | 调度器找不到匹配后端 | 检查卷类型的 volume_backend_name 是否匹配 | 对齐标签 |
| 性能很差,写速度慢 | NFS 版本不是 v4 | mount 参数里确认 vers | 升级到 v4,追加 noatime |
最典型的场景是创建卷时调度失败。很多时候配置 NFS 后只重启了 API 服务,忘了重启 cinder-volume,调度器就会因为后端未就绪而报错。此时在 cinder-volume 日志里能看到 scheduler 或 driver 相关的异常,重启服务后问题通常立刻消失。
另一个很容易踩的坑是同时运行了多个 Cinder 节点,共享目录挂载点不一致。例如一个节点挂载在 /var/lib/cinder/mnt/data,另一个节点挂载在 /var/lib/cinder/mnt/vol,Cinder 会认为自己看到的是两个不同的后端,调度逻辑会变得混乱。解决办法是统一所有节点的挂载路径和共享配置。
5.2 NFS 性能调优的思路
NFS 后端的性能主要受三块因素影响:网络带宽、文件系统类型、挂载参数。
网络这一层,建议把存储网络和业务网络分开。如果虚机流量、管理流量、存储流量挤在同一块物理网卡上,NFS 的读写会被其它流量干扰,性能波动会很明显。条件允许时使用独立的万兆网卡直连存储节点,这个改善效果立竿见影。
文件系统层面,NFS 共享目录所在的分区尽量使用 XFS 格式。XFS 在大文件和高并发读写场景下的表现比 ext4 更稳定,而且支持较大的卷文件。我在测试中对比过 ext4 和 XFS 下的 NFS 卷读写速度,XFS 在大量并发小文件场景下的性能优势比较明显。
挂载参数层面,建议加上 noatime,并考虑使用 lookupcache 和 actimeo 这类缓存参数。NFS 客户端的缓存机制对读性能影响不小,适当放宽缓存时间可以减少不必要的网络交互。例如:
mount -t nfs 10.0.0.10:/data/cinder /var/lib/cinder/mnt/data -o vers=4,noatime,hard,nolock,actimeo=30注意 nolock 参数在某些内核版本里与 NFSv4 不兼容,使用时要先验证,不确定时宁可不加。性能调优本身就是一个持续验证的过程,建议调完参数后跑一轮 fio 测试,对比调参前后的读写吞吐和时延再下结论。
5.3 Neutron 网络不通排查顺序
Neutron 网络故障的排查思路和物理网络很像,我自己总结了一套顺序,按这个顺序排查能省很多时间。
第一步,确认最终目标:云主机能 ping 通网关吗?如果不能,先看计算节点的虚拟网桥状态,确认虚拟机网卡已经接入正确的网络。这个阶段最容易出问题的点有两个——安全组是否放行了 ICMP 入方向流量,DHCP 是否正常分配了 IP。直接在控制台用 VNC 打开云主机运行 ip addr,确认网卡有没有获取到地址。
第二步,如果云主机能 ping 通网关但 ping 不通对端云主机,需要确认两个云主机是否在同一网络内。在同一网络内但不通,多半是安全组规则或二层隔离配置问题。如果不在同一网络,就需要检查虚拟路由器是否配置正确,路由器接口是否挂载了两端正确的子网。
第三步,如果内部互访没问题但出不了外网,重点检查路由器的外部网关配置、是否绑定了浮动 IP,以及外部网络类型是否正确。很多时候出外网不通,是因为在创建云主机时没给默认路由,或者安全组出方向规则被误改。外网访问不了云主机,则反过来看浮动 IP 是否成功关联、外部网络的物理连通性以及安全组入方向规则。
我处理过一个很隐蔽的问题:两台云主机在同一个 VXLAN 网络里,互相却 ping 不通。最后排查到计算节点的 L2 Agent 没有正常运行,集群里的其它节点也连不上这台计算节点的隧道端点。重启该计算节点上的 neutron-openvswitch-agent 服务后,网络立刻恢复。这种问题不看服务状态很难发现,因为表面现象像是网络配置错误。
结尾
NFS Volume Provider 的部分到这里就完整了。从服务端导出规则、客户端挂载参数到 Cinder 多后端配置、卷生命周期验证,这一整套流程走下来,你会发现 NFS 后端真正难的不是配置,而是理解每个参数背后的取舍。做存储选型时,多想想你的业务到底要什么,需要共享能力就选 NFS,需要极简环境就上 LVM,规模一上来再考虑更重的方案。而 Neutron 网络这边,核心是把网络、子网、端口、路由、安全组这几样东西彻底想明白,很多“通不了”的怪问题,最后都能归结到这五个基本概念上。
我个人在实际操作中的体会是:存储和网络是最容易“只配不测”的两个环节,但恰恰是它们决定了平台能不能真正用起来。这篇文章里所有配置我都建议你在实验环境里亲手敲一遍,别只做验证性测试——创建卷、挂卷、写成文件、删掉卷、重建、搞坏网络再修好,这些动作越熟练,遇到线上问题就越从容。下一篇如果继续往下写,我打算专门聊聊卷类型的冷热分层,把多后端配合 QoS 限制这类进阶玩法展开,到时候再一起验证。