中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Kubernetes中鎖機制的設計與實現方法是什么

發布時間:2021-10-29 17:56:04 來源:億速云 閱讀:145 作者:iii 欄目:web開發

這篇文章主要講解了“Kubernetes中鎖機制的設計與實現方法是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Kubernetes中鎖機制的設計與實現方法是什么”吧!

面向終態的鎖基礎篇

在分布式系統中通常由各種各樣的鎖,我們先來看下,主流的鎖里面有哪些共性,以及是如何進行設計的。

分布式系統中的鎖

在分布式系統中鎖有很多種實現方式: 基于CP模型的、基于AP模型的 ,但是這些鎖機制都有一些通用的設計原則,接下來我們先看下這部分。

1. 鎖憑證

鎖憑證主要來證明誰持有鎖,不同系統里面的實現各不相同,比如在zookeeper中是臨時順序節點,而在redission中則是通過uuid+threadID組成,而K8s中則是LeaderElectionRecord,  通過該憑證來識別當前是哪個客戶端加的鎖。

2. 鎖超時

當有leader節點持有鎖之后,其余的節點就需要嘗試競爭鎖,在CP系統中通常會由服務端進行維護,即如果發現對應的節點沒有心跳,則會進行節點的踢出,并且通過watch這種機制進行回調,而在AP系統中則需要客戶端自己維護,比如redission里面的時間戳。

3. 時鐘

在分布式系統中通常我們無法保證各個節點的物理時鐘完全一致,通常就會有一個邏輯時鐘的概念,在很多系統中比如raft和zab中其實就是一個遞增的全局計數器,但是在redission中則是通過物理時鐘,即需要保證大家的物理時鐘盡可能同步,不能超過鎖超時的時間。

網絡分區問題

Kubernetes中鎖機制的設計與實現方法是什么

無論是CP還是AP,在分布式系統中通常我們都要保證P即分區可用性,那如果持有鎖的Leader節點發生網絡分區的情況,則需要一種保護機制,即Leader節點需要主動退出。

在zookeeper中因為leader節點需要通過session來進行心跳的維護,如果說對應的leader節點發生分區,則session就無法進行心跳的發生,就會退出,就需要通知我們的主流程來進行退出清理工作。

資源鎖的實現機制

資源鎖其實就是可以通過操作一個資源(順序一致性),借助前面說的鎖的思想來實現分布式鎖,其首先核心流程如下:

通過資源對象來存儲鎖憑證信息

即將標識當前Leader節點的信息放入到對應的憑證里面,并嘗試進行鎖競爭,進行鎖的獲取的嘗試。

鎖超時

K8s的鎖超時的機制比較有趣,即他并不關心你的邏輯時鐘,而是以本地時鐘為準,即每個節點會存儲觀測到leader節點變更的時間,然后根據本地的鎖超時時間來檢測,是否重新發起leader的競爭。

核心源碼剖析

因為篇幅原因這里只介紹基于configMap的resourceLock, 其他的都大同小異。

LeaderElectionRecord

在我的理解上這個數結構的設計,才是真正的那把鎖(就好像生活中我們可以隨便買把鎖,鎖各種門)。通過這個鎖屏蔽底層的各種鎖實現系統的實現細節,但注意這把鎖并不是嚴格的分布式互斥鎖。

數據結構

在鎖的實現中,數據主要分為三類:身份憑證、時間戳、全局計數器,然后我們依次來看猜下對應的設計思路。

type LeaderElectionRecord struct {     HolderIdentity       string      `json:"holderIdentity"`     LeaseDurationSeconds int         `json:"leaseDurationSeconds"`     AcquireTime          metav1.Time `json:"acquireTime"`     RenewTime            metav1.Time `json:"renewTime"`     LeaderTransitions    int         `json:"leaderTransitions"` }

身份憑證:HolderIdentity

身份憑證主要是用于標識一個節點信息,在一些分布式協調系統中通常都是系統自帶的機制,比如zookeeper中的session,  在此處資源鎖的場景下,主要是為了用于后續流程里驗證當前節點是否獲取到鎖。

時間戳:LeaseDurationSeconds、AcquireTime、RenewTime

因為之前說的時間同步的問題,這里的時間相關的主要是用于leader節點觸發節點變更來使用(Lease類型也在使用),非Leader節點則根據當前記錄是否變更來檢測leader節點是否存活。

