RM新时代网站-首页

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

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

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

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

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

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

Background

在運行 kube-controller-manager 時,是有一些參數(shù)提供給 cm 進(jìn)行 leader 選舉使用的,可以參考官方文檔提供的 參數(shù)來了解相關(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
...

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

?

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

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

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

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

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

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

在 Kubernetes 中所有啟用了 leader 選舉的服務(wù)都會生成一個 EndPoint ,在這個 EndPoint 中會有上面提到的 label(Annotations)來標(biāo)識誰是 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: 標(biāo)出了哪個 node 是 leader。

election in controller-runtime

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

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

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

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

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

//這里使用了一個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,
//這里必須保證擁有的租約在調(diào)用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返回鎖的標(biāo)識
Identity()string

//Describeisusedtoconvertdetailsoncurrentresourcelockintoastring
Describe()string
}

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

leaselock

configmaplock

multilock

endpointlock

leaselock

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

在 Kubernetes 集群中,我們可以使用如下命令來查看對應(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:

下面來看下 leaselock 的實現(xiàn),leaselock 會實現(xiàn)了作為資源鎖的抽象

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

下面來看下 leaselock 實現(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
}

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

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

election workflow

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

前面我們看到了進(jìn)入選舉的入口是一個 RunOrDie()的函數(shù),那么就繼續(xù)從這里開始來了解。進(jìn)入 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 是一個結(jié)構(gòu)體,這里只是創(chuàng)建他,這個結(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//這個的配置,包含一些時間參數(shù),健康檢查
//recoder相關(guān)屬性
observedRecordrl.LeaderElectionRecord
observedRawRecord[]byte
observedTimetime.Time
//usedtoimplementOnNewLeader(),maylagslightlyfromthe
//valueobservedRecord.HolderIdentityifthetransitionhas
//notyetbeenreported.
reportedLeaderstring
//clockiswrapperaroundtimetoallowforlessflakytesting
clockclock.Clock
//鎖定observedRecord
observedRecordLocksync.Mutex
metricsleaderMetricsAdapter
}

可以看到 Run 實現(xiàn)的選舉邏輯就是在初始化客戶端時傳入的 三個 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 中調(diào)用了 acquire,這個是 通過一個 loop 去調(diào)用 tryAcquireOrRenew,直到 ctx 傳遞過來結(jié)束信號

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ù)func()是定時任務(wù)的邏輯
//RetryPeriod是周期間隔
//JitterFactor是重試系數(shù),類似于延遲隊列中的系數(shù)(duration+maxFactor*duration)
//sliding邏輯是否計算在時間內(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
}

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

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

//1.從API獲取或創(chuàng)建一個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)建租約的動作就是新建一個對應(yīng)的resource,這個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.獲取記錄檢查身份和時間
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比較,再加上時間,這個時候沒有到競選其,跳出
klog.V(4).Infof("lockisheldby%vandhasnotyetexpired",oldLeaderElectionRecord.HolderIdentity)
returnfalse
}

// 3.我們將嘗試更新。在這里leaderElectionRecord設(shè)置為默認(rèn)值。讓我們在更新之前更正它。
ifle.IsLeader(){//到這就說明是leader,修正他的時間
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中的鎖資源,也就是更新對應(yīng)的資源的屬性信息
iferr=le.config.Lock.Update(ctx,leaderElectionRecord);err!=nil{
klog.Errorf("Failedtoupdatelock:%v",err)
returnfalse
}
//setObservedRecord是通過一個新的record來更新這個鎖中的record
//操作是安全的,會上鎖保證臨界區(qū)僅可以被一個線程/進(jìn)程操作
le.setObservedRecord(&leaderElectionRecord)
returntrue
}

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

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

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

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

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

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

下面就通過一個 example 來實現(xiàn)一個,利用 kubernetes 提供的選舉機制完成的高可用應(yīng)用。

代碼實現(xiàn)

如果僅僅是使用 Kubernetes 中的鎖,實現(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){
//實現(xiàn)的業(yè)務(wù)邏輯,這里僅僅為實驗,就直接打印了
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)中斷
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)?{
????//?當(dāng)選舉為leader后所運行的業(yè)務(wù)邏輯
????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 模式下運行,如果需要類似二進(jìn)制部署的程序,可以選擇 endpoint 類型的資源鎖。

生成鏡像

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

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 運行的 pod 在請求 Kubernetes 集群內(nèi)資源時,默認(rèn)的賬戶是沒有權(quán)限的,默認(rèn)服務(wù)帳戶無權(quán)訪問協(xié)調(diào) API,因此我們需要創(chuàng)建另一個 serviceaccount 并相應(yīng)地設(shè)置 對應(yīng)的 RBAC 權(quán)限綁定;在清單中配置上這個 sa,此時所有的 pod 就會有協(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

集群中運行

執(zhí)行完清單后,當(dāng) 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:

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

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


審核編輯:劉清

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

    關(guān)注

    0

    文章

    89

    瀏覽量

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

    關(guān)注

    1

    文章

    84

    瀏覽量

    10437
  • kubernetes
    +關(guān)注

    關(guān)注

    0

    文章

    224

    瀏覽量

    8710

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

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

收藏 人收藏

    評論

    相關(guān)推薦

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

    ,無需修改Kubelet主干代碼,就可以實現(xiàn)支持GPU、FPGA、高性能 NIC、InfiniBand 等各種設(shè)備的擴(kuò)展。該能力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 實現(xiàn)
    發(fā)表于 04-17 14:05

    Kubernetes Ingress 高可靠部署最佳實踐

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

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

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

    Kubernetes上運行Kubernetes

    拍案叫絕的容器管理平臺卻遲遲未出現(xiàn)。 這樣的局面一直維持到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權(quán)威指南》一書的第三章3.2節(jié),獲得出版社和作者的獨家授權(quán)發(fā)布。本節(jié)重點講述了kubernetes的API概述。 Kubernetes API概述 Kubernetes
    發(fā)表于 10-12 16:19 ?0次下載
    <b class='flag-5'>Kubernetes</b> API詳解

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

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

    快速了解kubernetes

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

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

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

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

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

    帶你快速了解 kubernetes

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

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

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

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

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

    zookeeper集群主要有哪三種角色

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

    zookeeper的選舉機制

    ZooKeeper是一個分布式協(xié)調(diào)服務(wù),主要用于管理分布式系統(tǒng)的配置信息、命名服務(wù)、分布式鎖和分布式隊列等。ZooKeeper集群,為了保證高可用性,需要選舉出一個主節(jié)點(
    的頭像 發(fā)表于 12-04 10:39 ?1008次閱讀
    RM新时代网站-首页