成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Kubernetes中鎖機制的設計與實現

開發 前端
資源鎖是通過一個資源的CRUD操作,然后配合分布式鎖的一些機制來完成,分布式環境中Leader節點的選舉,今天我們來臆測下K8s里面是如何基于configMap來實現的吧。

資源鎖是通過一個資源的CRUD操作,然后配合分布式鎖的一些機制來完成,分布式環境中Leader節點的選舉,今天我們來臆測下K8s里面是如何基于configMap來實現的吧。

面向終態的鎖基礎篇

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

分布式系統中的鎖

 

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

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

數據結構

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

  1. type LeaderElectionRecord struct { 
  2.     HolderIdentity       string      `json:"holderIdentity"
  3.     LeaseDurationSeconds int         `json:"leaseDurationSeconds"
  4.     AcquireTime          metav1.Time `json:"acquireTime"
  5.     RenewTime            metav1.Time `json:"renewTime"
  6.     LeaderTransitions    int         `json:"leaderTransitions"

身份憑證:HolderIdentity

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

時間戳:LeaseDurationSeconds、AcquireTime、RenewTime

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

  1. LeaderTransitions 

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

ConfigMapLock

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

1. 創建鎖

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

  1. func (cml *ConfigMapLock) Create(ler LeaderElectionRecord) error { 
  2.     cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Create(&v1.ConfigMap{ 
  3.         ObjectMeta: metav1.ObjectMeta{ 
  4.             Name:      cml.ConfigMapMeta.Name
  5.             Namespace: cml.ConfigMapMeta.Namespace, 
  6.             Annotations: map[string]string{ 
  7.                 LeaderElectionRecordAnnotationKey: string(recordBytes), 
  8.             }, 
  9.         }, 
  10.     }) 
  11.     return err 

2. 獲取鎖

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

3. 更新鎖

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

LeaderElector

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

1. 核心流程

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

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

2. 鎖的續約

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

  1. func (le *LeaderElector) renew(ctx context.Context) { 
  2.     ctx, cancel := context.WithCancel(ctx) 
  3.     defer cancel() 
  4.     wait.Until(func() { 
  5.         timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline) 
  6.         defer timeoutCancel() 
  7.         err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) { 
  8.             done := make(chan bool, 1) 
  9.             go func() { 
  10.                 defer close(done) 
  11.                 // 鎖的續約 
  12.                 done <- le.tryAcquireOrRenew() 
  13.             }() 
  14.  
  15.             select { 
  16.             case <-timeoutCtx.Done(): 
  17.                 return false, fmt.Errorf("failed to tryAcquireOrRenew %s", timeoutCtx.Err()) 
  18.             case result := <-done: 
  19.                 return result, nil 
  20.             } 
  21.         }, timeoutCtx.Done()) 
  22.         cancel() 
  23.     }, le.config.RetryPeriod, ctx.Done()) 
  24.  
  25.     // if we hold the lease, give it up 
  26.     if le.config.ReleaseOnCancel { 
  27.         // 釋放鎖 
  28.         le.release() 
  29.     } 

3. 鎖的釋放

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

  1. func (le *LeaderElector) release() bool { 
  2.     if !le.IsLeader() { 
  3.         return true 
  4.     } 
  5.     leaderElectionRecord := rl.LeaderElectionRecord{ 
  6.         LeaderTransitions: le.observedRecord.LeaderTransitions, 
  7.     } 
  8.     if err := le.config.Lock.Update(leaderElectionRecord); err != nil { 
  9.         klog.Errorf("Failed to release lock: %v", err) 
  10.         return false 
  11.     } 
  12.     le.observedRecord = leaderElectionRecord 
  13.     le.observedTime = le.clock.Now() 
  14.     return true 

4. 鎖的競爭

 

Kubernetes中鎖機制的設計與實現

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

獲取鎖

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

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

創建鎖

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

  1. if err != nil { 
  2.         if !errors.IsNotFound(err) { 
  3.             klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err) 
  4.             return false 
  5.         } 
  6.         // 創建鎖 
  7.         if err = le.config.Lock.Create(leaderElectionRecord); err != nil { 
  8.             klog.Errorf("error initially creating leader election record: %v", err) 
  9.             return false 
  10.         } 
  11.         // 記錄當前的選舉記錄,還有時鐘 
  12.         le.observedRecord = leaderElectionRecord 
  13.         le.observedTime = le.clock.Now() 
  14.         return true 
  15.     } 

檢查鎖

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

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

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

更新鎖

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

  1. if le.IsLeader() { 
  2.         leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime 
  3.         leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions 
  4.     } else { 
  5.         leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1 
  6.     } 
  7.  
  8.     // update the lock itself 
  9.     if err = le.config.Lock.Update(leaderElectionRecord); err != nil { 
  10.         klog.Errorf("Failed to update lock: %v", err) 
  11.         return false 
  12.     } 
  13.  
  14.     le.observedRecord = leaderElectionRecord 
  15.     le.observedTime = le.clock.Now() 
  16.     return true 

疑問

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

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

責任編輯:未麗燕 來源: 今日頭條
相關推薦

2023-07-07 10:41:00

Javajar文件

2022-03-11 09:12:06

MySQLMDL

2025-06-03 00:00:02

Go協程鎖機制

2024-07-29 09:57:47

2025-02-26 10:49:14

2024-05-13 17:40:09

JavaLocking

2016-08-12 15:59:13

Kubernetes華為

2010-07-26 15:17:46

SQL Server鎖

2024-02-20 09:50:02

Redis分布式

2021-03-30 09:45:11

悲觀鎖樂觀鎖Optimistic

2011-06-13 10:21:25

QT 信號 槽機制

2025-03-20 09:54:47

2024-03-18 12:21:28

Java輕量級鎖重量級鎖

2025-05-22 08:15:00

2024-07-25 09:01:22

2020-07-01 08:05:46

Kubernetes容器開發

2020-04-24 15:44:50

MySQL數據庫鎖機制

2013-06-06 13:10:44

HashMap無鎖

2015-05-20 10:05:10

Ceph分布式文件系統序列化

2021-11-22 08:00:00

Kubernetes容器集群
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产91在线 | 亚洲 | 男女一区二区三区 | 超碰人人做 | 四虎最新 | 在线精品一区二区三区 | 一区二区三区精品在线 | 欧美成年黄网站色视频 | 精品99久久久久久 | 日本一本在线 | 国产精品v | 欧美激情a∨在线视频播放 成人免费共享视频 | 国产ts人妖一区二区三区 | 欧美成人精品激情在线观看 | 成人免费视屏 | 免费观看成人性生生活片 | 欧美精品一区二区三区蜜桃视频 | 97国产一区二区精品久久呦 | www312aⅴ欧美在线看 | 精品国产31久久久久久 | 久久久久无码国产精品一区 | 久久久久久亚洲精品 | 国产不卡视频 | 欧美一区二区三区免费电影 | 色综合天天网 | 精品国产免费一区二区三区五区 | 亚洲h色 | 日韩精品二区 | 天天影视网天天综合色在线播放 | 亚洲一区二区视频 | 国产精品久久久久久久久久三级 | 亚洲欧美精品久久 | 紧缚调教一区二区三区视频 | 国产aⅴ精品 | 日韩欧美国产精品一区二区 | 91网站在线看 | 污免费网站 | 欧美高清hd | 精品久久久久久久久久久久 | 亚洲精品自在在线观看 | 中文字幕乱码视频32 | 亚洲一区中文字幕 |