LeaderTransitions

計數器主要就是通過計數來記錄leader節點切換的次數。

ConfigMapLock

所謂的資源鎖其實就是通過創建一個ConfigMap實例來保存我們的鎖信息,并通過這個實例信息的維護,來實現鎖的競爭和釋放。

1. 創建鎖

通過利用etcd的冪等性操作,可以保證同時只會有一個leader節點進行鎖創建成功,并且通過Annotations來提交上面說的LeaderElectionRecord來進行鎖的提交。

func (cml *ConfigMapLock) Create(ler LeaderElectionRecord) error {     cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Create(&v1.ConfigMap{         ObjectMeta: metav1.ObjectMeta{             Name:      cml.ConfigMapMeta.Name,             Namespace: cml.ConfigMapMeta.Namespace,             Annotations: map[string]string{                 LeaderElectionRecordAnnotationKey: string(recordBytes),             },         },     })     return err }

2. 獲取鎖

func (cml *ConfigMapLock) Get() (*LeaderElectionRecord, []byte, error) {     cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(cml.ConfigMapMeta.Name, metav1.GetOptions{})     recordBytes, found := cml.cm.Annotations[LeaderElectionRecordAnnotationKey]     if found {         if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {             return nil, nil, err         }     }     return &record, []byte(recordBytes), nil }

3. 更新鎖

func (cml *ConfigMapLock) Update(ler LeaderElectionRecord) error {     cml.cm.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes)     cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Update(cml.cm)     return err }

LeaderElector

LeaderElector的核心流程分為三部分:競爭鎖、超時檢測、心跳維護,首先所有節點都會進行資源鎖的競爭,但是最終只會有一個節點成為Leader節點,  然后核心流程就會按照角色分成兩個主流程, 讓我們一起來看下其實現。

1. 核心流程

如果節點沒有acquire成功則會一直進行嘗試,直至取消或者競選成功,而leader節點則會執行成為  leader節點的回調(補充基于leader的zookeeper的實現機制)

