Kubernetes 基础体系 · 第 40/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

Kubernetes 存储拓扑:Zone、Local PV、延迟绑定和故障恢复

Kubernetes 中“Pod 能否访问某个卷”不是单纯的容量问题,还取决于卷、节点、可用区以及 Pod 调度约束是否存在交集。尤其是 Local PV,数据直接位于某个节点的本地磁盘上,它提供了数据局部性和较低访问延迟,却也把数据可用性绑定到了节点和磁盘故障域。

本文围绕四个概念展开:

  • Zone:云平台或集群中的可用区故障域;
  • Local PV:绑定到特定节点本地路径的持久卷;
  • 延迟绑定:直到 Pod 需要调度时才决定 PVC 绑定到哪个 PV,或在哪个拓扑中动态创建卷;
  • 故障恢复:节点、磁盘或可用区故障后,Kubernetes 能自动恢复什么,不能自动恢复什么。

需要先区分一个容易混淆的事实:

拓扑感知解决的是“卷应该放在哪里、Pod 应该调度到哪里”;它不等于数据复制,也不等于故障转移。

一个本地卷即使被正确调度到某个 Zone,也仍然可能只有一份数据。


一、先建立存储拓扑模型

1. 节点、Zone 和故障域

Kubernetes 节点通常带有描述拓扑的标签,例如:

topology.kubernetes.io/region=cn-beijing
topology.kubernetes.io/zone=cn-beijing-a
kubernetes.io/hostname=node-a1

其中:

  • region 通常表示更大的地理或基础设施区域;
  • zone 通常表示同一区域内相对独立的电力、网络或机架故障域;
  • hostname 通常用于标识单个节点。

这些标签是调度和卷拓扑约束的输入。它们并不自动改变数据位置,也不会自动复制数据。

可以把一个节点表示为:

n=(hostname,zone,region)n = (\text{hostname}, \text{zone}, \text{region})

例如:

node-a1 = (node-a1, cn-beijing-a, cn-beijing)
node-b1 = (node-b1, cn-beijing-b, cn-beijing)

对一个 Pod,实际可调度节点集合可以抽象为多个约束集合的交集:

Neligible=NpodNzoneNvolumeNresourceN其他约束N_{\text{eligible}} = N_{\text{pod}} \cap N_{\text{zone}} \cap N_{\text{volume}} \cap N_{\text{resource}} \cap N_{\text{其他约束}}

其中:

  • NpodN_{\text{pod}}:Pod 的节点选择器、节点亲和性、污点容忍等允许的节点;
  • NzoneN_{\text{zone}}:Pod 的 Zone 或区域约束允许的节点;
  • NvolumeN_{\text{volume}}:卷的拓扑约束允许的节点;
  • NresourceN_{\text{resource}}:满足 CPU、内存、临时存储等资源请求的节点。

只要这个交集为空,Pod 就无法调度。调度器不会为了满足 Pod 而把本地卷“搬到”另一个节点。

2. PV 的拓扑约束

PersistentVolume,即 PV,是集群中对实际存储资源的抽象。一个 PV 可以通过 nodeAffinity 声明它只能被哪些节点使用。

例如,一个位于 node-a1 本地磁盘上的 PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-a1-001
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-ssd
  local:
    path: /var/lib/local-disks/disk-001
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - node-a1

这里有两个独立信息:

  1. local.path 表示节点上的实际路径;
  2. nodeAffinity 表示只有 node-a1 可以使用这个 PV。

如果没有 nodeAffinity,Kubernetes 就无法正确判断这个本地路径在哪个节点上,Pod 可能被调度到错误的节点。因此,Local PV 必须带有节点拓扑约束。

拓扑约束也可以只限制到 Zone,例如云厂商提供的块存储可能声明:

nodeAffinity:
  required:
    nodeSelectorTerms:
      - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
              - cn-beijing-a

这种卷可以在 Zone 内的多个节点间挂载,但具体是否能同时挂载到多个节点,还取决于底层存储和访问模式。Zone 约束并不等于卷可以在 Zone 内任意节点访问。


二、Local PV 到底是什么

1. Local PV 与 hostPath 的区别

hostPath 是 Pod 直接引用节点上的路径:

volumes:
  - name: data
    hostPath:
      path: /data

它主要适合测试或明确接受节点绑定的场景。它不是一种完整的集群持久存储管理方案,常见问题包括:

  • 路径可能在不同节点上内容不同;
  • Pod 被调度到另一个节点后,看到的可能是另一份空目录;
  • 不经过 PV/PVC 生命周期管理;
  • 容易把节点本地目录误认为集群级持久化存储。

Local PV 则把节点本地设备或目录纳入 PV/PVC 模型,并通过 nodeAffinity 把调度约束传递给调度器。它仍然是节点本地数据,但生命周期和绑定关系由 Kubernetes 存储对象管理。

2. Local PV 的数据位置和故障域

Local PV 的数据路径通常位于:

  • 节点本地 SSD;
  • 节点直连 NVMe;
  • 节点本地磁盘分区;
  • 经过管理员准备和挂载的本地文件系统。

典型数据关系是:

Pod
  |
  v
PVC
  |
  v
PV local.path=/var/lib/local-disks/disk-001
  |
  v
node-a1 上的本地磁盘

这条链路中,PV 不是数据副本。PV 只是 Kubernetes 对这份数据的对象描述。

因此:

  • 节点宕机时,Pod 通常不能带着 Local PV 迁移到其他节点;
  • 磁盘损坏时,删除 Pod 不会恢复数据;
  • 删除并重建 PV 也不会恢复数据;
  • Zone 故障时,若数据只有该 Zone 的本地副本,其他 Zone 没有可供挂载的副本。

3. Local PV 通常需要静态供给

Kubernetes 内置了 local 卷类型,但没有内置一个自动扫描所有节点磁盘并创建 Local PV 的动态供给器。

生产环境常见两种方式:

  1. 管理员手工创建 PV;
  2. 使用外部 Local Static Provisioner,根据预先准备的目录或设备自动创建和回收 PV。

这里的“静态供给”表示 PV 先存在,PVC 再从已有 PV 中选择匹配项。它与云块存储常见的动态供给不同:动态供给器收到 PVC 后才调用云厂商 API 创建新磁盘。


三、延迟绑定解决了什么问题

1. 立即绑定的拓扑困境

假设集群有两个 Zone:

zone-a: node-a1, node-a2
zone-b: node-b1, node-b2

某个动态存储类只能在 zone-a 创建卷。Pod 同时要求运行在 zone-b

如果 PVC 创建后立即绑定,动态供给器可能先在 zone-a 创建卷:

PVC 创建
  -> 在 zone-a 创建 PV
  -> PVC 绑定到 zone-a 的 PV
  -> Pod 只能访问 zone-a
  -> Pod 又要求 zone-b
  -> Pod Pending

卷已经创建出来,但调度条件互相矛盾。

2. WaitForFirstConsumer

StorageClass.volumeBindingMode 用于控制 PVC 何时绑定或触发动态供给:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain

WaitForFirstConsumer 的含义是:

PVC 创建后,不立即完成绑定;等到存在使用该 PVC 的 Pod,并且调度器能够评估 Pod 的节点、Zone、资源和卷拓扑后,再完成合适的绑定或供给。

对于 Local PV,kubernetes.io/no-provisioner 表明没有内置动态供给器。PV 需要提前存在,但延迟绑定让调度器有机会根据 Pod 的目标节点选择合适的本地 PV。

状态流程可以表示为:

sequenceDiagram
    participant User
    participant API as API Server
    participant C as PV/PVC Controller
    participant S as Scheduler
    participant P as Kubelet
    participant D as Local Disk

    User->>API: 创建 StorageClass、PV、PVC
    API->>C: PVC Pending
    C-->>API: 暂不绑定 PVC

    User->>API: 创建使用 PVC 的 Pod
    API->>S: Pod Pending
    S->>S: 计算节点、资源和 PV nodeAffinity
    S->>C: 记录/确认卷绑定决策
    C-->>API: PVC Bound,PV Bound
    S-->>API: Pod 绑定到目标节点
    API->>P: 在目标节点创建 Pod
    P->>D: 挂载 local.path
    D-->>P: 提供本地文件系统

