0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

leader選舉在kubernetes controller中是如何實現的

馬哥Linux運維 ? 來源:Cylon ? 作者:Cylon ? 2022-07-21 10:03 ? 次閱讀

在 Kubernetes 的 kube-controller-manager , kube-scheduler, 以及使用 Operator 的底層實現 controller-rumtime 都支持高可用系統中的 leader 選舉,本文將以理解 controller-rumtime (底層的實現是 client-go) 中的 leader 選舉以在 kubernetes controller 中是如何實現的。

Background

在運行 kube-controller-manager 時,是有一些參數提供給 cm 進行 leader 選舉使用的,可以參考官方文檔提供的 參數來了解相關參數。

--leader-electDefault:true
--leader-elect-renew-deadlinedurationDefault:10s
--leader-elect-resource-lockstringDefault:"leases"
--leader-elect-resource-namestringDefault:"kube-controller-manager"
--leader-elect-resource-namespacestringDefault:"kube-system"
--leader-elect-retry-perioddurationDefault:2s
...

本身以為這些組件的選舉動作時通過 etcd 進行的,但是后面對 controller-runtime 學習時,發(fā)現并沒有配置其相關的 etcd 相關參數,這就引起了對選舉機制的好奇。懷著這種好奇心搜索了下有關于 kubernetes 的選舉,發(fā)現官網是這么介紹的,下面是對官方的說明進行一個通俗總結。simple leader election with kubernetes

?

通過閱讀文章得知,kubernetes API 提供了一中選舉機制,只要運行在集群內的容器,都是可以實現選舉功能的。

Kubernetes API 通過提供了兩個屬性來完成選舉動作的

ResourceVersions:每個 API 對象唯一一個 ResourceVersion

Annotations:每個 API 對象都可以對這些 key 進行注釋

注:這種選舉會增加 APIServer 的壓力。也就對 etcd 會產生影響

那么有了這些信息之后,我們來看一下,在 Kubernetes 集群中,誰是 cm 的 leader(我們提供的集群只有一個節(jié)點,所以本節(jié)點就是 leader)。

在 Kubernetes 中所有啟用了 leader 選舉的服務都會生成一個 EndPoint ,在這個 EndPoint 中會有上面提到的 label(Annotations)來標識誰是 leader。

$kubectlgetep-nkube-system
NAMEENDPOINTSAGE
kube-controller-manager3d4h
kube-dns3d4h
kube-scheduler3d4h

這里以 kube-controller-manager 為例,來看下這個 EndPoint 有什么信息

