Etcd按照官方介紹:node
Etcd is a distributed, consistent key-value store for shared configuration and service discoverylinux
是一個分佈式的,一致的鍵值對存儲,主要用於共享配置和服務發現。
Etcd是CoreOS團隊於2013年6月發起的開源項目,它的目標是構建一個高可用的分佈式鍵值對(key-value)數據庫,基於GO語言實現。
在分佈式系統中,最基本最重要的問題就是各類信息的一致性,包括對服務的配置信息的管理、服務的發現、更新、同步等等。
而要解決這些問題,每每須要基於一套能保證一致性的分佈式數據庫系統,好比經典的Apache ZooKeeper項目,經過維護文件目錄信息來實現數據的一致性。
Etcd就是專門爲集羣環境設計,能夠很好的實現數據的一致性,提供集羣節點管理和服務自動發現等。
受到Apache ZooKeeper項目和doozer項目的啓發,Etcd在進行設計的時候重點考慮下面四個要素:
簡單:支持REST風格的HTTP+JSON API;
安全:支持HTTPS方式的訪問;
快速:支持併發每秒一千次的寫操做;
可靠:支持分佈式結構,基於Raft算法實現一致性。
一般狀況下,使用Etcd能夠在多個節點上啓動多個實例,並將他們添加爲一個集羣。
同一個集羣中的Etcd實例將會自動保持彼此信息的一致性,這意味着分佈在各個節點上的應用也將獲取到一致的信息。git
Etcd基於Go語言實現,能夠直接從項目主頁:https://github.com/coreos/etcd下載源碼自行編譯,
也能夠下載編譯好的二進制文件,甚至直接使用製做好的Docker鏡像文件來使用,下面使用二進制文件的方式進行安裝。
github
下載和解壓:
wget https://github.com/etcd-io/etcd/releases/download/v3.3.10/etcd-v3.3.10-linux-arm64.tar.gz
tar -vxf etcd-v3.3.10-linux-arm64.tar.gz
解壓後,能夠看到的文件包括:算法
其中etcd是服務主文件,etcdctl是提供給用戶的的客戶端命名,其它都是文檔文件。
將etcd和etcdctl放到系統的可執行目錄/usr/bin/或者/usr/local/bin/下面就可使用了數據庫
cp etcd* /usr/local/bin/
直接執行Etcd命令,將啓動一個實例監聽本地的2379和4001端口。
此時客戶端能夠經過本地的2379和4001端口訪問Etcd,其餘Etcd本地實例能夠經過2380和7001端口鏈接到新啓動實例。json
(1)啓動centos
(2)查看版本安全
(3)查看集羣健康狀態bash
經過REST API直接查看集羣健康狀態:
經過ectdctl查看:
Etcd服務啓動的時候支持一些參數,用戶能夠經過這些參數來調整服務和集羣的行爲。
參數能夠經過環境變量形式傳入,命名所有爲大寫,而且加ETCD_前綴,例如ETCD_NAME='etcd-cluster'。
主要參數包括:通用參數、集羣參數、安全相關參數、代理參數。
(1)通用參數
(2)集羣參數
(3)安全相關參數
(4)代理參數
這些參數主要是當Etcd服務自身僅做爲代理模式時候使用,即轉發來自客戶端的請求到指定的Etcd集羣。
此時Etcd服務本省並不參與集羣中去,不保存數據和參加選舉。
(5)日誌參數
(6)其它
etcdctl是Etcd官方提供的命令行客戶端,它支持一些基於HTTP API封裝好的命令,
供用戶直接跟Etcd服務打交道,而無需基於HTTP API的方式。
固然這些命令跟HTTP API其實是對應的,最終效果上並沒有不一樣之處。
Etcd項目二進制文件包中已經包含了etcdctl工具,也能夠專門下載一個。
etcdctl的命令格式:
etcdctl [global options] command [command options] [arguments...]
etcdctl [全局選項] 命令 [命令選項] [命令參數]
etcdctl命令的全局選項參數:
支持的命令大致上分爲數據類操做和非數據類操做兩類。
etcdctl數據類操做命令:
etcdctl非數據類操做命令:
數據類操做圍繞對鍵值和目錄CRUD(符合REST風格的一套操做:Create)完整生命週期的管理。
CRUD即Create、Read、Update、Delete,是符合REST風格的一套API操做規範。
Etcd在鍵的組織上採用了層次化的空間結構(相似於文件系統中目錄的概念),用戶指定的鍵能夠爲單獨的名字。
例如,testkey放在根目錄/下面,也能夠爲指定目錄結構,如clusterl/node2/testkey,則將建立相應的目錄結構。
爲某個鍵設置定值
參數說明:
--ttl value 鍵值的超時時間(單位爲秒),默認爲0則爲永不超時。
--swap-with-value value 若該鍵如今的值是value,則進行設置操做
--swap-with-index value 若該鍵如今的索引值是指定索引,則進行設置操做。
獲取指定鍵的值:
當鍵不存在時報錯:
參數說明:
--sort 對返回結果進行排序
當鍵存在時,更新內容:
當不存在時,報錯:
參數說明:
--ttl value 鍵值的超時時間(單位爲秒),默認爲0則爲永不超時。
若是給定的鍵不存在,則建立一個新的鍵值:
若是鍵存在會直接報錯:
參數說明:
--in-order 在目錄<key>下建立按順序鍵
--ttl value 鍵值的超時時間(單位爲秒),默認爲0則爲永不超時。
刪除某個鍵:
當鍵不存在的時候,報錯:
參數說明:
--dir 若是鍵是個空目錄或者是鍵值對則刪除
--recursive, -r 刪除目錄和全部子鍵
--with-value value 檢查現有的值是否匹配
--with-index value 檢查現有的index是否匹配
監測一個鍵值的變化,一旦鍵值發生更新,就會輸出最新的值並退出。
參數說明:
--forever, -f 一直監測,直到用戶按「ctrl+c」退出;
--after-index value 在指定index以前一直監測;
--recursive, -r 返回全部的鍵值和子鍵值;
檢測一個鍵值的變化,一旦鍵值發生更新,就執行給定命令。
不少時候用戶實時根據鍵值更新本地服務的配置信息,並從新加載服務。
能夠實現分佈式應用配置的自動分發。
參數說明:
--after-index value 在指定index以前一直監測;
--recursive, -r 返回全部的鍵值和子鍵值;
列出目錄(默認爲根目錄)下的鍵或者子目錄,默認不顯示子目錄中的內容。
參數說明:
--sort 將輸出結果排序
--recursive 若是目錄下有子目錄,則遞歸輸出其中的內容
-p 若是輸出爲目錄,在最後添加‘/’進行分區
若是給定的目錄不存在,則建立一個新的鍵目錄:
若是鍵目錄存在,報錯:
參數說明:
--ttl value 鍵值的超時時間(單位爲秒),默認爲0則爲永不超時。
刪除一個空目錄或者鍵值對,如目錄不爲空則報錯:
建立一個鍵目錄,不管存在與否:
更新一個已經存在的目錄屬性。
備份Etcd的配置狀態數據目錄
選項:
--data-dir 要進行備份的Etcd的數據存放目錄
--backup-dir 備份數據到指定路徑
其中,snap爲快照目錄,保存節點狀態文件,wal保存了數據庫預寫日誌信息。
預寫日誌要求數據庫在發生實際提交以前必須先將操做寫入日誌,能夠保障系統在崩潰後更具日誌回覆狀態。
查看Etcd集羣的健康狀態:
參數說明:
--forever, -f 每10秒檢查一次,直到手動終止
經過list、add、remove等子命令列出、添加、刪除Etcd實例到Etcd集羣中。
etcdctl member command [command options] [arguments...]
例如本地啓動一個Etcd服務實例後,能夠用以下命令查看默認的成員實例:
導入舊版本的快照文件到系統
對用戶進行管理,包括一系列子命令
add 添加一個用戶
get 查詢用戶細節
list 列出全部用戶
remove 刪除用戶
grant 添加用戶到角色
revoke 刪除用戶角色
passwd 修改用戶密碼
默認狀況下,須要先建立(啓用)root用戶做爲etcd集羣的最高權限管理員。
建立一個testuser用戶:
分配某些已有角色給用戶:
對用戶角色進行管理
add 添加一個角色
get 查詢角色信息
list 列出全部用戶角色
remove 刪除用戶角色
grant 添加路徑到角色控制
revoke 刪除某路徑的角色用戶信息
默認帶有root、guest兩種角色,前者爲全局最高權限。
是否啓用訪問驗證,enable爲啓用,disable爲禁用。
Etcd的集羣也採用了典型的「主從」模型,經過Raft協議來保證在一段時間內有一個節點爲主節點,其它節點爲從節點。
一旦主節點發生故障,其它節點能夠自動再從新選舉出新的節點。
和其它分佈式系統相似,急羣衆節點個數推薦爲基數個,最少爲3個,此時(quorum爲2),
越多節點天然能提供更多的冗餘性,但同時會帶來寫數據能力的降低。
構建集羣無非是讓節點知道本身加入哪一個集羣,其它對等節點的訪問信息是什麼。
Etcd支持兩種模式來構建集羣:靜態配置和動態探測。
靜態配置就是提取寫好集羣中的有關信息。
三個節點的ip信息分別爲:
172.16.16.15 172.16.0.4 172.16.0.15
經過以下命令來分別啓動各個節點上的etcd服務。
節點1: etcd --name infra0 --initial-advertise-peer-urls http://172.16.16.15:2380 --listen-peer-urls http://172.16.16.15:2380 --listen-client-urls http://172.16.16.15:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.16.15:2379 --initial-cluster-token etcd-cluster-1 --initial-cluster infra0=http://172.16.16.15:2380,infra1=http://172.16.0.4:2380,infra2=http://172.16.0.15:2380 --initial-cluster-state new 節點2: etcd --name infra1 --initial-advertise-peer-urls http://172.16.0.4:2380 --listen-peer-urls http://172.16.0.4:2380 --listen-client-urls http://172.16.0.4:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.0.4:2379 --initial-cluster-token etcd-cluster-1 --initial-cluster infra0=http://172.16.16.15:2380,infra1=http://172.16.0.4:2380,infra2=http://172.16.0.15:2380 --initial-cluster-state new 節點3: etcd --name infra2 --initial-advertise-peer-urls http://172.16.0.15:2380 --listen-peer-urls http://172.16.0.15:2380 --listen-client-urls http://172.16.0.15:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.0.15:2379 --initial-cluster-token etcd-cluster-1 --initial-cluster infra0=http://172.16.16.15:2380,infra1=http://172.16.0.4:2380,infra2=http://172.16.0.15:2380 --initial-cluster-state new
啓動成功以後,能夠在任意一個節點查看當前集羣中的成員信息:
[root@centos002 ~]# etcdctl member list 1f146a283033baa3: name=infra2 peerURLs=http://172.16.0.15:2380 clientURLs=http://172.16.0.15:2379 isLeader=false 6de1f3013b32aaf9: name=infra1 peerURLs=http://172.16.0.4:2380 clientURLs=http://172.16.0.4:2379 isLeader=false d89aedc239d376e5: name=infra0 peerURLs=http://172.16.16.15:2380 clientURLs=http://172.16.16.15:2379 isLeader=true
還能夠查看各個節點的健康狀態:
[root@centos002 ~]# etcdctl cluster-health member 1f146a283033baa3 is healthy: got healthy result from http://172.16.0.15:2379 member 6de1f3013b32aaf9 is healthy: got healthy result from http://172.16.0.4:2379 member d89aedc239d376e5 is healthy: got healthy result from http://172.16.16.15:2379 cluster is healthy
靜態配置的方法雖然簡單,可是若是節點的信息須要變更的時候,就須要手動修改。
能夠經過動態發現的方法,讓集羣自動更新節點信息。
要實現動態發現,首先須要一套支持動態發現的服務。
CoreOS提供了一個公開的Etcd發現服務,地址是:https://discovery.etcd.io。
這個網址會爲建立的集羣提供一個獨一無二的uuid,須要提供的惟一參數是節點的個數
[root@centos001 ~]# curl https://discovery.etcd.io/new?size=3 https://discovery.etcd.io/37435a480ba59aa81a682fee7f415856
分別在各個節點上指定服務發現地址信息,替換掉原先動態指定的節點列表。
節點1: etcd --name infra0 --initial-advertise-peer-urls http://172.16.16.15:2380 --listen-peer-urls http://172.16.16.15:2380 --listen-client-urls http://172.16.16.15:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.16.15:2379 --initial-cluster-token etcd-cluster-1 --discovery https://discovery.etcd.io/37435a480ba59aa81a682fee7f415856 --initial-cluster-state new 節點2: etcd --name infra1 --initial-advertise-peer-urls http://172.16.0.4:2380 --listen-peer-urls http://172.16.0.4:2380 --listen-client-urls http://172.16.0.4:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.0.4:2379 --initial-cluster-token etcd-cluster-1 --discovery https://discovery.etcd.io/37435a480ba59aa81a682fee7f4158560 --initial-cluster-state new 節點3: etcd --name infra2 --initial-advertise-peer-urls http://172.16.0.15:2380 --listen-peer-urls http://172.16.0.15:2380 --listen-client-urls http://172.16.0.15:2379,http://127.0.0.1:2379 --advertise-client-urls http://172.16.0.15:2379 --initial-cluster-token etcd-cluster-1 --discovery https://discovery.etcd.io/37435a480ba59aa81a682fee7f415856 --initial-cluster-state new
dns發現主要經過dns服務來記錄集羣中各節點的域名信息,各節點到dns服務中獲取相互的地址信息,從而創建集羣。
即爲每一個節點指定同一個子域的域名,而後經過域名發現來自動註冊。例如:
infra0.example.com infra1.example.com infra2.example.com
則啓動參數中的集羣節點列表信息能夠替換爲--discovery-srv example.com
影響集羣性能的因素可能有不少,包括時間同步、網絡抖動、存儲壓力、讀寫壓力等。
須要經過優化配置儘可能減小這些因素的影響。
對於分佈式集羣來講,各個節點上的同步時鐘十分重要,
Etcd集羣須要各個節點時鐘差別不超過1s。不然可能會致使Raft協議的異常。
能夠修改/etc/ntp.conf這個配置文件,來指定ntp服務器的地址。
對於Etcd集羣來講,有兩個因素十分重要:心跳消息時間間隔和選舉時間間隔。
前者意味着主節點每隔多久來經過心跳消息通知從節點自身的存活狀態;
後者意味着從節點多久沒收到心跳通知後能夠嘗試發起選舉自身爲主節點。
顯而後者要比前者大,通常建議設爲前者的5倍以上。
時間越短,發生故障後回覆的越快,但心跳信息佔用的計算和網絡資源也就越多。
默認狀況下,心跳消息間隔爲100ms。選舉時間間隔爲1s。
能夠在啓動服務的時候經過--heartbeat-interval和--election-timeout來指定。
固然也能夠經過環境變量來指定。ETCD_HEARTBEAT_INTERVAL=100 ETCD_ELECTION_TIMEOUT=1000。
Etcd會按期將數據的修改存儲爲snapshop,默認狀況下每10000次修改纔會存一個snapshot。
在存儲的時候會有大量數據進行寫入,影響Etcd的性能。
啓動時經過--snapshot-count '100000'指定,也可使用環境變量ETCD_SNAPSHOP_COUNT=2000 etcd來指定。
不管是添加、刪除仍是遷移節點,都要一個一個的進行,而且確保先修改配置信息
(包括節點廣播的監聽地址、集羣中節點列表),而後再進行操做。
例如要刪多個節點,當有主節點被刪除時,須要先刪掉一個,等集羣中狀態穩定後(新的節點從新生成),再刪除另外節點。
要遷移或者替換節點的時候,先將節點從集羣中刪除掉,等集羣狀態從新穩定後,再添加上新的節點。
固然,使用舊節點的數據目錄會加快新節點的同步過程,但要保證這些數據是完整的,且是比較新的。
Etcd集羣中的節點會經過數據目錄來存放修改信息和集羣配置。
通常來講,當某個節點出現故障的時候,本地數據已通過期甚至格式破壞。
若是隻是簡單的重啓進程,容易形成數據的不一致。
這個時候保險的作法是先經過命令來刪除該節點,而後清空數據目錄,再從新做爲空節點加入。
Etcd提供了-strict-reconfig-check選項,確保當集羣狀態不穩定的時候拒絕對配置狀態的修改。
極端狀況下,集羣中大部分節點都出現問題,須要重啓整個集羣。
這個時候,最保險的作法就是找到一個數據記錄完整且比較新的節點,
先以它爲惟一節點建立新的集羣,而後將其餘節點一個一個的添加進來。
全部的分佈式系統,都面臨的一個問題是多個節點之間的數據共享,這和團隊協做的道理是同樣的,成員能夠分頭幹活,
但老是須要共享一些必要的信息,好比誰是leader,都有那些成員,依賴任務之間的順序協調等。
因此分佈式系統要麼本身實現一個可靠的共享存儲來同步信息(好比Elasticsearch),要麼依賴一個可靠的共享存儲,而Etcd就是這樣一個服務。
(1)提供存儲以及獲取數據的接口,它經過協議保證Etcd集羣中的多個節點數據的強一致性,用於存儲元信息和共享配置。
(2)提供監聽機制,客戶端能夠監聽某個key或者某些key的變動(v2和v3的機制不一樣),用於監聽和推送變動。
(3)提供key的過時以及續約機制,客戶端經過定時刷新來實現續約(v2和v3實現的機制也不同),用於集羣監控以及服務註冊發現。
(4)提供原子的CAS(Compare-and-swap)和CAD(Compare-and-Delete)支持(v2經過接口參數實現,v3經過批量事物實現),用於分佈式鎖以及leader選舉。
(1)raft算法經過對不一樣的場景(選主,日誌複製)設計不一樣的機制,雖然下降 了通用性(相對paxos),但同時也下降了複雜度,便於理解和實現。
(2)raft內置的選主協議是給本身用的,用於選出主節點,理解raft的選主機制的關鍵在於理解raft的時鐘週期以及超時機制。
(3)理解Etcd的數據同步的關鍵在於理解raft的日誌同步機制。
Etcd實現raft的時候,充分利用go語言CSP併發模型和chan的魔法,想更進一步瞭解的話能夠去閱讀源碼,下面簡單分析一下它的wal日誌。
wal日誌是二進制的,解析出來後是以上數據結構LogEntry。
其中第一個字段type,只有兩種,一種是0表示Normal,1表示ConfChange(ConfChange表示Etcd自己的配置變動同步,好比有新的節點加入等)。
第二個字段是term,每一個term表明一個主節點的任期,每次主節點變動term就會變化。
第三個字段是index,這個序號是嚴格有序遞增的,表明變動序號。
第四個字段是二進制的data,將raft request對象的pb結構整個保存下。
Etcd源碼下有個tools/etcd-dump-logs,能夠將wal日誌dump成文本查看,能夠協助分析raft協議。
raft協議自己不關心應用數據,也就是data中的部分,一致性都經過同步wal日誌來實現,
每一個節點將從主節點收到的data apply到本地的存儲,raft只關心日誌的同步狀態,
若是本地存儲實現的有bug,好比沒有正確的將data apply到本地,可能會致使數據不一致。
Etcd v2與v3本質上是共享同一套raft協議代碼的兩個獨立應用,接口不同,存儲不同,數據相互隔離。
也就是說若是從Etcd v2升級到Etcd v3,原來v2的數據仍是隻能用v3的接口訪問,v3的接口建立的數據只能訪問經過v3的接口訪問。
Etcd v2存儲,Watch以及過時機制
Etcd v2是個純內存的實現,並未實時將數據寫入到磁盤,持久化機制很簡單,就是將store整合序列成json寫入文件。
數據在內存中是一個簡單的樹結構,好比如下結構數據存儲到Etcd中的結構如圖所示:
store中有一個全局currentindex,每次變動,index會加1,而後每一個event都會關聯到currentindex。
當客戶端調用watch接口(參數中怎加wait參數)時,若是請求參數中有waitindex,
而且waitindex小於currentindex,則從EventHistory表中查詢index小於等於waitindex,而且和watch key匹配的event。
若是有數據,則直接返回。若是歷史表中沒有或者請求沒有帶waitindex,則放入WatchHub中,每一個key會關聯以和watcher列表。
當有變動操做時,變動生成的event會放入EventHistory表中,同時通知和該key相關的watcher。
這裏面有幾個影響使用的細節問題:
(1)EventHistory是有長度限制的,最長1000,也就是說,若是你的客戶端停了許久,
而後從新watch的時候,可能和該waitindex相關的event已經被淘汰了,這種狀況下會丟失變動。
(2)若是通知watch的時候,出現了阻塞(每一個watch的channel有100個緩衝空間),
Etcd會直接把watch刪除,也就是會致使wait請求的鏈接中斷,客戶端須要從新鏈接。
(3)Etcd store的每一個node中保存了過時時間,經過定時機制進行清理。
Etcd v2的一些限制:
(1)過時時間只能設置到每一個key上,若是多個key要保證生命週期一致則比較困難。
(2)watch只能watch某一個key以及其子節點(經過參數 recursive),不能進行多個watch。
(3)很難經過watch機制來實現完整的數據同步(有丟失變動的風險),因此當前的大多數使用方式是經過watch得知變動,
而後經過get從新獲取數據,並不徹底依賴於watch的變動event。