关键点不是“先调度 Pod,再绑定卷”这么简单,而是调度器需要把卷约束纳入节点筛选,并协调绑定结果。绑定和调度是相互影响的过程。

3. 延迟绑定不等于延迟挂载

WaitForFirstConsumer 延迟的是:

  • PVC 与 PV 的绑定;
  • 或动态供给器创建卷的决策。

它不表示 Pod 启动后才决定卷位置。Pod 被调度到节点后,PVC 通常已经有明确的 PV,kubelet 才会执行挂载或映射。

也不应把以下概念混为一谈:

概念 解决的问题
WaitForFirstConsumer 避免在不知道 Pod 拓扑时过早绑定或创建卷
PV nodeAffinity 限定卷可以被哪些节点使用
Pod node affinity 限定 Pod 可以运行在哪些节点
Zone 分布约束 让多个 Pod 尽量分布到不同故障域
数据复制 在多个位置保存数据副本,通常由存储系统或应用实现

四、一个可运行的 Local PV 示例

以下示例假设:

  • 节点 node-a1 已存在;
  • 该节点上的 /var/lib/local-disks/disk-001 已准备好;
  • 该路径位于管理员明确管理的本地文件系统上;
  • 节点标签中 kubernetes.io/hostname=node-a1 正确存在。

先创建 StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain

创建 PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-a1-001
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-ssd
  local:
    path: /var/lib/local-disks/disk-001
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - node-a1

创建 PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-ssd
  resources:
    requests:
      storage: 50Gi

创建使用 PVC 的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: local-writer
spec:
  containers:
    - name: writer
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          date >> /data/records.log
          cat /data/records.log
          sleep 3600
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: local-data

应用并观察:

kubectl apply -f local-storage.yaml
kubectl get pv local-pv-a1-001
kubectl get pvc local-data
kubectl get pod local-writer -o wide

典型状态变化为:

PVC 创建后:
NAME         STATUS    VOLUME   CAPACITY   STORAGECLASS   AGE
local-data   Pending                                      5s

Pod 创建并完成调度后:
NAME         STATUS   VOLUME            CAPACITY   STORAGECLASS   AGE
local-data   Bound    local-pv-a1-001   100Gi      local-ssd      20s

Pod 的节点应为 node-a1

NAME           READY   STATUS    NODE
local-writer   1/1     Running   node-a1

这个结果成立是因为:

  1. PVC 请求 local-ssd
  2. PV 使用同一个 storageClassName
  3. PV 容量 100Gi 不小于 PVC 的 50Gi
  4. PV 的访问模式包含 PVC 请求的 ReadWriteOnce
  5. Pod 使用了 PVC;
  6. PV 的 nodeAffinity 把可用节点限制为 node-a1
  7. 调度器选择该节点后,PVC 才完成合适的绑定。

可以使用以下命令查看绑定和调度原因:

kubectl describe pvc local-data
kubectl describe pod local-writer
kubectl get pv local-pv-a1-001 -o yaml

如果 PVC 一直 Pending,常见原因包括:

  • 没有匹配的 storageClassName
  • PV 容量不足;
  • PV 的访问模式不匹配;
  • PV 已经被其他 PVC 使用;
  • Pod 的节点亲和性与 PV 的 nodeAffinity 没有交集;
  • 节点不存在或标签与 PV 中记录的值不一致。

五、Pod 拓扑与 Local PV 如何共同决定调度结果

1. 单个 Pod 的交集

继续使用前面的 PV。若 Pod 增加以下约束:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: topology.kubernetes.io/zone
              operator: In
              values:
                - cn-beijing-b

node-a1 位于 cn-beijing-a,那么:

NPV={node-a1}N_{\text{PV}} = \{node\text{-}a1\}

NPod={cn-beijing-b 中的节点}N_{\text{Pod}} = \{cn\text{-}beijing\text{-}b\text{ 中的节点}\}

两者交集为空:

NPVNPod=N_{\text{PV}} \cap N_{\text{Pod}} = \varnothing

结果通常是 Pod 保持 Pending,事件中出现类似“volume node affinity conflict”的调度失败信息。这个错误不是容量不足,而是拓扑条件矛盾。

