4. CAP 理论在 K8s 中的体现:一致性、可用性、分区容忍性对调度与资源分配的影响
CAP 定理(Brewer 定理)是分布式系统的基石:一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance)三者最多同时满足两个。在 Kubernetes 集群中,etcd、API Server、调度器(kube-scheduler)与节点控制器之间的交互,处处体现着 CAP 的权衡。理解这些权衡,是进行容量调优和运维剧本设计的前提。
核心观点: K8s 在架构上选择了 CP(一致性 + 分区容忍性) 作为默认路线,但通过调度策略、资源配额和 Operator 模式,可以在特定工作负载下向 AP 倾斜,实现弹性与可用性的平衡。
4.1 CAP 在 K8s 核心组件中的映射
下表梳理了 CAP 三要素在 K8s 各组件中的具体体现与影响:
| CAP 要素 | K8s 组件 / 行为 | 对调度与资源分配的影响 |
|---|---|---|
| C(一致性) | etcd(Raft 共识)、API Server 校验、调度器绑定(Binding) | 调度决策必须基于最新的集群状态;资源预留(Reservation)需等待 etcd 确认,否则可能产生双调度(Double Scheduling) |
| A(可用性) | kubelet 心跳、节点控制器(Node Controller)、Pod 驱逐(Eviction) | 节点失联时,控制器需在容忍时间(pod-eviction-timeout)内做出驱逐决策,保证工作负载的持续运行 |
| P(分区容忍性) | 网络分区、etcd 集群脑裂保护、调度器故障转移 | 分区发生时,调度器可能无法获取最新节点资源;需依赖缓存和本地队列,避免调度死锁 |
运维启示: 当集群发生网络分区时,K8s 优先保证一致性(CP),即停止调度或驱逐不确定状态的 Pod,防止数据冲突。这会导致部分节点上的 Pod 暂时不可用,直到分区恢复。
4.2 一致性对调度与资源分配的影响
K8s 调度器(kube-scheduler)在做出调度决策时,必须读取 etcd 中一致性的节点资源视图。如果 etcd 返回的是过期数据,调度器可能将 Pod 分配到资源已满的节点,导致 Pod 启动失败或节点过载。
- 调度绑定(Binding)的原子性: 调度器通过
POST /api/v1/namespaces/{ns}/pods/{pod}/binding写入 etcd。只有写入成功(多数节点确认),调度才算完成。这保证了“一个 Pod 只被调度到一个节点”。 - 资源预留(Reservation)的强一致: 调度器在预选(Predicates)阶段计算节点剩余资源时,依赖的是 etcd 中已持久化的 Pod 资源请求(Requests)。如果 etcd 写入延迟,调度器可能看到“假空余”,导致超卖。
- 调度队列的线性一致性: 调度器内部的 PriorityQueue 需要保证 Pod 的入队顺序与 etcd 中的创建时间戳一致,否则优先级高的 Pod 可能被延迟调度。
# 调度器绑定操作的简化流程(强一致性路径)
1. 调度器从 etcd 读取节点 n1 的当前资源使用(一致性读)
2. 调度器计算 n1 剩余资源是否满足 Pod 请求
3. 调度器向 API Server 发送 Binding 请求
4. API Server 将 Binding 写入 etcd(Raft 共识,多数节点确认)
5. etcd 返回成功 → 调度器标记 Pod 已调度
6. kubelet 通过 Watch 感知 Binding 事件,开始拉取镜像启动容器
4.3 可用性对调度与资源分配的影响
K8s 在节点失联、控制器故障等场景下,会牺牲部分一致性来换取可用性。典型场景是节点控制器(Node Controller)的 Pod 驱逐机制。
- 节点心跳超时与驱逐: 当节点心跳超过
--node-monitor-grace-period(默认 40s),节点控制器将节点标记为Unknown。再经过--pod-eviction-timeout(默认 5m),控制器开始驱逐该节点上的 Pod。 - 可用性优先的调度补偿: 如果调度器发现某个节点长时间未上报状态(分区),它会将该节点从调度候选列表中移除,并将 Pod 调度到其他可用节点。这保证了工作负载的持续可用,但可能造成资源碎片。
- 调度器高可用(Leader Election): 多副本调度器通过 etcd 的租约(Lease)机制选主。当主调度器故障时,备用调度器在租约过期后接管,保证调度服务的可用性。切换期间,调度队列中的 Pod 可能被重新排序。
容量调优陷阱: 如果集群中大量节点同时失联(例如网络分区),节点控制器会批量驱逐 Pod。这些 Pod 重新调度时,可能瞬间耗尽集群剩余资源,导致其他正常节点过载。建议设置
--node-eviction-rate 和 --secondary-node-eviction-rate 限制驱逐速率。
4.4 分区容忍性对调度与资源分配的影响
分区容忍性是 K8s 集群必须支持的属性。当网络分区发生时,etcd 集群可能分裂为多数派和少数派。K8s 的应对策略如下:
| 分区场景 | etcd 状态 | 调度器行为 | 资源分配影响 |
|---|---|---|---|
| 多数派分区(≥ (n/2)+1 节点) | 正常服务,可读写 | 继续调度,但无法感知少数派节点状态 | 少数派节点上的 Pod 可能被驱逐,资源被释放 |
| 少数派分区(< (n/2)+1 节点) | 只读,拒绝写入 | 调度器无法写入 Binding,调度暂停 | 新 Pod 无法调度,已有 Pod 继续运行 |
| 全分区(所有节点失联) | 不可用 | 调度器本地缓存失效,调度完全停止 | 集群进入只读状态,资源分配冻结 |
4.5 容量调优中的 CAP 实践策略
在运维剧本中,针对不同工作负载的 CAP 需求,可以采用差异化的调度与资源分配策略:
- 强一致性工作负载(如 etcd、数据库): 使用
PodAntiAffinity将 Pod 分散到不同故障域,避免分区导致多数派丢失。设置priorityClassName: system-cluster-critical保证调度优先级。 - 高可用工作负载(如 Web 服务、API 网关): 使用
PodDisruptionBudget限制自愿干扰(如节点维护)期间同时不可用的 Pod 数量。设置topologySpreadConstraints实现跨可用区分布。 - 分区容忍性优化: 为调度器配置
--failure-domains参数,在分区发生时优先将 Pod 调度到可用区内的健康节点。使用descheduler在分区恢复后重新平衡资源。
# 高可用工作负载的 PodDisruptionBudget 示例
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2 # 至少保持 2 个 Pod 可用
selector:
matchLabels:
app: web-frontend
# 跨可用区拓扑分布约束
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 6
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-frontend
运维剧本要点: 在容量规划时,建议为每个工作负载标注 CAP 需求标签(
cap.workload/type: cp | ap)。调度器可以通过 Extender 或 Scheduler Framework 插件,根据标签动态调整调度策略,在分区场景下优先保障 AP 类型工作负载的可用性。