您好,登錄后才能下訂單哦!
前言
隨著kubernetes項目的日益火熱,該項目中用到的etcd組件作為一個高可用強一致性的服務發現存儲倉庫,漸漸的被開發人員所關注。
在云計算時代,如何讓服務快速、透明的接入到計算集群中,如何讓共享配置信息快速被集群中的所有節點發現,如何構建一套高可用、安全、易于部署以及快速響應的服務集群成為了需要解決的問題。
Etcd為解決這類問題帶來便捷。
官方地址: https://coreos.com/etcd/
項目地址: https://github.com/coreos/etcd
Etcd是什么
Etcd是一個高可用的鍵值存儲系統,主要用于共享配置和服務發現,它通過Raft一致性算法處理日志復制以保證強一致性,我們可以理解它為一個高可用強一致性的服務發現存儲倉庫。
在kubernetes集群中,etcd主要用于配置共享和服務發現
Etcd主要解決的是分布式系統中數據一致性的問題,而分布式系統中的數據分為控制數據和應用數據,etcd處理的數據類型為控制數據,對于很少量的應用數據也可以進行處理。
Etcd和Zookeeper的比較
Zookeeper有如下缺點
1.復雜。ZooKeeper的部署維護復雜,管理員需要掌握一系列的知識和技能;而Paxos強一致性算法也是素來以復雜難懂而聞名于世(ETCD使用[Raft]協議, ZK使用ZAB,類PAXOS協議);另外,ZooKeeper的使用也比較復雜,需要安裝客戶端,官方只提供了Java和C兩種語言的接口。
2.Java編寫。這里不是對Java有偏見,而是Java本身就偏向于重型應用,它會引入大量的依賴。而運維人員則普遍希望保持強一致、高可用的機器集群盡可能簡單,維護起來也不易出錯。
3.發展緩慢。Apache基金會項目特有的“Apache Way”在開源界飽受爭議,其中一大原因就是由于基金會龐大的結構以及松散的管理導致項目發展緩慢。
相較之下,Etcd
1.簡單。使用Go語言編寫部署簡單;使用HTTP作為接口使用簡單;使用Raft算法保證強一致性讓用戶易于理解。
2.數據持久化。etcd默認數據一更新就進行持久化。
3.安全。etcd支持SSL客戶端安全認證。
Etcd的架構與術語
流程分析
通常一個用戶的請求發送過來,會經過HTTP Server轉發給Store進行具體的事務處理,如果涉及到節點的修改,則需要交給Raft模塊進行狀態的變更,日志的記錄。
然后再同步給別的etcd節點確認數據提交,最后進行數據提交,再次同步。
工作原理
Etcd使用Raft協議來維護集群內各個節點狀態的一致性。簡單說,ETCD集群是一個分布式系統,由多個節點相互通信構成整體對外服務,每個節點都存儲了完整的數據,并且通過Raft協議保證每個節點維護的數據是一致的。
Etcd主要分為四個部分
HTTP Server: 用于處理用戶發送的API請求以及其他etcd節點的同步與心跳信息請求
Store: 用于處理 etcd 支持的各類功能的事務,包括數據索引、節點狀態變更、監控與反饋、事件處理與執行等等,是 etcd 對用戶提供的大多數 API 功能的具體實現。
Raft: Raft 強一致性算法的具體實現,是 etcd 的核心。
WAL:Write Ahead Log(預寫式日志/日志先行),是 etcd 的數據存儲方式,也是一種實現事務日志的標準方法。etcd通過 WAL 進行持久化存儲,所有的數據提交前都會事先記錄日志。Snapshot 是為了防止數據過多而進行的狀態快照;Entry 表示存儲的具體日志內容。
服務發現
服務發現要解決的也是分布式系統中最常見的問題之一,即在同一個分布式集群中的進程或服務,要如何才能找到對方并建立連接。本質上來說,服務發現就是想要了解集群中是否有進程在監聽 udp 或 tcp 端口,并且通過名字就可以查找和連接。要解決服務發現的問題,需要具備以下三點:
1.一個強一致性、高可用的服務存儲目錄。基于 Raft 算法的 etcd 天生就是這樣一個強一致性高可用的服務存儲目錄。
2.一種注冊服務和監控服務健康狀態的機制。用戶可以在 etcd 中注冊服務,并且對注冊的服務設置key TTL,定時保持服務的心跳以達到監控健康狀態的效果。
3.一種查找和連接服務的機制。通過在 etcd 指定的主題下注冊的服務也能在對應的主題下查找到。為了確保連接,我們可以在每個服務機器上都部署一個 Proxy 模式的 etcd,這樣就可以確保能訪問 etcd 集群的服務都能互相連接。
例如隨著 Docker 容器的流行,多種微服務共同協作,構成一個相對功能強大的架構的案例越來越多。透明化的動態添加這些服務的需求也日益強烈。通過服務發現機制,在 etcd 中注冊某個服務名字的目錄,在該目錄下存儲可用的服務節點的 IP。在使用服務的過程中,只要從服務目錄下查找可用的服務節點去使用即可。
Etcd集群中的術語
Raft: etcd所采用的保證分布式系統強一致的算法
Node: 一個Raft狀態機實例
Member: 一個etcd實例,管理一個Node,可以為客戶端請求提供服務
Cluster: 多個Member構成的可以協同工作的etcd集群
Peer: 同一個集群中,其他Member的稱呼
Client: 向etcd集群發送HTTP請求的客戶端
WAL: 預寫日志,是etcd用于持久化存儲的日志格式
Snapshot: etcd防止WAL文件過多而設置的快照,存儲etcd數據狀態
Proxy: etcd的一種模式,可以為etcd提供反向代理服務
Leader: Raft算法中通過競選而產生的處理所有數據提交的節點
Follower: Raft算法中競選失敗的節點,作為從屬節點,為算法提供強一致性保證
Candidate: Follower超過一定時間接收不到Leader節點的心跳的時候,會轉變為Candidate(候選者)開始Leader競選
Term: 某個節點稱為Leader到下一次競選開始的時間周期,稱為Term(任界,任期)
Index: 數據項編號, Raft中通過Term和Index來定位數據
Raft算法
Raft 是一種為了管理復制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法結構和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易構建實際的系統。一致性算法允許一組機器像一個整體一樣工作,即使其中一些機器出現故障也能夠繼續工作下去。正因為如此,一致性算法在構建可信賴的大規模軟件系統中扮演著重要的角色。
Raft算法分為三部分
Leader選舉、日志復制和安全性
Raft算法特性:
1.強領導者: 和其他一致性算法相比,Raft 使用一種更強的領導能力形式。比如,日志條目只從領導者發送給其他的服務器。這種方式簡化了對復制日志的管理并且使得 Raft 算法更加易于理解。
2.領導選舉: Raft 算法使用一個隨機計時器來選舉領導者。這種方式只是在任何一致性算法都必須實現的心跳機制上增加了一點機制。在解決沖突的時候會更加簡單快捷。
3.成員關系調整: Raft 使用一種共同一致的方法來處理集群成員變換的問題,在這種方法下,處于調整過程中的兩種不同的配置集群中大多數機器會有重疊,這就使得集群在成員變換的時候依然可以繼續工作。
Leader選舉
Raft 狀態機
Raft集群中的每個節點都處于一種基于角色的狀態機中。具體來說,Raft定義了節點的三種角色: Follower、Candidate和Leader。
1.Leader(領導者): Leader節點在集群中有且僅能有一個,它負責向所有的Follower節點同步日志數據
2.Follower(跟隨者): Follower節點從Leader節點獲取日志,提供數據查詢功能,并將所有修改請求轉發給Leader節點
3.Candidate(候選者): 當集群中的Leader節點不存在或者失聯之后,其他Follower節點轉換為Candidate,然后開始新的Leader節點選舉
這三種角色狀態之間的轉換,如下圖:
一個 Raft 集群包含若干個服務器節點;通常是 5 個,這允許整個系統容忍 2 個節點的失效。在任何時刻,每一個服務器節點都處于這三個狀態之一:領導人、跟隨者或者候選人。在通常情況下,系統中只有一個領導人并且其他的節點全部都是跟隨者。跟隨者都是被動的:他們不會發送任何請求,只是簡單的響應來自領導者或者候選人的請求。領導人處理所有的客戶端請求(如果一個客戶端和跟隨者聯系,那么跟隨者會把請求重定向給領導人)
在節點初始啟動的時候,所有節點的Raft狀態機都會處于Follower狀態。當Follower在一定的時間周期內沒有收到來自Leader節點的心跳數據包的時候,節點會將自己的狀態切換為Candidate,并向集群中其他Follower節點發送投票請求,Follower都會將自己的票投給收到的第一個投票請求節點。當Candidate收到來自集群中超過半數節點的投票后,會成為新的Leader節點。
Leader節點將接受并保存用戶發送的數據,并向其他的Follower節點同步日志。
Follower只響應來自其他服務器的請求。如果Follower接收不到消息,那么他就會變成候選人并發起一次選舉。獲得集群中大多數選票的候選人將成為Leader。在一個任期(Term)內,領導人一直都會是領導人直到自己宕機了。
Leader節點依靠定時向所有Follower發送心跳數據來保持地位。當急群眾的Leader節點出現故障的時候,Follower會重新選舉新的節點,保證整個集群正常運行。
每次成功的選舉,新的Leader的Term(任期)值都會比之前的Leader增加1。當集群中由于網絡或者其他原因出現分裂后又重新合并的時候,集群中可能會出現多于一個的Leader節點,此時,Term值更高的節點才會成為真正的Leader。
Raft算法中的Term(任期)
關于Term,如下圖:
Raft會把時間分割成任意長度的任期。并且任期用連續的整數來標記。每一段任期都是從一次選舉開始,一個或者多個候選人嘗試成為領導者。如果一個候選人贏得選舉,然后他就會在接下來的任期中充當Leader的職責。在某些情況下,一次選舉會造成選票瓜分,這樣,這一個任期將沒有Leader。如果沒有Leader,那么新的一輪選舉就馬上開始,也就是新的任期就會開始。Raft保證了在一個Term任期內,有且只有一個Leader。
日志復制
所謂日志復制,是指主節點將每次操作形成日志條目,并持久化到本地磁盤,然后通過網絡IO發送給其他節點。
一旦一個領導人被選舉出來,他就開始為客戶端提供服務。客戶端的每一個請求都包含一條被復制狀態機執行的指令。領導人把這條指令作為一條新的日志條目附加到日志中去,然后并行的發起附加條目 RPCs 給其他的服務器,讓他們復制這條日志條目。
Raft 算法保證所有已提交的日志條目都是持久化的并且最終會被所有可用的狀態機執行。當主節點收到包括自己在內超過半數節點成功返回,那么認為該日志是可提交的(committed),并將日志輸入到狀態機,將結果返回給客戶端。
在正常的操作中,領導人和跟隨者的日志保持一致性,所以附加日志 RPC 的一致性檢查從來不會失敗。然而,領導人崩潰的情況會使得日志處于不一致的狀態(老的領導人可能還沒有完全復制所有的日志條目)。這種不一致問題會在一系列的領導人和跟隨者崩潰的情況下加劇。跟隨者的日志可能和新的領導人不同的方式。跟隨者可能會丟失一些在新的領導人中有的日志條目,他也可能擁有一些領導人沒有的日志條目,或者兩者都發生。丟失或者多出日志條目可能會持續多個任期。這就引出了另一個部分,就是安全性
安全性
截止此刻,選主以及日志復制并不能保證節點間數據一致。試想,當一個某個節點掛掉了,一段時間后再次重啟,并當選為主節點。而在其掛掉這段時間內,集群若有超過半數節點存活,集群會正常工作,那么會有日志提交。這些提交的日志無法傳遞給掛掉的節點。當掛掉的節點再次當選主節點,它將缺失部分已提交的日志。在這樣場景下,按Raft協議,它將自己日志復制給其他節點,會將集群已經提交的日志給覆蓋掉。這顯然是錯誤的
其他協議解決這個問題的辦法是,新當選的主節點會詢問其他節點,和自己數據對比,確定出集群已提交數據,然后將缺失的數據同步過來。這個方案有明顯缺陷,增加了集群恢復服務的時間(集群在選舉階段不可服務),并且增加了協議的復雜度。Raft解決的辦法是,在選主邏輯中,對能夠成為主的節點加以限制,確保選出的節點已定包含了集群已經提交的所有日志。如果新選出的主節點已經包含了集群所有提交的日志,那就不需要從和其他節點比對數據了。簡化了流程,縮短了集群恢復服務的時間。
這里存在一個問題,加以這樣限制之后,還能否選出主呢?答案是:只要仍然有超過半數節點存活,這樣的主一定能夠選出。因為已經提交的日志必然被集群中超過半數節點持久化,顯然前一個主節點提交的最后一條日志也被集群中大部分節點持久化。當主節點掛掉后,集群中仍有大部分節點存活,那這存活的節點中一定存在一個節點包含了已經提交的日志了。
Etcd的代理節點(proxy)
Etcd針對Raft的角色模型進行了擴展,增加了Proxy角色。proxy模式的本職就是啟一個HTTP代理服務器,把客戶發到這個服務器的請求轉發給別的 etcd 節點。
作為Proxy角色的節點不會參與Leader的選舉,只是將所有接收到的用戶查詢和修改請求轉發到任意一個Follower或者Leader節點上。
Proxy節點可以在啟動Etcd的時候通過"--proxy on"參數指定。在使用了"節點自發現"服務的集群中,可以設置一個固定的"參選節點數目",超過這個數目的成員自動轉換為Proxy節點。
一旦節點成為Proxy之后,便不再參與所有Leader選舉和Raft狀態變化。除非將這個節點重啟并指定為成員的Follower節點
etcd 作為一個反向代理把客戶的請求轉發給可用的 etcd 集群。這樣,你就可以在每一臺機器都部署一個 Proxy 模式的 etcd 作為本地服務,如果這些 etcd Proxy 都能正常運行,那么你的服務發現必然是穩定可靠的。
完整的Etcd角色狀態轉換過程如下圖:
kubernetes項目中,Etcd用來做什么,為什么選擇它
etcd在kubernetes集群是用來存放數據并通知變動的。
Kubernetes中沒有用到數據庫,它把關鍵數據都存放在etcd中,這使kubernetes的整體結構變得非常簡單。
在kubernetes中,數據是隨時發生變化的,比如說用戶提交了新任務、增加了新的Node、Node宕機了、容器死掉了等等,都會觸發狀態數據的變更。狀態數據變更之后呢,Master上的kube-scheduler和kube-controller-manager,就會重新安排工作,它們的工作安排結果也是數據。這些變化,都需要及時地通知給每一個組件。etcd有一個特別好用的特性,可以調用它的api監聽其中的數據,一旦數據發生變化了,就會收到通知。有了這個特性之后,kubernetes中的每個組件只需要監聽etcd中數據,就可以知道自己應該做什么。kube-scheduler和kube-controller-manager呢,也只需要把最新的工作安排寫入到etcd中就可以了,不用自己費心去逐個通知了
試想一下,如果沒有etcd,那么要怎樣做?這里的本質是:數據的傳遞有兩種方式,一種是消息的方式,比如說NodeA有了新的任務,Master直接給NodeA發一個消息,中間不經過任何人;一種是輪詢的方式,大家都把數據寫到同一個地方,每個人自覺地盯著看,及時發現變化。前者演化出rabbitmq這樣的消息隊列系統,后者演化出一些有訂閱功能的分布式系統。
第一種方式的問題是,所有要通信的組件之間都要建立長連接,并且要處理各種異常情況,比例如連接斷開、數據發送失敗等。不過有了消息隊列(message queue)這樣的中間件之后,問題就簡單多了,組件都和mq建立連接即可,將各種異常情況都在mq中處理。
那么為什么kubernetes沒有選用mq而是選用etcd呢?mq和etcd是本質上完全不同的系統,mq的作用消息傳遞,不儲存數據(消息積壓不算儲存,因為沒有查詢的功能),etcd是個分布式存儲(它的設計目標是分布式鎖,順帶有了存儲功能),是一個帶有訂閱功能的key-value存儲。如果使用mq,那么還需要引入數據庫,在數據庫中存放狀態數據。
選擇etcd還有一個好處,etcd使用raft協議實現一致性,它是一個分布式鎖,可以用來做選舉。如果在kubernetes中部署了多個kube-schdeuler,那么同一時刻只能有一個kube-scheduler在工作,否則各自安排各自的工作,就亂套了。怎樣保證只有一個kube-schduler在工作呢?那就是前文說到的通過etcd選舉出一個leader。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。