前言
之前分析过K8S的选主流程,整个流程比较复杂。operatorsdk的实现却很简单。
整体流程
以key创建一个configmap,configmap的metadata.ownerReferences设置为pod信息,当pod退出后,configmap也会被删除。
- 获取当前POD的namespace
- 检查cm是否存在,如果存在而且cm.owner.name一致,说明当前pod已经抢到锁
- 创建锁,如果创建成功,代表抢锁成功,可以执行下面流程
- 创建失败,进入死循环,定时尝试建锁,直到创建锁成功后退出循环
代码实现
operator-framework/operator-sdk@v0.10.1/pkg/leader/leader.go:45
抢锁
func Become(ctx context.Context, lockName string) error {
// 获取Operator运行的POD的namespace
ns, err := k8sutil.GetOperatorNamespace()
config, err := config.GetConfig()
client, err := crclient.New(config, crclient.Options{})
// 将configmap的reference
owner, err := myOwnerRef(ctx, client, ns)
if err != nil {
return err
}
// 判断cm是否已经存在(对于POD重启场景可能会发生)
existing := &corev1.ConfigMap{}
key := crclient.ObjectKey{Namespace: ns, Name: lockName}
err = client.Get(ctx, key, existing)
switch {
case err == nil:
// 如果已经存在,而且owner.name一致,说明cm已经存在
for _, existingOwner := range existing.GetOwnerReferences() {
if existingOwner.Name == owner.Name {
log.Info("Found existing lock with my name. I was likely restarted.")
log.Info("Continuing as the leader.")
return nil
} else {
log.Info("Found existing lock", "LockOwner", existingOwner.Name)
}
}
case apierrors.IsNotFound(err):
log.Info("No pre-existing lock was found.")
default:
return err
}
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: lockName,
Namespace: ns,
OwnerReferences: []metav1.OwnerReference{*owner},
},
}
// try to create a lock
backoff := time.Second
for {
// 尝试创建新的cm
err := client.Create(ctx, cm)
switch {
case err == nil:
log.Info("Became the leader.")
return nil
case apierrors.IsAlreadyExists(err):
// 如果已经存在,稍后会重试,结果就一直在这等。
log.Info("Not the leader. Waiting.")
select {
case <-time.After(wait.Jitter(backoff, .2)):
if backoff < maxBackoffInterval {
backoff *= 2
}
continue
case <-ctx.Done():
return ctx.Err()
}
default:
log.Error(err, "Unknown error creating ConfigMap")
return err
}
}
}
设置Ref
func myOwnerRef(ctx context.Context, client crclient.Client, ns string) (*metav1.OwnerReference, error) {
myPod, err := k8sutil.GetPod(ctx, client, ns)
if err != nil {
return nil, err
}
owner := &metav1.OwnerReference{
APIVersion: "v1",
Kind: "Pod",
Name: myPod.ObjectMeta.Name,
UID: myPod.ObjectMeta.UID,
}
return owner, nil
}