[root@master-machine~]#kubectldescribeepkube-controller-manager-nkube-system
Name:kube-controller-manager
Namespace:kube-system
Labels:
Annotations:control-plane.alpha.kubernetes.io/leader:
{"holderIdentity":"master-machine_06730140-a503-487d-850b-1fe1619f1fe1","leaseDurationSeconds":15,"acquireTime":"2022-06-27T1546Z","re...
Subsets:
Events:
TypeReasonAgeFromMessage
-------------------------
NormalLeaderElection2d22hkube-controller-managermaster-machine_76aabcb5-49ff-45ff-bd18-4afa61fbc5afbecameleader
NormalLeaderElection9mkube-controller-managermaster-machine_06730140-a503-487d-850b-1fe1619f1fe1becameleader

可以看出 Annotations: control-plane.alpha.kubernetes.io/leader: 標出了哪個 node 是 leader。

election in controller-runtime

controller-runtime 有關 leader 選舉的部分在 pkg/leaderelection下面,總共 100 行代碼,我們來看下做了些什么?

可以看到,這里只提供了創(chuàng)建資源鎖的一些選項

typeOptionsstruct{
//在manager啟動時,決定是否進行選舉
LeaderElectionbool
//使用那種資源鎖默認為租用lease
LeaderElectionResourceLockstring
//選舉發(fā)生的名稱空間
LeaderElectionNamespacestring
//該屬性將決定持有l(wèi)eader鎖資源的名稱
LeaderElectionIDstring
}

通過 NewResourceLock 可以看到,這里是走的 client-go/tools/leaderelection下面,而這個 leaderelection 也有一個 example來學習如何使用它。

通過 example 可以看到,進入選舉的入口是一個 RunOrDie() 的函數

//這里使用了一個lease鎖,注釋中說愿意為集群中存在lease的監(jiān)聽較少
lock:=&resourcelock.LeaseLock{
LeaseMeta:metav1.ObjectMeta{
Name:leaseLockName,
Namespace:leaseLockNamespace,
},
Client:client.CoordinationV1(),
LockConfig:resourcelock.ResourceLockConfig{
Identity:id,
},
}

//開啟選舉循環(huán)
leaderelection.RunOrDie(ctx,leaderelection.LeaderElectionConfig{
Lock:lock,
//這里必須保證擁有的租約在調用cancel()前終止,否則會仍有一個loop在運行
ReleaseOnCancel:true,
LeaseDuration:60*time.Second,
RenewDeadline:15*time.Second,
RetryPeriod:5*time.Second,
Callbacks:leaderelection.LeaderCallbacks{
OnStartedLeading:func(ctxcontext.Context){
//這里填寫你的代碼,
//usuallyputyourcode
run(ctx)
},
OnStoppedLeading:func(){
//這里清理你的lease
klog.Infof("leaderlost:%s",id)
os.Exit(0)
},
OnNewLeader:func(identitystring){
//we'renotifiedwhennewleaderelected
ifidentity==id{
//Ijustgotthelock
return
}
klog.Infof("newleaderelected:%s",identity)
},
},
})

到這里,我們了解了鎖的概念和如何啟動一個鎖,下面看下,client-go 都提供了那些鎖。

在代碼 tools/leaderelection/resourcelock/interface.go[6] 定義了一個鎖抽象,interface 提供了一個通用接口,用于鎖定 leader 選舉中使用的資源。

typeInterfaceinterface{
//Get返回選舉記錄
Get(ctxcontext.Context)(*LeaderElectionRecord,[]byte,error)

//Create創(chuàng)建一個LeaderElectionRecord
Create(ctxcontext.Context,lerLeaderElectionRecord)error

//UpdatewillupdateandexistingLeaderElectionRecord
Update(ctxcontext.Context,lerLeaderElectionRecord)error

//RecordEventisusedtorecordevents
RecordEvent(string)

//Identity返回鎖的標識
Identity()string

//Describeisusedtoconvertdetailsoncurrentresourcelockintoastring
Describe()string
}

那么實現這個抽象接口的就是,實現的資源鎖,我們可以看到,client-go 提供了四種資源鎖

leaselock

configmaplock

multilock

endpointlock

leaselock

Lease 是 kubernetes 控制平面中的通過 ETCD 來實現的一個 Leases 的資源,主要為了提供分布式租約的一種控制機制。相關對這個 API 的描述可以參考于:Lease 。

在 Kubernetes 集群中,我們可以使用如下命令來查看對應的 lease

$kubectlgetleases-A
NAMESPACENAMEHOLDERAGE
kube-node-leasemaster-machinemaster-machine3d19h
kube-systemkube-controller-managermaster-machine_06730140-a503-487d-850b-1fe1619f1fe13d19h
kube-systemkube-schedulermaster-machine_1724e2d9-c19c-48d7-ae47-ee4217b270733d19h

$kubectldescribeleaseskube-controller-manager-nkube-system
Name:kube-controller-manager
Namespace:kube-system
Labels:
Annotations:
APIVersion:coordination.k8s.io/v1
Kind:Lease
Metadata:
CreationTimestamp:2022-06-24T1151Z
ManagedFields:
APIVersion:coordination.k8s.io/v1
FieldsType:FieldsV1
fieldsV1:
f
f
f
f
f
f
Manager:kube-controller-manager
Operation:Update
Time:2022-06-24T1151Z
ResourceVersion:56012
SelfLink:/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-controller-manager
UID:851a32d2-25dc-49b6-a3f7-7a76f152f071
Spec:
AcquireTime:2022-06-27T1546.000000Z
HolderIdentity:master-machine_06730140-a503-487d-850b-1fe1619f1fe1
LeaseDurationSeconds:15
LeaseTransitions:2
RenewTime:2022-06-28T0626.837773Z
Events:

下面來看下 leaselock 的實現,leaselock 會實現了作為資源鎖的抽象

typeLeaseLockstruct{
//LeaseMeta就是類似于其他資源類型的屬性,包含namens以及其他關于lease的屬性
LeaseMetametav1.ObjectMeta
Clientcoordinationv1client.LeasesGetter//Client就是提供了informer中的功能
//lockconfig包含上面通過describe看到的Identity與recoder用于記錄資源鎖的更改
LockConfigResourceLockConfig
//lease就是API中的Lease資源,可以參考下上面給出的這個API的使用
lease*coordinationv1.Lease
}

下面來看下 leaselock 實現了那些方法?

Get

Get是從 spec 中返回選舉的記錄

func(ll*LeaseLock)Get(ctxcontext.Context)(*LeaderElectionRecord,[]byte,error){
varerrerror
ll.lease,err=ll.Client.Leases(ll.LeaseMeta.Namespace).Get(ctx,ll.LeaseMeta.Name,metav1.GetOptions{})
iferr!=nil{
returnnil,nil,err
}
record:=LeaseSpecToLeaderElectionRecord(&ll.lease.Spec)
recordByte,err:=json.Marshal(*record)
iferr!=nil{
returnnil,nil,err
}
returnrecord,recordByte,nil
}

//可以看出是返回這個資源spec里面填充的值
funcLeaseSpecToLeaderElectionRecord(spec*coordinationv1.LeaseSpec)*LeaderElectionRecord{
varrLeaderElectionRecord
ifspec.HolderIdentity!=nil{
r.HolderIdentity=*spec.HolderIdentity
}
ifspec.LeaseDurationSeconds!=nil{
r.LeaseDurationSeconds=int(*spec.LeaseDurationSeconds)
}
ifspec.LeaseTransitions!=nil{
r.LeaderTransitions=int(*spec.LeaseTransitions)
}
ifspec.AcquireTime!=nil{
r.AcquireTime=metav1.Time{spec.AcquireTime.Time}
}
ifspec.RenewTime!=nil{
r.RenewTime=metav1.Time{spec.RenewTime.Time}
}
return&r
}

Create

Create是在 kubernetes 集群中嘗試去創(chuàng)建一個租約,可以看到,Client 就是 API 提供的對應資源的 REST 客戶端,結果會在 Kubernetes 集群中創(chuàng)建這個 Lease

func(ll*LeaseLock)Create(ctxcontext.Context,lerLeaderElectionRecord)error{
varerrerror
ll.lease,err=ll.Client.Leases(ll.LeaseMeta.Namespace).Create(ctx,&coordinationv1.Lease{
ObjectMeta:metav1.ObjectMeta{
Name:ll.LeaseMeta.Name,
Namespace:ll.LeaseMeta.Namespace,
},
Spec:LeaderElectionRecordToLeaseSpec(&ler),
},metav1.CreateOptions{})
returnerr
}

Update

Update是更新 Lease 的 spec

func(ll*LeaseLock)Update(ctxcontext.Context,lerLeaderElectionRecord)error{
ifll.lease==nil{
returnerrors.New("leasenotinitialized,callgetorcreatefirst")
}
ll.lease.Spec=LeaderElectionRecordToLeaseSpec(&ler)

lease,err:=ll.Client.Leases(ll.LeaseMeta.Namespace).Update(ctx,ll.lease,metav1.UpdateOptions{})
iferr!=nil{
returnerr
}

ll.lease=lease
returnnil
}

RecordEvent

RecordEvent是記錄選舉時出現的事件,這時候我們回到上部分 在 kubernetes 集群中查看 ep 的信息時可以看到的 event 中存在 became leader 的事件,這里就是將產生的這個 event 添加到 meta-data 中。

func(ll*LeaseLock)RecordEvent(sstring){
ifll.LockConfig.EventRecorder==nil{
return
}
events:=fmt.Sprintf("%v%v",ll.LockConfig.Identity,s)
subject:=&coordinationv1.Lease{ObjectMeta:ll.lease.ObjectMeta}
//Populatethetypemeta,sowedon'thavetogetitfromtheschema
subject.Kind="Lease"
subject.APIVersion=coordinationv1.SchemeGroupVersion.String()
ll.LockConfig.EventRecorder.Eventf(subject,corev1.EventTypeNormal,"LeaderElection",events)
}

到這里大致上了解了資源鎖究竟是什么了,其他種類的資源鎖也是相同的實現的方式,這里就不過多闡述了;下面的我們來看看選舉的過程。

election workflow

選舉的代碼入口是在 leaderelection.go,這里會繼續(xù)上面的 example 向下分析整個選舉的過程。

前面我們看到了進入選舉的入口是一個 RunOrDie()的函數,那么就繼續(xù)從這里開始來了解。進入 RunOrDie,看到其實只有幾行而已,大致上了解到了 RunOrDie 會使用提供的配置來啟動選舉的客戶端,之后會阻塞,直到 ctx 退出,或停止持有 leader 的租約。

funcRunOrDie(ctxcontext.Context,lecLeaderElectionConfig){
le,err:=NewLeaderElector(lec)
iferr!=nil{
panic(err)
}
iflec.WatchDog!=nil{
lec.WatchDog.SetLeaderElection(le)
}
le.Run(ctx)
}

下面看下 NewLeaderElector做了些什么?可以看到,LeaderElector 是一個結構體,這里只是創(chuàng)建他,這個結構體提供了我們選舉中所需要的一切(LeaderElector 就是 RunOrDie 創(chuàng)建的選舉客戶端)。

funcNewLeaderElector(lecLeaderElectionConfig)(*LeaderElector,error){
iflec.LeaseDuration<=?lec.RenewDeadline?{
??return?nil,?fmt.Errorf("leaseDuration?must?be?greater?than?renewDeadline")
?}
?if?lec.RenewDeadline?<=?time.Duration(JitterFactor*float64(lec.RetryPeriod))?{
??return?nil,?fmt.Errorf("renewDeadline?must?be?greater?than?retryPeriod*JitterFactor")
?}
?if?lec.LeaseDuration?

LeaderElector是建立的選舉客戶端,

typeLeaderElectorstruct{
configLeaderElectionConfig//這個的配置,包含一些時間參數,健康檢查
//recoder相關屬性
observedRecordrl.LeaderElectionRecord
observedRawRecord[]byte
observedTimetime.Time
//usedtoimplementOnNewLeader(),maylagslightlyfromthe
//valueobservedRecord.HolderIdentityifthetransitionhas
//notyetbeenreported.
reportedLeaderstring
//clockiswrapperaroundtimetoallowforlessflakytesting
clockclock.Clock
//鎖定observedRecord
observedRecordLocksync.Mutex
metricsleaderMetricsAdapter
}

可以看到 Run 實現的選舉邏輯就是在初始化客戶端時傳入的 三個 callback

func(le*LeaderElector)Run(ctxcontext.Context){
deferruntime.HandleCrash()
deferfunc(){//退出時執(zhí)行callbacke的OnStoppedLeading
le.config.Callbacks.OnStoppedLeading()
}()

if!le.acquire(ctx){
return
}
ctx,cancel:=context.WithCancel(ctx)
defercancel()
gole.config.Callbacks.OnStartedLeading(ctx)//選舉時,執(zhí)行OnStartedLeading
le.renew(ctx)
}

在 Run 中調用了 acquire,這個是 通過一個 loop 去調用 tryAcquireOrRenew,直到 ctx 傳遞過來結束信號

func(le*LeaderElector)acquire(ctxcontext.Context)bool{
ctx,cancel:=context.WithCancel(ctx)
defercancel()
succeeded:=false
desc:=le.config.Lock.Describe()
klog.Infof("attemptingtoacquireleaderlease%v...",desc)
//jitterUntil是執(zhí)行定時的函數func()是定時任務的邏輯
//RetryPeriod是周期間隔
//JitterFactor是重試系數,類似于延遲隊列中的系數(duration+maxFactor*duration)
//sliding邏輯是否計算在時間內
//上下文傳遞
wait.JitterUntil(func(){
succeeded=le.tryAcquireOrRenew(ctx)
le.maybeReportTransition()
if!succeeded{
klog.V(4).Infof("failedtoacquirelease%v",desc)
return
}
le.config.Lock.RecordEvent("becameleader")
le.metrics.leaderOn(le.config.Name)
klog.Infof("successfullyacquiredlease%v",desc)
cancel()
},le.config.RetryPeriod,JitterFactor,true,ctx.Done())
returnsucceeded
}

這里實際上選舉動作在 tryAcquireOrRenew 中,下面來看下 tryAcquireOrRenew;tryAcquireOrRenew 是嘗試獲得一個 leader 租約,如果已經獲得到了,則更新租約;否則可以得到租約則為 true,反之 false

func(le*LeaderElector)tryAcquireOrRenew(ctxcontext.Context)bool{
now:=metav1.Now()//時間
leaderElectionRecord:=rl.LeaderElectionRecord{//構建一個選舉record
HolderIdentity:le.config.Lock.Identity(),//選舉人的身份特征,ep與主機名有關
LeaseDurationSeconds:int(le.config.LeaseDuration/time.Second),//默認15s
RenewTime:now,//重新獲取時間
AcquireTime:now,//獲得時間
}

//1.從API獲取或創(chuàng)建一個recode,如果可以拿到則已經有租約,反之創(chuàng)建新租約
oldLeaderElectionRecord,oldLeaderElectionRawRecord,err:=le.config.Lock.Get(ctx)
iferr!=nil{
if!errors.IsNotFound(err){
klog.Errorf("errorretrievingresourcelock%v:%v",le.config.Lock.Describe(),err)
returnfalse
}
//創(chuàng)建租約的動作就是新建一個對應的resource,這個lock就是leaderelection提供的四種鎖,
//看你在runOrDie中初始化傳入了什么鎖
iferr=le.config.Lock.Create(ctx,leaderElectionRecord);err!=nil{
klog.Errorf("errorinitiallycreatingleaderelectionrecord:%v",err)
returnfalse
}
//到了這里就已經拿到或者創(chuàng)建了租約,然后記錄其一些屬性,LeaderElectionRecord
le.setObservedRecord(&leaderElectionRecord)

returntrue
}

//2.獲取記錄檢查身份和時間
if!bytes.Equal(le.observedRawRecord,oldLeaderElectionRawRecord){
le.setObservedRecord(oldLeaderElectionRecord)

le.observedRawRecord=oldLeaderElectionRawRecord
}
iflen(oldLeaderElectionRecord.HolderIdentity)>0&&
le.observedTime.Add(le.config.LeaseDuration).After(now.Time)&&
!le.IsLeader(){//不是leader,進行HolderIdentity比較,再加上時間,這個時候沒有到競選其,跳出
klog.V(4).Infof("lockisheldby%vandhasnotyetexpired",oldLeaderElectionRecord.HolderIdentity)
returnfalse
}

// 3.我們將嘗試更新。在這里leaderElectionRecord設置為默認值。讓我們在更新之前更正它。
ifle.IsLeader(){//到這就說明是leader,修正他的時間
leaderElectionRecord.AcquireTime=oldLeaderElectionRecord.AcquireTime
leaderElectionRecord.LeaderTransitions=oldLeaderElectionRecord.LeaderTransitions
}else{//LeaderTransitions就是指leader調整(轉變?yōu)槠渌┝藥状?,如果是?//則為發(fā)生轉變,保持原有值
//反之,則+1
leaderElectionRecord.LeaderTransitions=oldLeaderElectionRecord.LeaderTransitions+1
}
//完事之后更新APIServer中的鎖資源,也就是更新對應的資源的屬性信息
iferr=le.config.Lock.Update(ctx,leaderElectionRecord);err!=nil{
klog.Errorf("Failedtoupdatelock:%v",err)
returnfalse
}
//setObservedRecord是通過一個新的record來更新這個鎖中的record
//操作是安全的,會上鎖保證臨界區(qū)僅可以被一個線程/進程操作
le.setObservedRecord(&leaderElectionRecord)
returntrue
}

到這里,已經完整知道利用 kubernetes 進行選舉的流程都是什么了;下面簡單回顧下,上述 leader 選舉所有的步驟:

首選創(chuàng)建的服務就是該服務的 leader,鎖可以為 lease , endpoint 等資源進行上鎖

已經是 leader 的實例會不斷續(xù)租,租約的默認值是 15 秒 (leaseDuration);leader 在租約滿時更新租約時間(renewTime)。

其他的 follower,會不斷檢查對應資源鎖的存在,如果已經有 leader,那么則檢查 renewTime,如果超過了租用時間(),則表明 leader 存在問題需要重新啟動選舉,直到有 follower 提升為 leader。

而為了避免資源被搶占,Kubernetes API 使用了 ResourceVersion 來避免被重復修改(如果版本號與請求版本號不一致,則表示已經被修改了,那么 APIServer 將返回錯誤)

利用 Leader 機制實現 HA 應用

下面就通過一個 example 來實現一個,利用 kubernetes 提供的選舉機制完成的高可用應用。

代碼實現

如果僅僅是使用 Kubernetes 中的鎖,實現的代碼也只有幾行而已。

packagemain

import(
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"

metav1"k8s.io/apimachinery/pkg/apis/meta/v1"
clientset"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/klog/v2"
)

funcbuildConfig(kubeconfigstring)(*rest.Config,error){
ifkubeconfig!=""{
cfg,err:=clientcmd.BuildConfigFromFlags("",kubeconfig)
iferr!=nil{
returnnil,err
}
returncfg,nil
}

cfg,err:=rest.InClusterConfig()
iferr!=nil{
returnnil,err
}
returncfg,nil
}

funcmain(){
klog.InitFlags(nil)

varkubeconfigstring
varleaseLockNamestring
varleaseLockNamespacestring
varidstring
//初始化客戶端的部分
flag.StringVar(&kubeconfig,"kubeconfig","","absolutepathtothekubeconfigfile")
flag.StringVar(&id,"id","","theholderidentityname")
flag.StringVar(&leaseLockName,"lease-lock-name","","theleaselockresourcename")
flag.StringVar(&leaseLockNamespace,"lease-lock-namespace","","theleaselockresourcenamespace")
flag.Parse()

ifleaseLockName==""{
klog.Fatal("unabletogetleaselockresourcename(missinglease-lock-nameflag).")
}
ifleaseLockNamespace==""{
klog.Fatal("unabletogetleaselockresourcenamespace(missinglease-lock-namespaceflag).")
}
config,err:=buildConfig(kubeconfig)
iferr!=nil{
klog.Fatal(err)
}
client:=clientset.NewForConfigOrDie(config)

run:=func(ctxcontext.Context){
//實現的業(yè)務邏輯,這里僅僅為實驗,就直接打印了
klog.Info("Controllerloop...")

for{
fmt.Println("Iamleader,Iwasworking.")
time.Sleep(time.Second*5)
}
}

//useaGocontextsowecantelltheleaderelectioncodewhenwe
//wanttostepdown
ctx,cancel:=context.WithCancel(context.Background())
defercancel()

//監(jiān)聽系統中斷
ch:=make(chanos.Signal,1)
signal.Notify(ch,os.Interrupt,syscall.SIGTERM)
gofunc(){
<-ch
??klog.Info("Received?termination,?signaling?shutdown")
??cancel()
?}()

?//?創(chuàng)建一個資源鎖
?lock?:=?&resourcelock.LeaseLock{
??LeaseMeta:?metav1.ObjectMeta{
???Name:??????leaseLockName,
???Namespace:?leaseLockNamespace,
??},
??Client:?client.CoordinationV1(),
??LockConfig:?resourcelock.ResourceLockConfig{
???Identity:?id,
??},
?}

?//?開啟一個選舉的循環(huán)
?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)?{
????//?當選舉為leader后所運行的業(yè)務邏輯
????run(ctx)
???},
???OnStoppedLeading:?func()?{
????//?we?can?do?cleanup?here
????klog.Infof("leader?lost:?%s",?id)
????os.Exit(0)
???},
???OnNewLeader:?func(identity?string)?{?//?申請一個選舉時的動作
????if?identity?==?id?{
?????return
????}
????klog.Infof("new?leader?elected:?%s",?identity)
???},
??},
?})
}

