從零開始入門 K8s | 手把手帶你理解 etcd

做者 | 曾凡鬆(逐靈) 阿里雲容器平臺高級技術專家算法

本文整理自《CNCF x Alibaba 雲原生技術公開課》第 16 講。後端

導讀:etcd 是用於共享配置和服務發現的分佈式、一致性的 KV 存儲系統。本文從 etcd 項目發展所經歷的幾個重要時刻開始,爲你們介紹了 etcd 的整體架構及其設計中的基本原理。但願可以幫助你們更好的理解和使用 etcd。設計模式

1、etcd 項目的發展歷程

etcd 誕生於 CoreOS 公司,它最初是用於解決集羣管理系統中 OS 升級的分佈式併發控制以及配置文件的存儲與分發等問題。基於此,etcd 被設計爲提供高可用、強一致的小型 keyvalue 數據存儲服務。性能優化

項目當前隸屬於 CNCF 基金會,被 AWS、Google、Microsoft、Alibaba 等大型互聯網公司普遍使用。微信

1.png

最初,在 2013 年 6 月份由 CoreOS 公司向 GitHub 中提交了第一個版本的初始代碼。網絡

到了 2014 年的 6 月,社區發生了一件事情,Kubernetes v0.4 版本發佈。這裏有必要介紹一下 Kubernetes 項目,它首先是一個容器管理平臺,由谷歌開發並貢獻給社區,由於它集齊了谷歌在容器調度以及集羣管理等領域的多年經驗,從誕生之初就備受矚目。在 Kubernetes v0.4 版本中,它使用了 etcd 0.2 版本做爲實驗覈心元數據的存儲服務,自此 etcd 社區獲得了飛速的發展。架構

很快,在 2015 年 2 月份,etcd 發佈了第一個正式的穩定版本 2.0。在 2.0 版本中,etcd 從新設計了 Raft 一致性算法,併爲用戶提供了一個簡單的樹形數據視圖,在 2.0 版本中 etcd 支持每秒超過 1000 次的寫入性能,知足了當時絕大多數的應用場景需求。2.0 版本發佈以後,通過不斷的迭代與改進,其原有的數據存儲方案逐漸成爲了新時期的性能瓶頸,以後 etcd 啓動了 v3 版本的方案設計。併發

2017 年 1 月份的時候,etcd 發佈了 3.1 版本,v3 版本方案基本上標誌着 etcd 技術上全面成熟。在 v3 版本中 etcd 提供了一套全新的 API,從新實現了更高效的一致性讀取方法,而且提供了一個 gRPC 的 proxy 用於擴展 etcd 的讀取性能。同時,在 v3 版本的方案中包含了大量的 GC 優化,在性能優化方面取得了長足的進步,在該版本中 etcd 能夠支持每秒超過 10000 次的寫入。mvc

2018 年,CNCF 基金會下的衆多項目都使用了 etcd 做爲其核心的數據存儲。據不徹底統計,使用 etcd 的項目超過了 30 個,在同年 11 月份,etcd 項目自身也成爲了 CNCF 旗下的孵化項目。進入 CNCF 基金會後,etcd 擁有了超過 400 個貢獻組,其中包含了來自 AWS、Google、Alibaba 等 8 個公司的 9 個項目維護者。less

2019 年,etcd 即將發佈全新的 3.4 版本,該版本由 Google、Alibaba 等公司聯合打造,將進一步改進 etcd 的性能及穩定性,以知足在超大型公司使用中苛刻的場景要求。

2、架構及內部機制解析

整體架構

etcd 是一個分佈式的、可靠的 key-value 存儲系統,它用於存儲分佈式系統中的關鍵數據,這個定義很是重要。

2.png

一個 etcd 集羣,一般會由 3 個或者 5 個節點組成,多個節點之間經過 Raft 一致性算法的完成分佈式一致性協同,算法會選舉出一個主節點做爲 leader,由 leader 負責數據的同步與數據的分發。當 leader 出現故障後系統會自動地選取另外一個節點成爲 leader,並從新完成數據的同步。客戶端在多個節點中,僅須要選擇其中的任意一個就能夠完成數據的讀寫,內部的狀態及數據協同由 etcd 自身完成。

在 etcd 整個架構中,有一個很是關鍵的概念叫作 quorum,quorum 的定義是 (n+1)/2,也就是說超過集羣中半數節點組成的一個團體,在 3 個節點的集羣中,etcd 能夠允許 1 個節點故障,也就是隻要有任何 2 個節點可用,etcd 就能夠繼續提供服務。同理,在 5 個節點的集羣中,只要有任何 3 個節點可用,etcd 就能夠繼續提供服務,這也是 etcd 集羣高可用的關鍵。

