Li Sheng | Backend / Distributed Storage Engineer Li Sheng | Backend / Distributed Storage Engineer
Home
Resume
Projects
Topics
Notes
GitHub (opens new window)
Home
Resume
Projects
Topics
Notes
GitHub (opens new window)
  • Go语言

  • C++

  • 算法题

  • 存储系统

  • CephFS

  • 分布式系统

  • 计算机网络

  • Redis与缓存

  • Kubernetes

    • 存储

      • 待完成专题池
      • 为什么 Kubernetes 要把存储抽象拆成 PV、PVC、StorageClass 与 CSI
      • 一个 PVC 从创建到 Pod 成功挂载,中间经过了哪些控制链路
      • 为什么 PVC 已经 Bound,Pod 仍然可能挂载失败
      • 本地盘、网络盘与拓扑约束,是如何共同影响 Kubernetes 调度的
        • 进一步讨论
      • Kubernetes 存储故障为什么常表现为应用启动超时
      • 为什么 Kubernetes CSI 插件架构要拆成 Controller、Node 与 Sidecar
  • 技术笔记
  • Kubernetes
  • 存储
lisheng
2026-04-28
目录

本地盘、网络盘与拓扑约束,是如何共同影响 Kubernetes 调度的

# 本地盘、网络盘与拓扑约束,是如何共同影响 Kubernetes 调度的

这篇文章要回答的问题是:为什么同样是“给 Pod 一块盘”,本地盘和网络盘会把调度问题变成两种完全不同的约束系统,以及这些约束如何反过来决定可用性、性能和运维复杂度。

很多人在刚接触 Kubernetes 存储时,会把调度理解成一件“先找台有 CPU 和内存的机器,再把卷挂上去”的后置动作。这个直觉只在极少数场景里成立。只要卷本身带有位置属性,或者后端对可附加范围有拓扑限制,调度就不再只是算计算资源,而会被存储反向塑形。也就是说,Pod 最终能否落到某台节点,并不只取决于这台机器够不够空,还取决于卷能不能在那台机器上存在、附加或保持性能可接受。

本地盘和网络盘的差异,恰好把这个问题拆成了两种完全不同的调度难题。本地盘最强的特征是数据和节点位置天然绑定。卷在哪台机器上,数据就在哪台机器上,无法像云盘那样跨节点附加,更不可能在另一个可用区临时接管。对调度器来说,这意味着卷不是跟着 Pod 走,而是 Pod 必须主动迁就卷的位置。只要一个工作负载依赖本地盘,它的可选节点集合在一开始就被急剧缩小了。此时调度问题不再是“从所有节点里挑一个最好”,而是“在少数真正持有数据的节点里,是否还有一个既满足算力约束又满足其他亲和规则的落点”。

这会立刻带来两个后果。第一,本地盘让数据位置直接变成调度约束,卷的拓扑不是附属信息,而是主决策条件。第二,本地盘把故障恢复和调度弹性一起压缩了。某台节点一旦不可用,系统并不能像网络盘那样简单把卷重新附加到别处,而是必须面对数据是否可迁移、是否有副本、是否允许跨节点重建这些更重的问题。换句话说,本地盘常常能给出更直接的 I/O 路径和更可控的性能,但它是用调度自由度和故障转移能力换来的。

网络盘则处在另一端。它看起来更“灵活”,因为卷数据通常不跟节点绑定,而是由后端网络存储系统托管。这样 Pod 的调度不必一开始就被某一台具体机器锁死,理论上只要节点满足访问该卷的拓扑条件,卷就可以在后续被附加过去。这种特性显著放松了位置约束,也让工作负载跨节点迁移、滚动更新和节点替换变得更容易。但代价并没有消失,而是换了形式。网络盘把“数据必须在哪台节点上”这个硬约束,转化成了“这台节点是否位于允许附加的可用区、机架或网络边界内”“后端是否允许同时挂载到多个消费者”“附加链路是否足够稳定”这些新的限制。

因此,本地盘和网络盘最大的区别,并不只是“一个快、一个慢”或者“一个本地、一个远端”,而是它们把存储约束注入调度过程的时机完全不同。本地盘往往在调度之前就已经把节点集合切窄,因为卷位置本身就是既定事实。网络盘则更常见的是:调度器先挑出一个看起来合适的节点,然后附加链路再去验证这块卷能否真的与这台节点建立关系。前者是“先有位置,再有调度”,后者更接近“先选消费者,再完成存储连接”。从系统行为上看,这两种模式会塑造出完全不同的等待点、失败点和运维心智模型。

拓扑约束真正复杂的地方,在于它从来不会单独存在。存储位置约束几乎总要和节点亲和、Pod 反亲和、可用区分布、故障域要求以及资源空闲情况一起共同生效。比如一个 Pod 既要求使用某类本地 NVMe 卷,又要求和同一服务的其他副本分散在不同节点上,还要求只落在某个可用区内,这时调度器面对的就不是一条约束,而是几组可能互相冲突的限制同时求交。网络盘虽然缓和了“必须在某台节点”这种刚性绑定,但它仍然可能受限于“必须与卷位于同一个 zone”“只能在支持该存储驱动的节点上附加”“不能跨特定故障域使用”。也就是说,网络盘并没有取消拓扑,只是把拓扑从节点级硬绑定提升成了区域级或能力级约束。

WaitForFirstConsumer 之所以重要,就是因为它正好站在“卷先决定位置”还是“调度先暴露消费者位置”这条分界线上。假如系统在创建 PVC 时就立刻动态供给一块卷,却还不知道未来 Pod 会被调度到哪个节点、哪个可用区,那么它很可能过早地在错误拓扑里创建出一块卷。对本地盘尤其如此,因为一旦卷在某个节点或某个 zone 落地,后续可选节点范围就被提前锁死,甚至可能让 Pod 永远找不到能同时满足计算和存储条件的节点。WaitForFirstConsumer 的本质,不是单纯“延后一点绑定”,而是把卷决策延迟到调度器已经看见真实消费者约束之后,再让存储选择和节点选择一起收敛。

从调度链路的角度看,这相当于把存储从被动资源变成了主动约束参与者。调度器不再只是在卷已经准备好之后接收结果,而要在某些场景中先给出一个候选放置,再让供给系统依据这个放置结果选择正确的拓扑。于是存储和调度之间不再是简单串行,而是一种带反馈的协同关系。也正因为如此,很多 Kubernetes 存储问题表面看像是“卷没起来”,底层其实是“调度和拓扑在互相等待或互相否决”。

把本地盘和网络盘放回可用性与性能的视角再看,会更容易理解为何不能只按“是否远端”来分类。本地盘通常意味着更短路径、更少网络跳数和更直接的设备性能,但它要求工作负载接受更低的迁移自由度,以及在节点故障时更重的恢复代价。网络盘通常意味着更高的迁移弹性和更平滑的运维体验,但也引入了附加延迟、后端网络依赖、可用区限制和新的失败域。调度器面对的并不是“哪块盘更好”,而是“在这类存储的物理约束下,哪种放置仍然可行”。所以存储选择本质上也在选择调度空间。

如果把这篇文章压缩成一句话,那么本地盘和网络盘并不是给调度器提供了同一类资源的两种实现,而是在根本上定义了两种不同的放置逻辑。本地盘让数据位置先天进入调度前提,网络盘则把位置约束推迟到附加和拓扑匹配阶段;WaitForFirstConsumer 这类机制负责在两者之间调和绑定时机;而真正的调度结果,则始终是存储拓扑、节点能力、可用区和故障域等多组约束共同求交的产物。

# 进一步讨论

  • 【待展开:WaitForFirstConsumer 为什么要把卷绑定延后到调度阶段|wait-for-first-consumer-binding-delay】:这能继续讨论绑定时机和调度决策如何互相依赖。
Edit (opens new window)
Last Updated: 2026/04/28, 14:21:08
为什么 PVC 已经 Bound,Pod 仍然可能挂载失败
Kubernetes 存储故障为什么常表现为应用启动超时

← 为什么 PVC 已经 Bound,Pod 仍然可能挂载失败 Kubernetes 存储故障为什么常表现为应用启动超时→

最近更新
01
待完成专题池
04-28
02
待完成专题池
04-28
03
为什么 Kubernetes CSI 插件架构要拆成 Controller、Node 与 Sidecar
04-28
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式