?

注:這種 lease 鎖只能在 in-cluster 模式下運行,如果需要類似二進制部署的程序,可以選擇 endpoint 類型的資源鎖。

生成鏡像

這里已經制作好了鏡像并上傳到 dockerhub(cylonchau/leaderelection:v0.0.2)上了,如果只要學習運行原理,則忽略此步驟

FROMgolang:alpineASbuilder
MAINTAINERcylon
WORKDIR/election
COPY./election
ENVGOPROXYhttps://goproxy.cn,direct
RUNGOOS=linuxGOARCH=amd64CGO_ENABLED=0gobuild-oelectormain.go

FROMalpineASrunner
WORKDIR/go/elector
COPY--from=builder/election/elector.
VOLUME["/election"]
ENTRYPOINT["./elector"]

準備資源清單

默認情況下,Kubernetes 運行的 pod 在請求 Kubernetes 集群內資源時,默認的賬戶是沒有權限的,默認服務帳戶無權訪問協調 API,因此我們需要創(chuàng)建另一個 serviceaccount 并相應地設置 對應的 RBAC 權限綁定;在清單中配置上這個 sa,此時所有的 pod 就會有協調鎖的權限了。

apiVersion:v1
kind:ServiceAccount
metadata:
name:sa-leaderelection
---
apiVersion:rbac.authorization.k8s.io/v1
kind:Role
metadata:
name:leaderelection
rules:
-apiGroups:
-coordination.k8s.io
resources:
-leases
verbs:
-'*'
---
apiVersion:rbac.authorization.k8s.io/v1
kind:RoleBinding
metadata:
name:leaderelection
roleRef:
apiGroup:rbac.authorization.k8s.io
kind:Role
name:leaderelection
subjects:
-kind:ServiceAccount
name:sa-leaderelection
---
apiVersion:apps/v1
kind:Deployment
metadata:
labels:
app:leaderelection
name:leaderelection
namespace:default
spec:
replicas:3
selector:
matchLabels:
app:leaderelection
template:
metadata:
labels:
app:leaderelection
spec:
containers:
-image:cylonchau/leaderelection:v0.0.2
imagePullPolicy:IfNotPresent
command:["./elector"]
args:
-"-id=$(POD_NAME)"
-"-lease-lock-name=test"
-"-lease-lock-namespace=default"
env:
-name:POD_NAME
valueFrom:
fieldRef:
apiVersion:v1
fieldPath:metadata.name
name:elector
serviceAccountName:sa-leaderelection

集群中運行

執(zhí)行完清單后,當 pod 啟動后,可以看到會創(chuàng)建出一個 lease。

$kubectlgetlease
NAMEHOLDERAGE
testleaderelection-5644c5f84f-frs5n1s


$kubectldescribelease
Name:test
Namespace:default
Labels:
Annotations:
APIVersion:coordination.k8s.io/v1
Kind:Lease
Metadata:
CreationTimestamp:2022-06-28T1645Z
ManagedFields:
APIVersion:coordination.k8s.io/v1
FieldsType:FieldsV1
fieldsV1:
f
f
f
f
f
f
Manager:elector
Operation:Update
Time:2022-06-28T1645Z
ResourceVersion:131693
SelfLink:/apis/coordination.k8s.io/v1/namespaces/default/leases/test
UID:bef2b164-a117-44bd-bad3-3e651c94c97b
Spec:
AcquireTime:2022-06-28T1645.931873Z
HolderIdentity:leaderelection-5644c5f84f-frs5n
LeaseDurationSeconds:60
LeaseTransitions:0
RenewTime:2022-06-28T1655.963537Z
Events:

通過其持有者的信息查看對應 pod(因為程序中對 holder Identity 設置的是 pod 的名稱),實際上是工作的 pod。

如上實例所述,這是利用 Kubernetes 集群完成的 leader 選舉的方案,雖然這不是最完美解決方案,但這是一種簡單的方法,因為可以無需在集群上部署更多東西或者進行大量的代碼工作就可以利用 Kubernetes 集群來實現一個高可用的 HA 應用。


審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • LEADER
    +關注

    關注

    0

    文章

    89

    瀏覽量

    9910
  • API接口
    +關注

    關注

    1

    文章

    81

    瀏覽量

    10398
  • kubernetes
    +關注

    關注

    0

    文章

    222

    瀏覽量

    8655

原文標題:巧用 Kubernetes 中的 Leader 選舉機制來實現自己的 HA 應用

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Kubernetes的Device Plugin設計解讀

    ,無需修改Kubelet主干代碼,就可以實現支持GPU、FPGA、高性能 NIC、InfiniBand 等各種設備的擴展。該能力Kubernetes 1.8和1.9版本處于Alpha版本,
    發(fā)表于 03-12 16:23

    Kubernetes之路 2 - 利用LXCFS提升容器資源可見性

    lxcfs-proc-meminfo (rw)/proc/stat from lxcfs-proc-stat (rw)...Kubernetes,還可以通過 Preset 實現
    發(fā)表于 04-17 14:05

    Kubernetes Ingress 高可靠部署最佳實踐

    摘要: Kubernetes集群,Ingress作為集群流量接入層,Ingress的高可靠性顯得尤為重要,今天我們主要探討如何部署一套高性能高可靠的Ingress接入層。簡介
    發(fā)表于 04-17 14:35

    再次升級!阿里云Kubernetes日志解決方案

    /刪除/修改AliyunLogConfig資源時,alibaba-log-controller會監(jiān)聽到資源變化,并對應的日志服務上創(chuàng)建/刪除/修改相應的采集配置。以此實現K8S內部
    發(fā)表于 05-28 19:08

    Kubernetes上運行Kubernetes

    拍案叫絕的容器管理平臺卻遲遲未出現。 這樣的局面一直維持到2014年,谷歌將 Kubernetes 項目發(fā)布到開放源代碼社區(qū)之前。 Kubernetes 一開源,企業(yè)或者開發(fā)人員就可以 Ku
    發(fā)表于 09-30 13:33 ?0次下載
    <b class='flag-5'>在</b><b class='flag-5'>Kubernetes</b>上運行<b class='flag-5'>Kubernetes</b>

    Kubernetes API詳解

    的《kubernetes權威指南》一書的第三章3.2節(jié),獲得出版社和作者的獨家授權發(fā)布。本節(jié)重點講述了kubernetes的API概述。 Kubernetes API概述 Kubernetes
    發(fā)表于 10-12 16:19 ?0次下載
    <b class='flag-5'>Kubernetes</b> API詳解

    一種更安全的分布式一致性算法選舉機制

    目前應用于分布式系統的基于選舉的分布式一致性算法(類 Paxos算法),都是采用得到50%以上選票者當選 Leader的方式進行選舉。此種選舉
    發(fā)表于 04-07 10:29 ?9次下載
    一種更安全的分布式一致性算法<b class='flag-5'>選舉</b>機制

    快速了解kubernetes

    Master 即主節(jié)點,負責控制整個 kubernetes 集群。它包括 Api Server、Scheduler、Controller 等組成部分。它們都需要和 Etcd 進行交互以存儲數據。
    發(fā)表于 08-03 10:38 ?311次閱讀

    Kubernetes如何實現灰度發(fā)布

    Kubernetes 作為基礎平臺,提供了強大的容器編排能力。但是在其上部署業(yè)務和服務治理上,仍然會面對一些復雜性和局限性。服務治理上,已經有許多成熟的 ServiceMesh 框架用于擴充其能力
    的頭像 發(fā)表于 09-22 11:33 ?3302次閱讀

    Kubernetes的網絡模型

    kubernetes ,underlay network 中比較典型的例子是通過將宿主機作為路由器設備,Pod 的網絡則通過學習路由條目從而實現跨節(jié)點通訊。
    的頭像 發(fā)表于 12-14 10:07 ?762次閱讀

    帶你快速了解 kubernetes

    節(jié)點,負責控制整個 kubernetes 集群。它包括 Api Server、Scheduler、Controller 等組成部分。它們都需要和 Etcd 進行交互以存儲數據。 Api Server:
    的頭像 發(fā)表于 01-17 10:00 ?1042次閱讀

    基于Kubernetes實現CI/CD配置的流程

    基于 Kubernetes 實現 CI/CD 配置,其實和往常那些 CI/CD 配置并沒有太大區(qū)別。
    的頭像 發(fā)表于 02-08 16:51 ?1240次閱讀

    探討Kubernetes的網絡模型(各種網絡模型分析)

    kubernetes ,underlay network 中比較典型的例子是通過將宿主機作為路由器設備,Pod 的網絡則通過學習路由條目從而實現跨節(jié)點通訊。
    發(fā)表于 08-24 12:44 ?286次閱讀
    探討<b class='flag-5'>Kubernetes</b><b class='flag-5'>中</b>的網絡模型(各種網絡模型分析)

    zookeeper集群主要有哪三種角色

    Zookeeper是一個開源的分布式協調服務,用于維護和管理分布式集群的配置信息、命名服務、分布式鎖、領導者選舉等。Zookeeper集群,主要有以下三種角色:
    的頭像 發(fā)表于 12-03 16:35 ?2714次閱讀

    zookeeper的選舉機制

    ZooKeeper是一個分布式協調服務,主要用于管理分布式系統的配置信息、命名服務、分布式鎖和分布式隊列等。ZooKeeper集群,為了保證高可用性,需要選舉出一個主節(jié)點(
    的頭像 發(fā)表于 12-04 10:39 ?807次閱讀