在容許部分節點故障以後繼續提供服務,就須要解決一個很是複雜的問題:分佈式一致性。在 etcd 中,該分佈式一致性算法由 Raft 一致性算法完成,這個算法自己是比較複雜的有機會再詳細展開,這裏僅作一個簡單的介紹以方便你們對其有一個基本的認知。Raft 一致性算法可以工做的一個關鍵點是:任意兩個 quorum 的成員之間必定會有一個交集(公共成員),也就是說只要有任意一個 quorum 存活,其中必定存在某一個節點(公共成員),它包含着集羣中全部的被確認提交的數據。正是基於這一原理,Raft 一致性算法設計了一套數據同步機制,在 Leader 任期切換後可以從新同步上一個 quorum 被提交的全部數據,從而保證整個集羣狀態向前推動的過程當中保持數據的一致。

3.png

etcd 內部的機制比較複雜,但 etcd 給客戶提供的接口是簡單直接的。如上圖所示,咱們能夠經過 etcd 提供的客戶端去訪問集羣的數據,也能夠直接經過 http 的方式(相似 curl 命令)直接訪問 etcd。在 etcd 內部,其數據表示也是比較簡單的,咱們能夠直接把 etcd 的數據存儲理解爲一個有序的 map,它存儲着 key-value 數據。同時 etcd 爲了方便客戶端去訂閱數據的變動,也支持了一個 watch 機制,經過 watch 實時地拿到 etcd 中數據的增量更新,從而實現與 etcd 中的數據同步等業務邏輯。

API 介紹

接下來咱們看一下 etcd 提供的接口,這裏將 etcd 的接口分爲了 5 組:

4.png

  • 第一組是 Put 與 Delete。上圖能夠看到 put 與 delete 的操做都很是簡單,只須要提供一個 key 和一個 value,就能夠向集羣中寫入數據了,刪除數據的時候只須要指定 key 便可;
  • 第二組是查詢操做。etcd 支持兩種類型的查詢:第一種是指定單個 key 的查詢,第二種是指定的一個 key 的範圍;
  • 第三組是數據訂閱。etcd 提供了 Watch 機制,咱們能夠利用 watch 實時訂閱到 etcd 中增量的數據更新,watch 支持指定單個 key,也能夠指定一個 key 的前綴,在實際應用場景中的一般會採用第二種形勢;
  • 第四組事務操做。etcd 提供了一個簡單的事務支持,用戶能夠經過指定一組條件知足時執行某些動做,當條件不成立的時候執行另外一組操做,相似於代碼中的 if else 語句,etcd 確保整個操做的原子性;
  • 第五組是 Leases 接口。Leases 接口是分佈式系統中經常使用的一種設計模式,其用法後面會具體展開。

數據版本機制

要正確使用 etcd 的 API,必需要知道內部對應數據版本號的基本原理。

首先 etcd 中有個 term 的概念,表明的是整個集羣 Leader 的任期。當集羣發生 Leader 切換,term 的值就會 +1。在節點故障,或者 Leader 節點網絡出現問題,再或者是將整個集羣中止後再次拉起,都會發生 Leader 的切換。

第二個版本號叫作 revision,revision 表明的是全局數據的版本。當數據發生變動,包括建立、修改、刪除,其 revision 對應的都會 +1。特別的,在集羣中跨 Leader 任期之間,revision 都會保持全局單調遞增。正是 revision 的這一特性,使得集羣中任意一次的修改都對應着一個惟一的 revision,所以咱們能夠經過 revision 來支持數據的 MVCC,也能夠支持數據的 Watch。

對於每個 KeyValue 數據節點,etcd 中都記錄了三個版本:

  • 第一個版本叫作 create_revision,是 KeyValue 在建立時對應的 revision;
  • 第二個叫作 mod_revision,是其數據被操做的時候對應的 revision;
  • 第三個 version 就是一個計數器,表明了 KeyValue 被修改了多少次。

這裏能夠用圖的方式給你們展現一下:

5.png

在同一個 Leader 任期以內,咱們發現全部的修改操做,其對應的 term 值始終都等於 2,而 revision 則保持單調遞增。當重啓集羣以後,咱們會發現全部的修改操做對應的 term 值都變成了 3。在新的 Leader 任期內,全部的 term 值都等於3,且不會發生變化,而對應的 revision 值一樣保持單調遞增。從一個更大的維度去看,能夠發如今 term=2 和 term=3 的兩個 Leader 任期之間,數據對應的 revision 值依舊保持了全局單調遞增。

mvcc & streaming watch

瞭解 etcd 的版本號控制後,接下來如何使用 etcd 多版本號來實現併發控制以及數據訂閱(Watch)。

在 etcd 中支持對同一個 Key 發起屢次數據修改,每次數據修改都對應一個版本號。etcd 在實現上記錄了每一次修改對應的數據,也就就意味着一個 key 在 etcd 中存在多個歷史版本。在查詢數據的時候若是不指定版本號,etcd 會返回 Key 對應的最新版本,固然 etcd 也支持指定一個版本號來查詢歷史數據。

6.png

由於 etcd 將每一次修改都記錄了下來,使用 watch 訂閱數據時,能夠支持從任意歷史時刻(指定 revision)開始建立一個 watcher,在客戶端與 etcd 之間創建一個數據管道,etcd 會推送從指定 revision 開始的全部數據變動。etcd 提供的 watch 機制保證,該 Key 的數據後續的被修改以後,經過這個數據管道即時的推送給客戶端。

以下圖所示,etcd 中全部的數據都存儲在一個 b+tree 中(灰色),該 b+tree 保存在磁盤中,並經過 mmap 的方式映射到內存用來支持快速的訪問。灰色的 b+tree 中維護着 revision 到 value 的映射關係,支持經過 revision 查詢對應的數據。由於 revision 是單調遞增的,當咱們經過 watch 來訂閱指定 revision 以後的數據時,僅須要訂閱該 b+ tree 的數據變化便可。

7.png

在 etcd 內部還維護着另一個 btree(藍色),它管理着 key 到 revision 的映射關係。當客戶端使用 key 查詢數據時,首先須要通過藍色的 btree 將 key 轉化爲對應的 revision,再經過灰色的 btree 查詢到對應的數據。

細心的讀者會發現,etcd 將每一次修改都記錄下來會致使數據持續增加,這會帶來內存及磁盤的空間消耗,同時也會影響 b+tree 的查詢效率。爲了解決這一問題,在 etcd 中會運行一個週期性的 Compaction 的機制來清理歷史數據,將一段時間以前的同一個 Key 的多個歷史版本數據清理掉。最終的結果是灰色的 b+tree 依舊保持單調遞增,但可能會出現一些空洞。

mini-transactions

在理解了 mvcc 機制及 watch 機制以後,繼續看 etcd 提供的 mini-transactions 機制。etcd 的 transaction 機制比較簡單,基本能夠理解爲一段 if-else 程序,在 if 中能夠提供多個操做,以下圖所示:

8.png

If 裏面寫了兩個條件,當 Value(key1) 大於 "bar" 而且 Version(key1) 的版本等於 2 的時候,執行 Then 裏面指定的操做:修改 Key2 的數據爲 valueX,同時刪除 Key3 的數據。若是不知足條件,則執行另一個操做:Key2 修改成 valueY。

在 etcd 內部會保證整個事務操做的原子性。也就是說 If 操做全部的比較條件,其看到的視圖必定是一致的。同時它可以確保多個操做的原子性不會出現 Then 中的操做僅執行了一半的狀況。

經過 etcd 提供的事務操做,咱們能夠在多個競爭中去保證數據讀寫的一致性,好比說前面已經提到過的 Kubernetes 項目,它正是利用了 etcd 的事務機制,來實現多個 KubernetesAPI server 對一樣一個數據修改的一致性。

lease 的概念及用法

lease 是分佈式系統中一個常見的概念,用於表明一個分佈式租約。典型狀況下,在分佈式系統中須要去檢測一個節點是否存活的時,就須要租約機制。

9.png

上圖示例中的代碼示例首先建立了一個 10s 的租約,若是建立租約後不作任何的操做,那麼 10s 以後,這個租約就會自動過時。接着將 key1 和 key2 兩個 key value 綁定到這個租約之上,這樣當租約過時時 etcd 就會自動清理掉 key1 和 key2,使得節點 key1 和 key2 具有了超時自動刪除的能力。

若是但願這個租約永不過時,須要週期性的調用 KeeyAlive 方法刷新租約。好比說須要檢測分佈式系統中一個進程是否存活,能夠在進程中去建立一個租約,並在該進程中週期性的調用 KeepAlive 的方法。若是一切正常,該節點的租約會一致保持,若是這個進程掛掉了,最終這個租約就會自動過時。

在 etcd 中,容許將多個 key 關聯在同一個 lease 之上,這個設計是很是巧妙的,能夠大幅減小 lease 對象刷新帶來的開銷。試想一下,若是有大量的 key 都須要支持相似的租約機制,每個 key 都須要獨立的去刷新租約,這會給  etcd 帶來很是大的壓力。經過多個 key 綁定在同一個 lease 的模式,咱們能夠將超時間類似的 key 聚合在一塊兒,從而大幅減少租約刷新的開銷,在不失靈活性同時可以大幅提升 etcd 支持的使用規模。

3、典型的使用場景介紹

元數據存儲