func (le *LeaderElector) Run(ctx context.Context) {     defer func() {         runtime.HandleCrash()         le.config.Callbacks.OnStoppedLeading()     }()     if !le.acquire(ctx)  { // 精選鎖         return // ctx signalled done     }     // 如果鎖競選成功,則leader節點會執行剩余流程,而非leader節點則繼續嘗試acquire     ctx, cancel := context.WithCancel(ctx)     defer cancel()     go le.config.Callbacks.OnStartedLeading(ctx)     le.renew(ctx) }

2. 鎖的續約

如果競選為leader節點,則就需要進行鎖的續約操作,就是通過調用上面提到的更新鎖的操作來,周期性的更新鎖記錄信息即LeaderElectionRecord,從而達到續約的目標。

func (le *LeaderElector) renew(ctx context.Context) {     ctx, cancel := context.WithCancel(ctx)     defer cancel()     wait.Until(func() {         timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)         defer timeoutCancel()         err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) {             done := make(chan bool, 1)             go func() {                 defer close(done)                 // 鎖的續約                 done <- le.tryAcquireOrRenew()             }()              select {             case <-timeoutCtx.Done():                 return false, fmt.Errorf("failed to tryAcquireOrRenew %s", timeoutCtx.Err())             case result := <-done:                 return result, nil             }         }, timeoutCtx.Done())         cancel()     }, le.config.RetryPeriod, ctx.Done())      // if we hold the lease, give it up     if le.config.ReleaseOnCancel {         // 釋放鎖         le.release()     } }

3. 鎖的釋放

鎖的釋放則比較好玩,就是更新對應的資源,去掉annotations里面的信息,這樣在獲取鎖的時候,因為檢測到當前資源沒有被任何憑證信息,就會嘗試進行競選。

func (le *LeaderElector) release() bool {     if !le.IsLeader() {         return true     }     leaderElectionRecord := rl.LeaderElectionRecord{         LeaderTransitions: le.observedRecord.LeaderTransitions,     }     if err := le.config.Lock.Update(leaderElectionRecord); err != nil {         klog.Errorf("Failed to release lock: %v", err)         return false     }     le.observedRecord = leaderElectionRecord     le.observedTime = le.clock.Now()     return true }

4. 鎖的競爭

Kubernetes中鎖機制的設計與實現方法是什么

鎖的競爭整體分為四個部分: 1)獲取鎖 2)創建鎖 3)檢測鎖 4)更新鎖,下面來依次看下對應的實現。

獲取鎖

首先會嘗試獲取對應的鎖,在獲取鎖中會檢測對應的annotations中是否存在,如果不存在則oldLeaderElectionRecord就為空,即當前資源鎖沒有被人持有。

oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get()

創建鎖

如果檢測到對應的鎖不存在,則就會直接進行鎖的創建,如果創建成功則表明當前節點獲取鎖,則就成為leader,執行leader的回調邏輯。

if err != nil {         if !errors.IsNotFound(err) {             klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)             return false         }         // 創建鎖         if err = le.config.Lock.Create(leaderElectionRecord); err != nil {             klog.Errorf("error initially creating leader election record: %v", err)             return false         }         // 記錄當前的選舉記錄,還有時鐘         le.observedRecord = leaderElectionRecord         le.observedTime = le.clock.Now()         return true     }

檢查鎖

在K8s里面并沒有使用邏輯時鐘而是使用本地時間,通過對比每次鎖憑證是否更新,來進行本地observedTime的更新,如果leader沒有在LeaseDuration內來更新對應的鎖憑證信息,則當前節點就會嘗試成為leader。

同時這里還會保障最終的一致性鎖,因為后續的renew其實也是走的這個邏輯,如果說當前節點最開始持有鎖,但是被別的節點搶占,則當前節點會主動讓出鎖。

if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) {         le.observedRecord = *oldLeaderElectionRecord         le.observedRawRecord = oldLeaderElectionRawRecord         le.observedTime = le.clock.Now() // 此處更新的是本地的時鐘     }     if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&         le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&         !le.IsLeader() {         // 如果當前Leader任期沒有超時,則當前競選鎖失敗         klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity)         return false     }

更新鎖

核心邏輯其實就是Lock.Update這個地方,設計的比較有意思,不同于強一致性的鎖,在K8s中我們可以同時有多個節點都走到這里,但是因為更新etcd是一個原子的操作,最終只會有一個節點更新成功,那如何保證最終的鎖的語義呢,其實就要配合上面的檢測鎖,這樣就可以實現一個面向終態的最終的鎖機制。

if le.IsLeader() {         leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime         leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions     } else {         leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1     }      // update the lock itself     if err = le.config.Lock.Update(leaderElectionRecord); err != nil {         klog.Errorf("Failed to update lock: %v", err)         return false     }      le.observedRecord = leaderElectionRecord     le.observedTime = le.clock.Now()     return true

疑問

回過來看鎖是因為最近在做系統設計的時候,想到的一個問題。在PAAS系統中通常會有N多的Operator,那在一些沖突的場景該如何解決呢?比如擴縮容、發布、容災這幾個控制器,如果要操作同一個app下面的pod該如何被調度呢?

其實我理解這個流程中是無法做到各種完美cover各種異常沖突的,但是我們可以玩另外一種有意思的事情,比如我們可以加一個保護狀態,因為對生產穩定壓倒一起。即對應的控制器,關注當前的狀態是否處于穩定狀態,如果是非穩定狀態,則就應該自身凍結,等當前應用處于非保護狀態再進行操作,保證SLA的同時也不影響各種好玩的操作。

感謝各位的閱讀,以上就是“Kubernetes中鎖機制的設計與實現方法是什么”的內容了,經過本文的學習后,相信大家對Kubernetes中鎖機制的設計與實現方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

中西区| 武义县| 房产| 大石桥市| 龙胜| 静宁县| 通海县| 布拖县| 康定县| 惠安县| 包头市| 昌宁县| 文安县| 兴文县| 泰来县| 喀什市| 哈尔滨市| 镇坪县| 宝山区| 大方县| 太谷县| 武陟县| 永仁县| 福海县| 阜新| 越西县| 通海县| 延川县| 东兰县| 安龙县| 宁津县| 抚远县| 隆尧县| 噶尔县| 景德镇市| 蓝山县| 安平县| 腾冲县| 大安市| 运城市| 彭州市|