2. 多副本服务的 Zone 分布

对于有多个副本的服务,单个副本使用 Local PV 并不能自动实现跨 Zone 高可用。至少需要同时满足:

  • 每个副本有独立的数据卷;
  • 每个卷位于不同节点或不同 Zone;
  • Pod 调度约束能够把副本分散;
  • 应用本身能够处理副本同步、选主和故障切换。

例如,三个副本分别落在三个 Zone:

replica-0 -> zone-a -> node-a1 -> local-pv-a1
replica-1 -> zone-b -> node-b1 -> local-pv-b1
replica-2 -> zone-c -> node-c1 -> local-pv-c1

这只是三个独立本地卷。它们是否包含相同数据,取决于数据库复制协议或其他存储系统,而不是 Kubernetes 的 PV 对象。

反例是:

replica-0 -> zone-a -> local-pv-a1
replica-1 -> zone-a -> local-pv-a2
replica-2 -> zone-a -> local-pv-a3

节点级别可能有一定分散,但 Zone 故障会同时影响所有副本。反亲和规则可以帮助避免这种布局,但仍然不负责数据复制。

3. ReadWriteOnce 的边界

ReadWriteOnce 表示卷可以以读写方式挂载到一个节点。它不是“只能被一个 Pod 使用”的严格语义。

同一个节点上的多个 Pod 是否能够同时使用该卷,取决于具体卷插件和挂载方式。若需要“同一时刻只能由一个 Pod 使用”,可以关注 ReadWriteOncePod,但必须确认集群版本和底层 CSI 驱动支持情况。

无论使用哪种访问模式,都不能从访问模式推导出:

  • 自动复制;
  • 跨 Zone 高可用;
  • 节点故障后的数据迁移;
  • 数据库一致性。

六、Zone 拓扑、动态供给与 Local PV 的区别

云环境中的动态块存储通常由 CSI provisioner 负责。它可能根据 StorageClass、Pod 调度结果和云平台能力,在某个 Zone 创建磁盘。

示例 StorageClass 可能类似:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: cloud-block
provisioner: example.csi.cloudprovider.com
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true

这里的 provisioner 必须替换为实际云厂商或 CSI 驱动提供的名称,不能把示例值直接用于生产。

动态云卷的典型过程是:

Pod 使用 PVC
  -> 调度器评估 Pod 的节点和 Zone
  -> CSI provisioner 在合适拓扑创建卷
  -> PV 记录卷标识和拓扑
  -> PVC Bound
  -> Pod 挂载卷

与 Local PV 相比:

特性 Local PV 云块存储动态供给
数据位置 节点本地磁盘 云平台管理的卷
创建方式 通常静态创建或外部本地供给器 CSI 动态创建
节点绑定 通常绑定到单个节点 可能绑定到 Zone 或多节点能力
节点故障恢复 通常不能自动迁移 取决于云平台和 CSI 能力
性能局部性 通常较好 受网络和云平台实现影响
运维责任 磁盘发现、格式化、替换由用户承担较多 由云平台承担更多基础设施运维

allowedTopologies 可以限制动态供给器只能在指定拓扑中创建卷,但它不是跨 Zone 复制配置。例如:

allowedTopologies:
  - matchLabelExpressions:
      - key: topology.kubernetes.io/zone
        values:
          - cn-beijing-a
          - cn-beijing-b

这只表示允许创建的位置。最终是否支持跨 Zone 挂载、故障转移和多副本,仍由 CSI 驱动及底层存储系统决定。

对于 Local PV,通常不依赖 allowedTopologies 生成磁盘;真正决定位置的是每个 PV 的 nodeAffinity


七、故障路径:节点、磁盘和 Zone 分别发生什么

1. Pod 所在节点暂时不可用

假设:

local-writer -> local-pv-a1-001 -> node-a1

node-a1 断电或失联时,可能出现:

node-a1: NotReady
local-writer: Unknown、Terminating 或不可访问
StatefulSet/Deployment 新 Pod: Pending
PVC: Bound
PV: Bound

