在k8s本身的组件中,kube-scheduler和kube-manager-controller两个组件是有leader选举的,这个选举机制是k8s对于这两个组件的高可用保障。即正常情况下kube-scheduler或kube-manager-controller组件的多个副本只有一个是处于业务逻辑运行状态,其它副本则不断的尝试去获取锁,去竞争leader,直到自己成为leader。如果正在运行的leader因某种原因导致当前进程退出,或者锁丢失,则由其它副本去竞争新的leader,获取leader继而执行业务逻辑。
不光是k8s本身组件用到了这个选举策略,我们自己定义的服务同样可以用这个算法去实现选主。在k8s client包中就提供了接口供用户使用。代码路径在client-go/tools/leaderelection下。
leaderelection 主要是利用了k8s API操作的原子性实现了一个分布式锁,在不断的竞争中进行选举。选中为leader的进行才会执行具体的业务代码,这在k8s中非常的常见,而且我们很方便的利用这个包完成组件的编写,从而实现组件的高可用,比如部署为一个多副本的Deployment,当leader的pod退出后会重新启动,可能锁就被其他pod获取继续执行。
以es operator为例,使用endpoint说明leader选举:
1、首先operator配置文件中可以设置是否开启选举(默认开启)
- operator-manager
- --v=5
- --leader-elect=true
- --operators=escluster
- --lock-object-name=escluster
- --lock-object-namespace={{ .Release.Namespace }}
2、定义一个controller的主控制逻辑,可以是一个函数,比如run(ctx context.Context)
在没有开启选举情况下,那么直接运行run;
3、如果开启选举:
定义该进程唯一id,比如hostname;
根据锁类型new一个锁实体,比如这里使用endpoint:
func DefaultLeaderElectionConfiguration() v1alpha1.LeaderElectionConfiguration {
return v1alpha1.LeaderElectionConfiguration{
LeaderElect: false,
LeaseDuration: metav1.Duration{Duration: DefaultLeaseDuration},
RenewDeadline: metav1.Duration{Duration: DefaultRenewDeadline},
RetryPeriod: metav1.Duration{Duration: DefaultRetryPeriod},
ResourceLock: rl.EndpointsResourceLock,
LockObjectNamespace: DefaultLockObjectNamespace,
LockObjectName: DefaultLockObjectName,
}
}
设计锁竞争的控制逻辑,callback里面定义抢占锁后执行的业务逻辑(比如这里执行run);进程退出执行的逻辑;产生新的锁执行的逻辑等:
leaderelection.RunOrDie(context.TODO(), leaderelection.LeaderElectionConfig{
Lock: rl,
LeaseDuration: s.LeaderElection.LeaseDuration.Duration,
RenewDeadline: s.LeaderElection.RenewDeadline.Duration,
RetryPeriod: s.LeaderElection.RetryPeriod.Duration,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: run,
OnStoppedLeading: func() {
klog.Fatalf("leaderelection lost")
},
},
WatchDog: electionChecker,
Name: s.LeaderElection.LockObjectName,
})
4、某进程抢占锁后,查询对应ns中ep,内容如下:
apiVersion: v1
kind: Endpoints
metadata:
annotations:
control-plane.alpha.kubernetes.io/leader: '{"holderIdentity":"elasticsearch-operator-cb4bbc85f-wnjx4","leaseDurationSeconds":15,"acquireTime":"2022-12-19T08:22:42Z","renewTime":"2023-01-31T11:53:01Z","leaderTransitions":0}'
creationTimestamp: "2022-12-19T08:21:15Z"
name: escluster
namespace: middleware-operator
resourceVersion: "165028190"
selfLink: /api/v1/namespaces/middleware-operator/endpoints/escluster
uid: 3ffc4bae-0433-4a78-9ef1-44c57891b95b