Leader Election 领导者选举
为什么需要领导者选举
控制器多副本部署时,需要确保只有一个实例真正执行 Reconcile,避免并发冲突。K8s 通过 Lease 对象实现分布式锁。
实现原理
多个 Controller 实例竞争创建/更新 Lease 对象
│
▼
成功获取 Lease → 成为 Leader → 执行控制器逻辑
│
▼
定期续约 Lease(renewDeadline 内)
│
▼
Leader 宕机 → Lease 过期 → 其他实例竞争成为新 Leader完整示例
go
package main
import (
"context"
"os"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
)
func main() {
config, _ := rest.InClusterConfig()
clientset, _ := kubernetes.NewForConfig(config)
// 获取当前 Pod 名称作为 ID
id := os.Getenv("POD_NAME")
if id == "" {
id, _ = os.Hostname()
}
// 创建 Lease 锁
lock := &resourcelock.LeaseLock{
LeaseMeta: metav1.ObjectMeta{
Name: "my-controller-leader",
Namespace: "kube-system",
},
Client: clientset.CoordinationV1(),
LockConfig: resourcelock.ResourceLockConfig{
Identity: id,
},
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{
Lock: lock,
LeaseDuration: 15 * time.Second, // 租约时长
RenewDeadline: 10 * time.Second, // 续约截止时间
RetryPeriod: 2 * time.Second, // 重试间隔
ReleaseOnCancel: true,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
// 成为 Leader,启动控制器
runController(ctx, clientset)
},
OnStoppedLeading: func() {
// 失去 Leader,退出进程(让 K8s 重启)
os.Exit(0)
},
OnNewLeader: func(identity string) {
if identity == id {
return
}
fmt.Printf("当前 Leader: %s\n", identity)
},
},
})
}查看 Lease 对象
bash
# 查看 Leader 信息
kubectl get lease -n kube-system my-controller-leader -o yaml
# 输出示例
spec:
acquireTime: "2024-01-01T00:00:00Z"
holderIdentity: my-controller-pod-abc123 # 当前 Leader
leaseDurationSeconds: 15
leaseTransitions: 3
renewTime: "2024-01-01T00:01:00Z"