概述
Kubernetes 中存在一个 Lease 类型,顾名思义,它的作用就是可以创建一个带租约的资源。假设有多个节点都想竞争 Leader,那么大家就先尝试创建这个带租约的 Lease 资源,如果创建成功,那么你就是 Leader 了。创建成功并不代表你一直都是 Leader,你还得定期(leaseDurationSeconds)地续租,如果没有续租,那么你的租约就会到期,那么就会被其他的参与者抢占了 Leader 的位置,从而失去 Leader。
示例代码
代码的使用方式是这样的:
第一步,得先创建出参与者对象
// we use the Lease lock type since edits to Leases are less common
// and fewer objects in the cluster watch "all Leases".
lock := &resourcelock.LeaseLock{
LeaseMeta: metav1.ObjectMeta{
Name: leaseLockName,
Namespace: leaseLockNamespace,
},
Client: client.CoordinationV1(),
LockConfig: resourcelock.ResourceLockConfig{
Identity: id,
},
}
这里需要指定几个东西,分别是:
- Name:要竞争的锁的名字,所有的参与者使用同一个;
- Namespace:锁所在的 namespace,一般情况下和应用在同一个 NS 就好了;
- Identity:参与者的唯一标识,每个参与者的都不一样,用于标识谁在占有锁;
第二步:参与 Leader 竞选
当确定要参加 Leader 的选举竞争之后,那么就需要实时关注选举的进程,这里有几个点是需要注意的:
type LeaderCallbacks struct {
// OnStartedLeading is called when a LeaderElector client starts leading
OnStartedLeading func(context.Context)
// OnStoppedLeading is called when a LeaderElector client stops leading
OnStoppedLeading func()
// OnNewLeader is called when the client observes a leader that is
// not the previously observed leader. This includes the first observed
// leader when the client starts.
OnNewLeader func(identity string)
}
然后,参与竞选的代码需要这么写:
// start the leader election code loop
leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{
Lock: lock,
ReleaseOnCancel: true,
LeaseDuration: 60 * time.Second,
RenewDeadline: 15 * time.Second,
RetryPeriod: 5 * time.Second,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
run(ctx)
},
OnStoppedLeading: func() {
klog.Infof("leader lost: %s", id)
os.Exit(0)
},
OnNewLeader: func(identity string) {
if identity == id {
return
}
klog.Infof("new leader elected: %s", identity)
},
},
})
这就是 Leader 选举的所有代码了,而你只需要关注当你成为 Leader 或者失去 Leader 的时候,你需要执行什么业务代码。
原理解析
其实这个 Leader 选举的原理很简单,Kubernetes 提供了一种带 “租期” 的类型 Lease,然后,通过创建 Name 唯一的 LeaseLock 资源,多个参与者中只有一个人能创建成功,那么谁创建成功(拥有)这个资源,谁就是 Leader。这里带 ”租期“,所以要求拥有这个资源的 Leader 必须定时续租,如果断租了,那么别的参与者就可以抢占这个 Lock。
因为有多个参与者参与抢占这个资源,所以会出现竞争,而解决竞争的方式 Kubernetes 是通过版本号的乐观锁解决。这个可以参考 4 进行了解 Kubernetes 的乐观锁。虽然在使用的时候,我们对这个版本号没有感觉,但是,通过查看 Kubernetes Client Go 的代码可以发现,这个事情其实是 SDK 帮助我们完成了: