在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

leader選舉在kubernetes controller中是如何實(shí)現(xiàn)的

馬哥Linux運(yùn)維 ? 來(lái)源:Cylon ? 作者:Cylon ? 2022-07-21 10:03 ? 次閱讀

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

Background

在運(yùn)行 kube-controller-manager 時(shí),是有一些參數(shù)提供給 cm 進(jìn)行 leader 選舉使用的,可以參考官方文檔提供的 參數(shù)來(lái)了解相關(guān)參數(shù)。

--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
...

本身以為這些組件的選舉動(dòng)作時(shí)通過(guò) etcd 進(jìn)行的,但是后面對(duì) controller-runtime 學(xué)習(xí)時(shí),發(fā)現(xiàn)并沒(méi)有配置其相關(guān)的 etcd 相關(guān)參數(shù),這就引起了對(duì)選舉機(jī)制的好奇。懷著這種好奇心搜索了下有關(guān)于 kubernetes 的選舉,發(fā)現(xiàn)官網(wǎng)是這么介紹的,下面是對(duì)官方的說(shuō)明進(jìn)行一個(gè)通俗總結(jié)。simple leader election with kubernetes

?

通過(guò)閱讀文章得知,kubernetes API 提供了一中選舉機(jī)制,只要運(yùn)行在集群內(nèi)的容器,都是可以實(shí)現(xiàn)選舉功能的。

Kubernetes API 通過(guò)提供了兩個(gè)屬性來(lái)完成選舉動(dòng)作的

ResourceVersions:每個(gè) API 對(duì)象唯一一個(gè) ResourceVersion

Annotations:每個(gè) API 對(duì)象都可以對(duì)這些 key 進(jìn)行注釋

注:這種選舉會(huì)增加 APIServer 的壓力。也就對(duì) etcd 會(huì)產(chǎn)生影響

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

在 Kubernetes 中所有啟用了 leader 選舉的服務(wù)都會(huì)生成一個(gè) EndPoint ,在這個(gè) EndPoint 中會(huì)有上面提到的 label(Annotations)來(lái)標(biāo)識(shí)誰(shuí)是 leader。

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

這里以 kube-controller-manager 為例,來(lái)看下這個(gè) 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: 標(biāo)出了哪個(gè) node 是 leader。

election in controller-runtime

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

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

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

通過(guò) NewResourceLock 可以看到,這里是走的 client-go/tools/leaderelection下面,而這個(gè) leaderelection 也有一個(gè) example來(lái)學(xué)習(xí)如何使用它。

通過(guò) example 可以看到,進(jìn)入選舉的入口是一個(gè) RunOrDie() 的函數(shù)

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

//開(kāi)啟選舉循環(huán)
leaderelection.RunOrDie(ctx,leaderelection.LeaderElectionConfig{
Lock:lock,
//這里必須保證擁有的租約在調(diào)用cancel()前終止,否則會(huì)仍有一個(gè)loop在運(yùn)行
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)
},
},
})

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

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

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

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

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

//RecordEventisusedtorecordevents
RecordEvent(string)

//Identity返回鎖的標(biāo)識(shí)
Identity()string

//Describeisusedtoconvertdetailsoncurrentresourcelockintoastring
Describe()string
}

那么實(shí)現(xiàn)這個(gè)抽象接口的就是,實(shí)現(xiàn)的資源鎖,我們可以看到,client-go 提供了四種資源鎖

leaselock

configmaplock

multilock

endpointlock

leaselock

Lease 是 kubernetes 控制平面中的通過(guò) ETCD 來(lái)實(shí)現(xiàn)的一個(gè) Leases 的資源,主要為了提供分布式租約的一種控制機(jī)制。相關(guān)對(duì)這個(gè) API 的描述可以參考于:Lease 。

在 Kubernetes 集群中,我們可以使用如下命令來(lái)查看對(duì)應(yīng)的 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:

下面來(lái)看下 leaselock 的實(shí)現(xiàn),leaselock 會(huì)實(shí)現(xiàn)了作為資源鎖的抽象

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

下面來(lái)看下 leaselock 實(shí)現(xiàn)了那些方法?

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
}

//可以看出是返回這個(gè)資源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)建一個(gè)租約,可以看到,Client 就是 API 提供的對(duì)應(yīng)資源的 REST 客戶端,結(jié)果會(huì)在 Kubernetes 集群中創(chuàng)建這個(gè) 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是記錄選舉時(shí)出現(xiàn)的事件,這時(shí)候我們回到上部分 在 kubernetes 集群中查看 ep 的信息時(shí)可以看到的 event 中存在 became leader 的事件,這里就是將產(chǎn)生的這個(gè) 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)
}

到這里大致上了解了資源鎖究竟是什么了,其他種類的資源鎖也是相同的實(shí)現(xiàn)的方式,這里就不過(guò)多闡述了;下面的我們來(lái)看看選舉的過(guò)程。

election workflow

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

前面我們看到了進(jìn)入選舉的入口是一個(gè) RunOrDie()的函數(shù),那么就繼續(xù)從這里開(kāi)始來(lái)了解。進(jìn)入 RunOrDie,看到其實(shí)只有幾行而已,大致上了解到了 RunOrDie 會(huì)使用提供的配置來(lái)啟動(dòng)選舉的客戶端,之后會(huì)阻塞,直到 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 是一個(gè)結(jié)構(gòu)體,這里只是創(chuàng)建他,這個(gè)結(jié)構(gòu)體提供了我們選舉中所需要的一切(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//這個(gè)的配置,包含一些時(shí)間參數(shù),健康檢查
//recoder相關(guān)屬性
observedRecordrl.LeaderElectionRecord
observedRawRecord[]byte
observedTimetime.Time
//usedtoimplementOnNewLeader(),maylagslightlyfromthe
//valueobservedRecord.HolderIdentityifthetransitionhas
//notyetbeenreported.
reportedLeaderstring
//clockiswrapperaroundtimetoallowforlessflakytesting
clockclock.Clock
//鎖定observedRecord
observedRecordLocksync.Mutex
metricsleaderMetricsAdapter
}

可以看到 Run 實(shí)現(xiàn)的選舉邏輯就是在初始化客戶端時(shí)傳入的 三個(gè) callback

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

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

在 Run 中調(diào)用了 acquire,這個(gè)是 通過(guò)一個(gè) loop 去調(diào)用 tryAcquireOrRenew,直到 ctx 傳遞過(guò)來(lái)結(jié)束信號(hào)

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í)行定時(shí)的函數(shù)func()是定時(shí)任務(wù)的邏輯
//RetryPeriod是周期間隔
//JitterFactor是重試系數(shù),類似于延遲隊(duì)列中的系數(shù)(duration+maxFactor*duration)
//sliding邏輯是否計(jì)算在時(shí)間內(nèi)
//上下文傳遞
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
}

這里實(shí)際上選舉動(dòng)作在 tryAcquireOrRenew 中,下面來(lái)看下 tryAcquireOrRenew;tryAcquireOrRenew 是嘗試獲得一個(gè) leader 租約,如果已經(jīng)獲得到了,則更新租約;否則可以得到租約則為 true,反之 false

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

//1.從API獲取或創(chuàng)建一個(gè)recode,如果可以拿到則已經(jīng)有租約,反之創(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)建租約的動(dòng)作就是新建一個(gè)對(duì)應(yīng)的resource,這個(gè)lock就是leaderelection提供的四種鎖,
//看你在runOrDie中初始化傳入了什么鎖
iferr=le.config.Lock.Create(ctx,leaderElectionRecord);err!=nil{
klog.Errorf("errorinitiallycreatingleaderelectionrecord:%v",err)
returnfalse
}
//到了這里就已經(jīng)拿到或者創(chuàng)建了租約,然后記錄其一些屬性,LeaderElectionRecord
le.setObservedRecord(&leaderElectionRecord)

returntrue
}

//2.獲取記錄檢查身份和時(shí)間
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,進(jìn)行HolderIdentity比較,再加上時(shí)間,這個(gè)時(shí)候沒(méi)有到競(jìng)選其,跳出
klog.V(4).Infof("lockisheldby%vandhasnotyetexpired",oldLeaderElectionRecord.HolderIdentity)
returnfalse
}

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

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

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

已經(jīng)是 leader 的實(shí)例會(huì)不斷續(xù)租,租約的默認(rèn)值是 15 秒 (leaseDuration);leader 在租約滿時(shí)更新租約時(shí)間(renewTime)。

其他的 follower,會(huì)不斷檢查對(duì)應(yīng)資源鎖的存在,如果已經(jīng)有 leader,那么則檢查 renewTime,如果超過(guò)了租用時(shí)間(),則表明 leader 存在問(wèn)題需要重新啟動(dòng)選舉,直到有 follower 提升為 leader。

而為了避免資源被搶占,Kubernetes API 使用了 ResourceVersion 來(lái)避免被重復(fù)修改(如果版本號(hào)與請(qǐng)求版本號(hào)不一致,則表示已經(jīng)被修改了,那么 APIServer 將返回錯(cuò)誤)

利用 Leader 機(jī)制實(shí)現(xiàn) HA 應(yīng)用

下面就通過(guò)一個(gè) example 來(lái)實(shí)現(xiàn)一個(gè),利用 kubernetes 提供的選舉機(jī)制完成的高可用應(yīng)用。

代碼實(shí)現(xiàn)

如果僅僅是使用 Kubernetes 中的鎖,實(shí)現(xiàn)的代碼也只有幾行而已。

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){
//實(shí)現(xiàn)的業(yè)務(wù)邏輯,這里僅僅為實(shí)驗(yàn),就直接打印了
klog.Info("Controllerloop...")

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

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

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

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

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

?

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

生成鏡像

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

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"]

準(zhǔn)備資源清單

默認(rèn)情況下,Kubernetes 運(yùn)行的 pod 在請(qǐng)求 Kubernetes 集群內(nèi)資源時(shí),默認(rèn)的賬戶是沒(méi)有權(quán)限的,默認(rèn)服務(wù)帳戶無(wú)權(quán)訪問(wèn)協(xié)調(diào) API,因此我們需要?jiǎng)?chuàng)建另一個(gè) serviceaccount 并相應(yīng)地設(shè)置 對(duì)應(yīng)的 RBAC 權(quán)限綁定;在清單中配置上這個(gè) sa,此時(shí)所有的 pod 就會(huì)有協(xié)調(diào)鎖的權(quán)限了。

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

集群中運(yùn)行

執(zhí)行完清單后,當(dāng) pod 啟動(dòng)后,可以看到會(huì)創(chuàng)建出一個(gè) 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:

通過(guò)其持有者的信息查看對(duì)應(yīng) pod(因?yàn)槌绦蛑袑?duì) holder Identity 設(shè)置的是 pod 的名稱),實(shí)際上是工作的 pod。

如上實(shí)例所述,這是利用 Kubernetes 集群完成的 leader 選舉的方案,雖然這不是最完美解決方案,但這是一種簡(jiǎn)單的方法,因?yàn)榭梢詿o(wú)需在集群上部署更多東西或者進(jìn)行大量的代碼工作就可以利用 Kubernetes 集群來(lái)實(shí)現(xiàn)一個(gè)高可用的 HA 應(yīng)用。


審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • LEADER
    +關(guān)注

    關(guān)注

    0

    文章

    89

    瀏覽量

    10000
  • API接口
    +關(guān)注

    關(guān)注

    1

    文章

    84

    瀏覽量

    10525
  • kubernetes
    +關(guān)注

    關(guān)注

    0

    文章

    227

    瀏覽量

    8757

原文標(biāo)題:巧用 Kubernetes 中的 Leader 選舉機(jī)制來(lái)實(shí)現(xiàn)自己的 HA 應(yīng)用

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Kubernetes的Device Plugin設(shè)計(jì)解讀

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

    Kubernetes之路 2 - 利用LXCFS提升容器資源可見(jiàn)性

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

    Kubernetes Ingress 高可靠部署最佳實(shí)踐

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

    再次升級(jí)!阿里云Kubernetes日志解決方案

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

    Kubernetes上運(yùn)行Kubernetes

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

    Kubernetes API詳解

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

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

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

    快速了解kubernetes

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

    Kubernetes如何實(shí)現(xiàn)灰度發(fā)布

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

    Kubernetes的網(wǎng)絡(luò)模型

    kubernetes ,underlay network 中比較典型的例子是通過(guò)將宿主機(jī)作為路由器設(shè)備,Pod 的網(wǎng)絡(luò)則通過(guò)學(xué)習(xí)路由條目從而實(shí)現(xiàn)跨節(jié)點(diǎn)通訊。
    的頭像 發(fā)表于 12-14 10:07 ?905次閱讀

    帶你快速了解 kubernetes

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

    基于Kubernetes實(shí)現(xiàn)CI/CD配置的流程

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

    探討Kubernetes的網(wǎng)絡(luò)模型(各種網(wǎng)絡(luò)模型分析)

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

    zookeeper集群主要有哪三種角色

    Zookeeper是一個(gè)開(kāi)源的分布式協(xié)調(diào)服務(wù),用于維護(hù)和管理分布式集群的配置信息、命名服務(wù)、分布式鎖、領(lǐng)導(dǎo)者選舉等。Zookeeper集群,主要有以下三種角色:
    的頭像 發(fā)表于 12-03 16:35 ?3288次閱讀

    zookeeper的選舉機(jī)制

    ZooKeeper是一個(gè)分布式協(xié)調(diào)服務(wù),主要用于管理分布式系統(tǒng)的配置信息、命名服務(wù)、分布式鎖和分布式隊(duì)列等。ZooKeeper集群,為了保證高可用性,需要選舉出一個(gè)主節(jié)點(diǎn)(
    的頭像 發(fā)表于 12-04 10:39 ?1087次閱讀
    主站蜘蛛池模板: 香蕉视频黄色片 | 欧美黄色大全 | 欧美黄一片 | 手机看片1024国产基地 | 黄色免费看视频 | 精品视频一区在线观看 | 国产精品免费视频拍拍拍 | 日本人的色道www免费一区 | 国产精品日韩欧美亚洲另类 | 真实女人寂寞偷人视频 | 欧美一区二区三区视频在线观看 | 天天夜夜操 | 色倩网站 | 日本三级三级三级免费看 | 四虎影视永久在线精品免费播放 | 天天碰天天干 | 五月婷婷网站 | 五月天激激婷婷大综合丁香 | 中文字幕在线一区二区三区 | 国产免费播放 | 99精品国产第一福利网站 | 婷婷色六月 | 成人区精品一区二区毛片不卡 | 天堂网www天堂在线资源链接 | 欧美人与动欧交视频 | 一区在线观看 | 婷婷色在线观看 | 欧美三级一级 | 免费看黄视频网站 | 操片免费| 日韩啪啪片| 欧美特黄一免在线观看 | 特黄特色三级在线播放 | 1024你懂的国产精品 | 一区二区三区网站在线免费线观看 | bt天堂资源种子在线8 | 黄色成人在线 | 色综合久久久久久久久五月 | 美女视频永久黄网站免费观看国产 | 在线麻豆国产传媒60在线观看 | 黄 色 片免费观看 |