Kubernetes 將自身所用的狀態存儲在 etcd 中,其狀態數據的高可用交給 etcd 來解決,Kubernetes 系統自身不須要再應對複雜的分佈式系統狀態處理,自身的系統架構獲得了大幅的簡化。

10.png

Server Discovery (Naming Service)

第二個場景是 Service Discovery,也叫作名字服務。在分佈式系統中,一般會出現的一個模式就是須要多個後端(多是成百上千個進程)來提供一組對等的服務,好比說檢索服務、推薦服務。

11.png

對於這樣一種後端服務,一般狀況下爲了簡化後端服務的運維成本(節點故障時隨時被替換),後端的這一進程會被相似 Kubernetes 這樣的集羣管理系統所調度,這樣當用戶(或上游服務)調用過來時,咱們就須要一個服務發現機制來解決服務路由問題。這一服務發現問題能夠利用 etcd 來高效解決,方式以下:

  • 在進程內部啓動以後,能夠將自身所在的地址註冊到 etcd;
  • API 網關夠經過 etcd 及時感知到後端進程的地址,當後端進程發生故障遷移時會從新註冊到 etcd 中,API 網關也可以及時地感知到新的地址;
  • 利用 etcd 提供的 Lease 機制,若是提供服務的進程運行過程當中出現了異常(crash),API 網關也能夠摘除其流量避免調用超時。

在這一架構中,服務狀態數據被 etcd 接管,API 網關自己也是無狀態的,能夠水平地擴展來服務更多的客戶。同時得益於 etcd 的良好性能,能夠支持上萬個後端進程的節點,使得這一架構能夠服務於大型的企業。

Distributed Coordination: leader election

在分佈式系統中,有一種典型的設計模式就是 Master+Slave。一般狀況下,Slave 提供了 CPU、內存、磁盤以及網絡等各類資源 ,而 Master 用來調和這些節點以使其對外提供一個服務(好比分佈式存儲,分佈式計算)。典型的分佈式存儲服務(HDFS)以及分佈式計算服務(Hadoop)它們都是採用了相似這樣的設計模式。這樣的設計模式會有一個典型的問題:Master 節點的可用性。當 Master 故障之後,整個集羣的服務就掛掉了,沒有辦法再服務用戶的請求。

爲了解決這個問題,典型的作法就是啓動多個 Master 節點。由於 Master 節點內會包含控制邏輯,多個節點之間的狀態同步是很是複雜的,這裏最典型的作法就是經過選主的方式,選出其中一個節點做爲主節點來提供服務,另外一個節點處於等待狀態。

12.png

經過 etcd 提供的機制能夠很容易的實現分佈式進程的選主功能,好比能夠經過對同一個 key 的事務寫來實現搶主的邏輯。通常而言,被選主的 Leader 會將本身的 IP 註冊到 etcd 中,使得 Slave 節點可以及時獲取到當前的 Leader 地址,從而使得系統按照以前單個 Master 節點的方式繼續工做。當 Leader 節點發生異常以後,經過 etcd 可以選取出一個新的節點成爲主節點,而且註冊新的 IP 以後,Slave 又可以拉取新的主節點的 IP,繼續恢復服務。

Distributed Coordination 分佈式系統併發控制

在分佈式系統中,當咱們去執行一些任務,好比說去升級 OS、或者說升級 OS 上的軟件的時候、又或者去執行一些計算任務的時候,出於對後端服務的瓶頸或者是業務穩定性的考慮,一般狀況下須要控制任務的併發度。若是該任務缺乏一個調和的 Master 節點,能夠經過 etcd 來完成這樣的分佈式系統工做。

13.png

在這個模式中經過 etcd 去實現一個分佈式的信號量,而且能夠利用 etcd leases 機制來實現自動地剔除掉故障節點。在進程執行過程當中,若是進程的運行週期比較長,咱們能夠將進程運行過程當中的一些狀態數據存儲到 etcd,從而使得當進程故障以後且須要恢復到其餘地方時,可以從 etcd 中去恢復一些執行狀態,而不須要從新去完成整個的計算邏輯,以此來加速整個任務的執行效率。

本文總結

本文分享的主要內容就到此爲止了,這裏爲你們簡單總結一下:

  • 第一部分,爲你們介紹了 etcd 項目是如何誕生的,以及在 etcd 發展過程當中經歷的幾個重要時刻;
  • 第二部分,爲你們介紹了 etcd 的架構以及其內部的基本操做接口,在理解 etcd 是如何實現高可用的基礎之上,展現了 etcd 數據的一些基本操做以及其內部的實現原理;
  • 第三部分,介紹了三種典型的 etcd 使用場景,以及在對應的場景下,分佈式系統的設計思路。

阿里巴巴雲原生微信公衆號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術公衆號。」

相關文章
相關標籤/搜索