4. Leader Election 原理:endpoint/lease 对象、选举算法、leader 任期与心跳

Kubernetes 控制面高可用的核心机制是 Leader Election(领导者选举)。控制器管理器(kube-controller-manager)和调度器(kube-scheduler)作为有状态服务,同一时刻只能有一个活跃实例执行实际的控制逻辑,其余实例处于热备状态。Leader Election 确保在 Leader 故障时,备机能够快速接管,且不会出现多个 Leader 同时工作的“脑裂”场景。

4.1 选举载体:Endpoint 与 Lease 对象

Leader Election 依赖存储在 etcd 中的 API 对象作为“锁”。历史上 Kubernetes 使用 Endpoint 对象,从 v1.14 开始引入更轻量的 Lease 对象(coordination.k8s.io/v1),目前两者并存,但 Lease 已成为默认和推荐方式。

特性 Endpoint 锁 Lease 锁
API 组 v1(核心) coordination.k8s.io/v1
资源名称 kube-controller-manager kube-controller-manager
命名空间 kube-system kube-system
存储内容 Annotations + Endpoint 地址 专用字段(holderIdentity, leaseDurationSeconds, acquireTime, renewTime, leaseTransitions)
更新开销 较大(整个 Endpoint 对象) 较小(仅 Lease 对象)
推荐程度 兼容旧版本 默认(v1.14+)
实践建议: 在 Kubernetes v1.24+ 集群中,kube-controller-manager 默认使用 Lease 锁。如果因升级或迁移需要回退,可通过 --leader-elect-resource-lock=endpoints 参数显式指定。

4.2 选举算法:基于租约的抢占式选举

Kubernetes 的 Leader Election 采用 基于租约(Lease-based)的抢占式算法,并非 Paxos 或 Raft。其核心思想是:所有候选者尝试更新同一个 Lease 对象,成功更新者成为 Leader,并定期续约;其他候选者通过观察 Lease 的更新时间来判断 Leader 是否存活。

选举流程如下:

  1. 初始化: 每个控制器管理器实例启动时,生成唯一的身份标识(identity),通常为 <hostname>_<random-string>。
  2. 尝试获取锁: 实例读取 Lease 对象,检查当前 holderIdentity 是否为空,或 renewTime 是否已过期(超过 leaseDuration)。
  3. 抢占: 如果锁空闲或过期,实例尝试通过 Update 操作将自己的 identity 写入 Lease 对象。由于 etcd 的乐观锁机制(resourceVersion),只有一个实例能成功。
  4. 成为 Leader: 更新成功的实例成为 Leader,开始执行控制循环,并定期(每 renewDeadline 内)续约。
  5. 失败重试: 未获取锁的实例进入等待状态,持续观察 Lease 对象,一旦发现 Leader 停止续约(renewTime 过期),立即发起新一轮抢占。
关键参数:
  • --leader-elect-lease-duration(默认 15s):Lease 的租约时长。超过此时间未续约,锁被视为释放。
  • --leader-elect-renew-deadline(默认 10s):Leader 必须在此时长内完成续约,否则视为失联。
  • --leader-elect-retry-period(默认 2s):候选者尝试获取锁或 Leader 尝试续约的间隔。

4.3 Leader 任期与心跳机制

Leader 任期从成功获取锁开始,到主动释放或失联结束。心跳通过定期更新 Lease 对象的 renewTime 字段实现。

下图展示了 Leader 正常任期与故障转移的时间线:

Leader 任期与心跳时间线 T0 T0+5s T0+15s T0+25s T0+30s Leader A 任期 续约 续约 续约 故障 过期 Leader B 任期 续约 续约 续约 时间线说明: • T0~T0+15s:Leader A 每 5s 续约一次(renewDeadline=10s,leaseDuration=15s)。 • T0+15s:Leader A 故障,停止续约。Lease 在 T0+15s 时租约到期。 • T0+15s~T0+17s:候选者 B 检测到租约过期,发起抢占,成功成为新 Leader。

4.4 脑裂防护与优雅降级

Leader Election 通过以下机制防止脑裂:

  • 乐观锁(resourceVersion): 所有更新操作携带 etcd 的 resourceVersion,只有版本匹配的请求才能成功,确保同一时刻只有一个实例能写入 Lease。
  • 租约过期(leaseDuration): 即使 Leader 因网络分区与 etcd 失联,旧 Leader 也不会立即释放锁,而是等待 leaseDuration 超时。这避免了网络抖动导致的频繁切换。
  • Stale Leader 检测: 旧 Leader 在失联后,其本地控制循环仍可能短暂运行。但 Lease 锁已被新 Leader 获取,旧 Leader 的更新操作会因 resourceVersion 冲突而失败,从而自动停止控制逻辑。
注意: 如果 leaseDuration 设置过小(如 <5s),网络抖动可能导致频繁的 Leader 切换,增加控制面抖动。如果设置过大(如 >60s),Leader 故障后故障转移时间会变长。生产环境建议保持默认值(15s)。

4.5 选举状态查看与调试

可以通过以下命令查看当前 Leader 信息:

# 查看 Lease 对象(推荐)
kubectl get lease -n kube-system kube-controller-manager -o yaml

# 输出示例(关键字段):
# spec:
#   holderIdentity: "master-01_abc123"
#   leaseDurationSeconds: 15
#   acquireTime: "2025-03-20T10:00:00Z"
#   renewTime: "2025-03-20T10:00:10Z"
#   leaseTransitions: 3

# 查看 Endpoint 对象(兼容旧版本)
kubectl get endpoints -n kube-system kube-controller-manager -o yaml

通过 leaseTransitions 字段可以统计 Leader 切换次数,如果该值在短时间内快速增长,说明控制面存在稳定性问题。

4.6 多锁兼容与迁移策略

在集群升级过程中,可能同时存在使用 Endpoint 锁和 Lease 锁的组件。Kubernetes 支持 多锁(MultiLock) 模式,允许组件同时竞争多个锁,确保平滑迁移。

锁模式 参数设置 适用场景
Endpoint 锁 --leader-elect-resource-lock=endpoints v1.13 及以下集群
Lease 锁 --leader-elect-resource-lock=leases v1.14+ 集群(默认)
多锁(Endpoint + Lease) --leader-elect-resource-lock=endpointsleases 集群升级过渡期,确保新旧版本组件互认 Leader
迁移建议: 在升级控制面组件时,先启用多锁模式(endpointsleases),待所有组件都升级到支持 Lease 的版本后,再切换为纯 Lease 锁。这样可以避免升级过程中出现多个 Leader 或没有 Leader 的情况。