这里最重要的状态关系是:

  • PVC 仍然绑定原 PV;
  • PV 仍然声明其节点亲和性;
  • 新 Pod 不能随意调度到 node-b1
  • Kubernetes 不会自动复制 /var/lib/local-disks/disk-001 的数据。

如果强行删除 Pod,控制器可能创建一个新 Pod,但新 Pod 仍受同一个 PV 的节点约束,最终继续 Pending

2. 节点恢复但磁盘损坏

节点重新上线不代表数据可用。可能出现:

  • 路径不存在;
  • 文件系统无法挂载;
  • 磁盘设备变更;
  • 文件系统损坏;
  • 本地盘被重装或清空。

此时应先在节点和文件系统层面诊断,而不是立刻删除 PV:

kubectl get node node-a1
kubectl describe node node-a1
kubectl get pod local-writer -o wide
kubectl describe pod local-writer
kubectl describe pv local-pv-a1-001

在节点上执行的检查取决于操作系统和磁盘管理方式,例如:

findmnt /var/lib/local-disks/disk-001
df -h /var/lib/local-disks/disk-001
ls -la /var/lib/local-disks/disk-001
dmesg | tail -n 50

fsck 等修复命令必须在卷未被使用、并且已经确认文件系统类型和维护窗口后执行。对仍在挂载和写入的文件系统直接修复,可能造成进一步损坏。

3. 节点永久丢失

如果节点永久丢失,Local PV 的数据恢复路径通常只有以下几类:

  1. 修复或取回原节点磁盘;
  2. 从底层磁盘或快照恢复;
  3. 从备份恢复到新节点的新卷;
  4. 由应用副本从其他副本重建数据;
  5. 接受数据丢失。

Kubernetes 本身只管理对象状态,不拥有本地磁盘数据的第二份副本。


八、Retain、删除 PV 与恢复操作

Local PV 通常建议使用:

persistentVolumeReclaimPolicy: Retain

Retain 的含义是 PVC 删除后,PV 不会自动删除底层数据。PV 通常进入 Released 状态,需要管理员决定如何处理。

这可以避免误删数据,但也带来一个重要风险:重新创建相同名称的 PV 或修改对象,并不会自动验证目录中的数据是否属于原 PVC。

恢复前应先确认:

kubectl get pv local-pv-a1-001 -o yaml
kubectl get pvc local-data -o yaml
kubectl get events --sort-by=.lastTimestamp

不应把以下操作当作恢复手段:

kubectl delete pv local-pv-a1-001
kubectl apply -f local-pv-a1-001.yaml

这样做只会改变 Kubernetes 对象,不能修复磁盘,也不能复制数据。若底层目录仍有数据,错误的清理脚本或本地供给器回收逻辑还可能将其删除。

一种较安全的恢复思路是:

  1. 停止会写入原卷的应用;
  2. 确认原节点或磁盘是否仍可读取;
  3. 备份或复制原数据;
  4. 准备新的节点本地磁盘或其他类型 PV;
  5. 恢复数据;
  6. 让应用重新使用恢复后的 PVC;
  7. 验证应用一致性,而不仅是验证 Pod 进入 Running

若应用是数据库,不能只使用文件复制替代数据库备份。数据库可能存在 WAL、事务日志、缓存页和一致性边界,恢复方式应遵循数据库自身的备份与恢复协议。


九、完整故障诊断顺序

当使用 Local PV 的 Pod 处于 PendingContainerCreating 或反复重启时,可以按资源关系逐层检查。

第一步:确认 PVC 和 PV 绑定状态

kubectl get pvc,pv

重点观察:

PVC STATUS: Pending 或 Bound
PV STATUS: Available、Bound、Released、Failed

如果 PVC 是 Pending,查看:

kubectl describe pvc <pvc-name>

常见事件包括:

  • 没有可匹配的 PV;
  • 等待第一个消费者;
  • 访问模式或容量不匹配;
  • 存储类名称错误。

第二步:确认 Pod 的调度事件

kubectl describe pod <pod-name>

如果出现:

node(s) had volume node affinity conflict

说明 Pod 允许的节点集合与 PV 的节点集合没有交集。此时应对照:

kubectl get pv <pv-name> -o yaml
kubectl get pod <pod-name> -o yaml
kubectl get nodes --show-labels

重点检查:

  • spec.nodeAffinity
  • Pod 的 nodeSelector
  • Pod 的 requiredDuringSchedulingIgnoredDuringExecution
  • topology.kubernetes.io/zone
  • kubernetes.io/hostname
  • 节点是否被污点隔离;
  • 节点是否有足够资源。

第三步:确认节点和本地路径

kubectl get node <node-name>
kubectl describe node <node-name>

节点是 Ready 只说明 kubelet 能够报告状态,不代表本地磁盘文件系统一定正常。还应在节点上检查:

mount | grep local-disks
findmnt /var/lib/local-disks/disk-001
stat /var/lib/local-disks/disk-001

第四步:确认应用层一致性

卷挂载成功只代表 kubelet 能访问文件系统,不代表数据库或应用数据一致。恢复后还需要检查:

  • 数据库日志;
  • 事务日志和校验;
  • 应用选主状态;
  • 副本同步状态;
  • 是否存在重复写入或脑裂。

十、常见错误理解

误解一:WaitForFirstConsumer 会自动选择最优 Zone

它只能让绑定或供给决策拥有 Pod 拓扑上下文。它不会自动提供:

  • 跨 Zone 数据复制;
  • 成本优化;
  • 业务级的主备选择;
  • 数据库副本编排;
  • 故障后自动恢复策略。

误解二:Local PV 的 Retain 会保护节点磁盘

Retain 保护的是回收策略下的底层数据不被 Kubernetes 自动删除。它不能防止:

  • 节点物理损坏;
  • 磁盘损坏;
  • 操作系统重装;
  • 运维人员删除目录;
  • 文件系统损坏。

误解三:Pod 有多个副本,所以 Local PV 自动高可用

多个 Pod 副本只有在它们保存的是可互相恢复的数据副本时,才可能形成高可用。三个副本分别挂载三个空的本地卷,并不会自动产生三个数据副本。

误解四:删除 Pod 就能让它迁移到其他节点

如果 Pod 使用的 PV 带有节点亲和性,重建后的 Pod 仍然必须满足该 PV 的拓扑条件。要迁移数据,必须先完成数据复制、备份恢复或应用级重建,再切换到新的 PV。

误解五:Zone 标签存在就代表 Zone 隔离可靠

拓扑标签是调度输入。节点标签错误、云平台标签不一致、节点被错误重命名,都可能导致调度结果与真实基础设施位置不一致。生产集群应确认节点标签由受信任机制管理,并定期核对云平台和 Kubernetes 中的拓扑信息。


十一、生产取舍

Local PV 适合以下场景:

  • 需要非常低的本地访问延迟;
  • 节点本地 NVMe 能提供明显性能收益;
  • 应用自身具备副本、重建或备份恢复能力;
  • 能接受较高的节点磁盘运维责任;
  • 业务已经设计了明确的故障恢复流程。

它不适合作为“无需复制即可高可用”的方案。若主要目标是节点故障后的自动恢复,应优先评估具备节点间或 Zone 间复制能力的 CSI 存储,或者使用应用自身的多副本复制机制。

最终的可用性取决于最小故障域中的数据副本数量。若某份业务数据只有一份:

数据副本数=1\text{数据副本数}=1

那么无论它位于哪个 Zone、是否使用 PV、是否采用延迟绑定,发生承载该副本的磁盘或节点永久故障时,都不存在 Kubernetes 层面的自动数据恢复。

而真正跨故障域的高可用至少需要:

副本位置互斥副本数据可恢复故障后有切换或重建机制\text{副本位置互斥} \quad\land\quad \text{副本数据可恢复} \quad\land\quad \text{故障后有切换或重建机制}

其中:

  • “副本位置互斥”避免所有副本落在同一节点或同一 Zone;
  • “副本数据可恢复”要求复制协议或备份保证数据能够重建;
  • “切换或重建机制”决定故障发生后服务是否能重新提供能力。

Kubernetes 的拓扑调度、Local PV 和延迟绑定分别解决位置约束与绑定时机问题;数据复制、备份和应用故障切换,则必须由底层存储系统或有状态应用负责。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。