天天看點

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

作者 | 曾凡松(逐靈) 阿裡雲容器平台進階技術專家

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

更多雲原生技術資訊可關注

阿裡巴巴雲原生技術圈

導讀:etcd 是用于共享配置和服務發現的分布式、一緻性的 KV 存儲系統。本文從 etcd 項目發展所經曆的幾個重要時刻開始,為大家介紹了 etcd 的總體架構及其設計中的基本原理。希望能夠幫助大家更好的了解和使用 etcd。

一、etcd 項目的發展曆程

etcd 誕生于 CoreOS 公司,它最初是用于解決叢集管理系統中 OS 更新的分布式并發控制以及配置檔案的存儲與分發等問題。基于此,etcd 被設計為提供高可用、強一緻的小型 keyvalue 資料存儲服務。

項目目前隸屬于 CNCF 基金會,被 AWS、Google、Microsoft、Alibaba 等大型網際網路公司廣泛使用。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

最初,在 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 次的寫入。

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

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

二、架構及内部機制解析

總體架構

etcd 是一個分布式的、可靠的 key-value 存儲系統,它用于存儲分布式系統中的關鍵資料,這個定義非常重要。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

一個 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 被送出的所有資料,進而保證整個叢集狀态向前推進的過程中保持資料的一緻。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

etcd 内部的機制比較複雜,但 etcd 給客戶提供的接口是簡單直接的。如上圖所示,我們可以通過 etcd 提供的用戶端去通路叢集的資料,也可以直接通過 http 的方式(類似 curl 指令)直接通路 etcd。在 etcd 内部,其資料表示也是比較簡單的,我們可以直接把 etcd 的資料存儲了解為一個有序的 map,它存儲着 key-value 資料。同時 etcd 為了友善用戶端去訂閱資料的變更,也支援了一個 watch 機制,通過 watch 實時地拿到 etcd 中資料的增量更新,進而實作與 etcd 中的資料同步等業務邏輯。

API 介紹

接下來我們看一下 etcd 提供的接口,這裡将 etcd 的接口分為了 5 組:

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結
  • 第一組是 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 被修改了多少次。

這裡可以用圖的方式給大家展示一下:

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

在同一個 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 也支援指定一個版本号來查詢曆史資料。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

因為 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 的資料變化即可。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

在 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 中可以提供多個操作,如下圖所示:

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

If 裡面寫了兩個條件,當 Value(key1) 大于 "bar" 并且 Version(key1) 的版本等于 2 的時候,執行 Then 裡面指定的操作:修改 Key2 的資料為 valueX,同時删除 Key3 的資料。如果不滿足條件,則執行另外一個操作:Key2 修改為 valueY。

在 etcd 内部會保證整個事務操作的原子性。也就是說 If 操作所有的比較條件,其看到的視圖一定是一緻的。同時它能夠確定多個操作的原子性不會出現 Then 中的操作僅執行了一半的情況。

通過 etcd 提供的事務操作,我們可以在多個競争中去保證資料讀寫的一緻性,比如說前面已經提到過的 Kubernetes 項目,它正是利用了 etcd 的事務機制,來實作多個 KubernetesAPI server 對同樣一個資料修改的一緻性。

lease 的概念及用法

lease 是分布式系統中一個常見的概念,用于代表一個分布式租約。典型情況下,在分布式系統中需要去檢測一個節點是否存活的時,就需要租約機制。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

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

如果希望這個租約永不過期,需要周期性的調用 KeepAlive 方法重新整理租約。比如說需要檢測分布式系統中一個程序是否存活,可以在程序中去建立一個租約,并在該程序中周期性的調用 KeepAlive 的方法。如果一切正常,該節點的租約會一緻保持,如果這個程序挂掉了,最終這個租約就會自動過期。

在 etcd 中,允許将多個 key 關聯在同一個 lease 之上,這個設計是非常巧妙的,可以大幅減少 lease 對象重新整理帶來的開銷。試想一下,如果有大量的 key 都需要支援類似的租約機制,每一個 key 都需要獨立的去重新整理租約,這會給  etcd 帶來非常大的壓力。通過多個 key 綁定在同一個 lease 的模式,我們可以将逾時間相似的 key 聚合在一起,進而大幅減小租約重新整理的開銷,在不失靈活性同時能夠大幅提高 etcd 支援的使用規模。

三、典型的使用場景介紹

中繼資料存儲

Kubernetes 将自身所用的狀态存儲在 etcd 中,其狀态資料的高可用交給 etcd 來解決,Kubernetes 系統自身不需要再應對複雜的分布式系統狀态處理,自身的系統架構得到了大幅的簡化。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

Server Discovery (Naming Service)

第二個場景是 Service Discovery,也叫做名字服務。在分布式系統中,通常會出現的一個模式就是需要多個後端(可能是成百上千個程序)來提供一組對等的服務,比如說檢索服務、推薦服務。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

對于這樣一種後端服務,通常情況下為了簡化後端服務的運維成本(節點故障時随時被替換),後端的這一程序會被類似 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 節點内會包含控制邏輯,多個節點之間的狀态同步是非常複雜的,這裡最典型的做法就是通過選主的方式,選出其中一個節點作為主節點來提供服務,另一個節點處于等待狀态。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

通過 etcd 提供的機制可以很容易的實作分布式程序的選主功能,比如可以通過對同一個 key 的事務寫來實作搶主的邏輯。一般而言,被選主的 Leader 會将自己的 IP 注冊到 etcd 中,使得 Slave 節點能夠及時擷取到目前的 Leader 位址,進而使得系統按照之前單個 Master 節點的方式繼續工作。當 Leader 節點發生異常之後,通過 etcd 能夠選取出一個新的節點成為主節點,并且注冊新的 IP 之後,Slave 又能夠拉取新的主節點的 IP,繼續恢複服務。

Distributed Coordination 分布式系統并發控制

在分布式系統中,當我們去執行一些任務,比如說去更新 OS、或者說更新 OS 上的軟體的時候、又或者去執行一些計算任務的時候,出于對後端服務的瓶頸或者是業務穩定性的考慮,通常情況下需要控制任務的并發度。如果該任務缺少一個調和的 Master 節點,可以通過 etcd 來完成這樣的分布式系統工作。

從零開始入門 K8s | 手把手帶你了解 etcd一、etcd 項目的發展曆程二、架構及内部機制解析三、典型的使用場景介紹本文總結

在這個模式中通過 etcd 去實作一個分布式的信号量,并且可以利用 etcd leases 機制來實作自動地剔除掉故障節點。在程序執行過程中,如果程序的運作周期比較長,我們可以将程序運作過程中的一些狀态資料存儲到 etcd,進而使得當程序故障之後且需要恢複到其他地方時,能夠從 etcd 中去恢複一些執行狀态,而不需要重新去完成整個的計算邏輯,以此來加速整個任務的執行效率。

本文總結

本文分享的主要内容就到此為止了,這裡為大家簡單總結一下:

  • 第一部分,為大家介紹了 etcd 項目是如何誕生的,以及在 etcd 發展過程中經曆的幾個重要時刻;
  • 第二部分,為大家介紹了 etcd 的架構以及其内部的基本操作接口,在了解 etcd 是如何實作高可用的基礎之上,展示了 etcd 資料的一些基本操作以及其内部的實作原理;
  • 第三部分,介紹了三種典型的 etcd 使用場景,以及在對應的場景下,分布式系統的設計思路。
阿裡巴巴雲原生 微信公衆号(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公衆号。”

繼續閱讀