Leader Election 在 AI 控制器:选主逻辑别影响调度决策 — 方向2

📅 2026/7/16 17:53:30
Leader Election 在 AI 控制器:选主逻辑别影响调度决策 — 方向2
Leader Election 在 AI 控制器选主逻辑别影响调度决策 — 方向2一、AI 控制器的主从切换5 秒的真空期能误多少事Kubernetes 原生控制器的 Leader Election 机制通过 Lease 资源实现。在标准的 controller-runtime 实现中Lease 的默认续约周期是 15 秒租约持续时间为 15 秒。这意味着在某一个 Leader 失联后最坏情况下需要等待 15 秒才能选出新的 Leader。15 秒对于无状态服务的控制器可能无关紧要——多等一会Deployment 的 ReplicaSet 状态最终还是会收敛。但对于 AI 推理控制器15 秒的真空期意味着新提交的推理任务可能在这 15 秒内无人调度。GPU 节点的心跳更新可能在这 15 秒内遗漏导致误判节点故障。模型预热的调度决策可能发生冲突——两个 Pod 被调度到同一个 GPU 不足的节点上。更隐蔽的问题是Lease 续约的竞争会在控制平面引入额外的 API Server 负载。在大规模集群中数十个控制器的 Lease 续约请求叠加起来在 API Server 高负载时可能延迟进而诱发误切换。AI 控制器的 Leader Election需要更短的失效检测时间与更稳定的续约策略。二、Leader Election 的竞态窗口与 AI 调度的一致性问题控制器进入选主逻辑时整个调度链路是停止的。调度决策的不一致往往发生在旧 Leader 尚未完全退出、新 Leader 已经开始工作的重叠窗口。问题出在旧 Leader 不知道自己已经失去了 Leader 身份。在标准的 controller-runtime 实现中Lease 续约失败并不会立即让控制器停止工作。控制器会在下一次 Reconcile 循环中检查 Leader 状态但这之间存在一段已失主但仍在工作的危险窗口。对策是最短路径的失主检测每一个调度决策操作之前都显式地检查当前 Lease 持有者是否是自己。三、带 Fencing Token 的调度决策保护解决这个问题的经典方案是 Fencing Token——每次 Leader 变更时产生一个递增的 TokenAPI Server 上的所有写操作都必须附带当前 Token。旧 Leader 的请求因为 Token 过期会被 API Server 拒绝。在 Kubernetes 中可以用 ResourceVersion 或自定义 Annotation 来实现类似效果// LeaseGuard 基于 Lease 的调度保护机制 type LeaseGuard struct { client client.Client leaseName string leaseNS string identity string // 当前控制器副本的唯一标识 currentToken atomic.Value // 当前有效的 Fencing Token(ResourceVersion) renewInterval time.Duration // 续约间隔建议2-3s远小于默认15s leaseDuration time.Duration // 租约持续时间建议5-8s } // TryBecomeLeader 尝试获取 Leader 身份 func (g *LeaseGuard) TryBecomeLeader(ctx context.Context) (bool, error) { lease : coordinationv1.Lease{} err : g.client.Get(ctx, client.ObjectKey{ Name: g.leaseName, Namespace: g.leaseNS, }, lease) if err ! nil { if !apierrors.IsNotFound(err) { return false, err } // Lease 不存在创建 lease coordinationv1.Lease{ ObjectMeta: metav1.ObjectMeta{ Name: g.leaseName, Namespace: g.leaseNS, }, } } now : metav1.MicroTime{Time: time.Now()} // 检查当前持有者是否是自己或者 Lease 已过期 if lease.Spec.HolderIdentity nil || *lease.Spec.HolderIdentity g.identity || lease.Spec.RenewTime.Time.Add(g.leaseDuration).Before(time.Now()) { lease.Spec.HolderIdentity g.identity lease.Spec.LeaseDurationSeconds ptr.To(int32(g.leaseDuration.Seconds())) lease.Spec.RenewTime now if err : g.client.Update(ctx, lease); err ! nil { return false, err } // 记录 Fencing Token使用 ResourceVersion g.currentToken.Store(lease.ResourceVersion) return true, nil } return false, nil } // Guard 在执行调度操作前校验 Leader 身份 func (g *LeaseGuard) Guard(ctx context.Context) error { token : g.currentToken.Load() if token nil { return ErrNotLeader } lease : coordinationv1.Lease{} if err : g.client.Get(ctx, client.ObjectKey{ Name: g.leaseName, Namespace: g.leaseNS, }, lease); err ! nil { return fmt.Errorf(check lease: %w, err) } if lease.Spec.HolderIdentity nil || *lease.Spec.HolderIdentity ! g.identity { return ErrNotLeader } // 更新当前 Token g.currentToken.Store(lease.ResourceVersion) return nil } // StartRenewLoop 启动续约循环 func (g *LeaseGuard) StartRenewLoop(ctx context.Context) { ticker : time.NewTicker(g.renewInterval) defer ticker.Stop() for { select { case -ctx.Done(): return case -ticker.C: lease : coordinationv1.Lease{} err : g.client.Get(ctx, client.ObjectKey{ Name: g.leaseName, Namespace: g.leaseNS, }, lease) if err ! nil { // 续约失败——主动放弃 Leader 身份 g.currentToken.Store(nil) return } if lease.Spec.HolderIdentity nil || *lease.Spec.HolderIdentity ! g.identity { g.currentToken.Store(nil) return } lease.Spec.RenewTime metav1.MicroTime{Time: time.Now()} if err : g.client.Update(ctx, lease); err ! nil { // 续约更新失败也可能意味着失主 g.currentToken.Store(nil) return } } } }核心差异点在于默认的 controller-runtime Lease 续约是通过leaderelection.RunOrDie在后台完成的控制器本身并不关心续约是否成功。上述实现将续约状态通过currentToken暴露出来让调度逻辑在每一次操作前都能验证 Leader 身份。同时Lease 的续约间隔从 15 秒缩短到了 2-3 秒租约持续时间缩短到了 5-8 秒。这样在 Leader 失联后新 Leader 的选举时间从 15 秒降低到 5-8 秒。四、抢主太快也有副作用缩短 Lease 时间并非没有代价。API Server 压力如果集群中有 50 个控制器每个以 3 秒间隔续约API Server 每秒多出约 17 次写请求。虽然对于 etcd 来说不是大问题但在 API Server 本身高负载或网络抖动时这等于是给自己增加了一份不确定因素。误切风险Lease 时间太短比如设为 2 秒网络的一瞬间延迟波动就可能导致不必要的 Leader 切换。每次切换都意味着调度链路停止几秒频繁切主反而降低了可用性。多控制器竞争同一资源如果推理调度器和模型预热管理器各自独立竞选它们可能同时操作同一个 GPU 节点。应该考虑合并控制器或者让它们共享同一个 Lease。适用边界适合延迟敏感的 AI 推理调度场景需要亚 10 秒级的故障恢复。适合GPU 资源稀缺、节点超分配风险高的集群。不适合控制器本身是低频调度如每天只调度几次。不适合控制器数量超过 100 的大规模集群Lease 频繁续约带来的 API Server 压力不可忽视。五、总结Leader Election 本身不复杂复杂的是选主过程中的调度一致性问题。在 AI 控制器中解决方案是三重保障缩短 Lease 时间以加快故障恢复、每次调度操作前显式校验 Leader 身份、续约失败时主动放弃 Leader 状态。基础设施不需要漂亮话需要的是在主从切换的那一刻不会有两份调度指令在争抢同一块 GPU 显存。