前言
之前分析過(guò)K8S的選主流程轴总,整個(gè)流程比較復(fù)雜。operatorsdk的實(shí)現(xiàn)卻很簡(jiǎn)單魄鸦。
整體流程
以key創(chuàng)建一個(gè)configmap前酿,configmap的metadata.ownerReferences設(shè)置為pod信息蓄诽,當(dāng)pod退出后碌尔,configmap也會(huì)被刪除旺罢。
- 獲取當(dāng)前POD的namespace
- 檢查cm是否存在淹仑,如果存在而且cm.owner.name一致,說(shuō)明當(dāng)前pod已經(jīng)搶到鎖
- 創(chuàng)建鎖令杈,如果創(chuàng)建成功走敌,代表?yè)屾i成功,可以執(zhí)行下面流程
- 創(chuàng)建失敗逗噩,進(jìn)入死循環(huán)掉丽,定時(shí)嘗試建鎖,直到創(chuàng)建鎖成功后退出循環(huán)
代碼實(shí)現(xiàn)
operator-framework/operator-sdk@v0.10.1/pkg/leader/leader.go:45
搶鎖
func Become(ctx context.Context, lockName string) error {
// 獲取Operator運(yùn)行的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是否已經(jīng)存在(對(duì)于POD重啟場(chǎng)景可能會(huì)發(fā)生)
existing := &corev1.ConfigMap{}
key := crclient.ObjectKey{Namespace: ns, Name: lockName}
err = client.Get(ctx, key, existing)
switch {
case err == nil:
// 如果已經(jīng)存在异雁,而且owner.name一致捶障,說(shuō)明cm已經(jīng)存在
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 {
// 嘗試創(chuàng)建新的cm
err := client.Create(ctx, cm)
switch {
case err == nil:
log.Info("Became the leader.")
return nil
case apierrors.IsAlreadyExists(err):
// 如果已經(jīng)存在,稍后會(huì)重試纲刀,結(jié)果就一直在這等项炼。
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
}
}
}
設(shè)置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
}