01 . etcd簡介原理,應用場景及部署,簡單使用
etcd簡介
Etcd是CoreOS團隊於2013年6月發起的開源專案,他的目標是構建一個高可用的分散式鍵值(key-value)資料庫,etcd內部採用raft協議作為一致性演算法,etcd基於Go語言實現.
特點
// 簡單: 安裝配置簡單,而且提供了HTTP API進行互動,使用也很簡單.
// 安全: 支援SSL證書驗證
// 快速: 根據官方提供的benchmark資料,單例項支援每秒2k+讀操作
// 可靠: 採用raft演算法,實現分散式系統資料的可用性和一致性.
Etcd vs zk
本文選取ZK作為典型代表與ETCD進行比較,而不考慮[Consul]專案作為比較物件,原因為Consul的可靠性和穩定性還需要時間來驗證(專案發起方自身服務並未使用Consul, 自己都不用...
# 一致性協議: ETCD使用[Raft]協議, ZK使用ZAB(類PAXOS協議),前者容易理解,方便工程實現;
# 運維方面:ETCD方便運維,ZK難以運維;
# 專案活躍度:ETCD社群與開發活躍,ZK已經快死了;
# API:ETCD提供HTTP+JSON, gRPC介面,跨平臺跨語言,ZK需要使用其客戶端;
# 訪問安全方面:ETCD支援HTTPS訪問,ZK在這方面缺失;
讀寫效能
按照官網給出的[Benchmark], 在2CPU,1.8G記憶體,SSD磁碟這樣的配置下,單節點的寫效能可以達到16K QPS, 而先寫後讀也能達到12K QPS。這個效能還是相當可觀的。
概念術語
// Raft: etcd所採用的保證分散式系統強一致性的演算法 // Node: 一個Raft狀態機例項 // Member: 一個etcd例項,他管理著一個Node,並且可以為客戶端請求提供服務 // Cluster: 由多個Member構成可以協同工作的etcd叢集 // Peer: 對同一etcd叢集另一個Member的稱呼 // Client: 向etcd叢集傳送http請求的客戶端 // WAL: 預寫式日誌,etcd用於持久化儲存的日誌格式 // snapshot: etcd防止WAL檔案過多而設定的快照,儲存etcd資料狀態 // Proxy: etcd的一種模式,為etcd叢集提供反向代理服務. // Leader: Raft演算法中通過競選而產生的處理所有資料提交的節點. // Follower: 競選失敗的節點作為Raft中的從屬節點,為演算法提供強一致性保證. // Candidate: 當Follower超過一定時間接受不到Leader的心跳時轉變為Candidate開始競選. // Term: 某個節點成為Leader到下一次競選時間,稱為一個Term. // Index: 資料項編號,Raft中通過Term和Index來定位資料
資料讀寫順序
為了保證資料的強一致性,etcd叢集中所有資料流向都是同一個方向,從Leader (主節點) 流向Follower,也就是所有Follower的資料必須與Leader保證一致,如果不一致會被覆蓋.
使用者對於etcd叢集所有節點進行讀寫
// 讀取: 由於叢集所有節點資料是強一致性的,讀取可以從叢集中隨便哪個節點進行讀取資料.
// 寫入: etcd叢集有leader,如果寫入往leader寫入,可以直接寫入,然後Leader節點會把寫入分發給所有Follower,如果往Follower寫入,然後Leader節點會把寫入分發給所有Follower.
leader選舉
假設三個節點的叢集,三個節點上均執行Timer(每個Timer持續時間是隨機的),Raft演算法使用隨機Timer來初始化Leader選舉流程,第一個節點率先完成了Timer,隨後他就會向其他兩個節點發送成為Leader的請求,其他節點接收到後會以投票迴應然後第一個節點被選舉為Leader.
成為Leader後,該節點會以固定時間間隔向其他節點發送通知,確保自己仍然是Leader,有些情況下當Follower收不到Leader的通知後,比如Leader節點宕機或者失去了連線,其他節點會重複之前選舉過程選舉出新的Leader.
判斷資料是否寫入
etcd認為寫入請求被Leader節點處理並分給了多數節點後,就是一個成功的寫入,那麼多少節點如何判定尼,假設總節點數就是N,那麼多數節點Quorum=N/2+1, 關於如何確定etcd叢集應該有多少個的問題,用Instances減去Quorom就是叢集中容錯節點(允許出故障節點)的數量.
所以在叢集中推薦的最少節點數量是3個,因為1和2個節點的容錯點數都是0,一旦有一個節點宕掉,整個叢集就不能正常工作了.
etcd架構及解析
架構圖
架構解析
從etcd的架構圖中我們可以看到, etcd主要分為四個部分:
// HTTP Server: 用於處理使用者傳送的API請求以及其他的etcd節點的同步與心跳資訊請求
// Store: 用於處理etcd支援的各類功能的事務,包含資料索引,節點狀態變更,監控與反饋,事件處理與執行等等,是etcd對使用者提供的大多數API功能的具體實現
// Raft: Raft強一致性演算法的具體實現,是etcd的核心.
// WAL: Write Ahead Log(預寫式日誌),是etcd的資料儲存方式,除了在記憶體中所有資料的狀態以及節點的索引以外,etcd就通過WAL進行持久化儲存,WAL中,所有的資料提交前都會事先記錄日誌.
// Shapshot是為了防止資料過多而進行的狀態快照.
// Entry表示儲存的具體日誌內容
通常,一個使用者的請求傳送過來,會經由HTTP Server轉發給Store進行具體的事務處理,如果涉及到節點的修改,則交給Raft模組進行狀態的變更,日誌的記錄,然後再同步給別的etcd節點以確認資料提交,最後進行資料的提交,再次同步.
應用場景
服務發現/註冊
前後端業務註冊發現
中介軟體以及後端服務在etcd中註冊,前端和中介軟體可以很輕鬆的從etcd中發現相關伺服器然後伺服器之間根據呼叫關係相互繫結呼叫
多組後端伺服器註冊發現
後端多個無狀態相同副本app可以同時註冊到etcd中,前端可以通過haproxy從etcd中獲取到後端的ip和埠組,然後進行請求轉發,可以用來故障轉移遮蔽後端埠以及後端多組app例項.
訊息釋出和訂閱
etcd可以充當訊息中介軟體,生產者可以往etcd中註冊topic併發送訊息,消費者從etcd中訂閱topci,來獲取生產者傳送至etcd中的訊息.
負載均衡
後端多組相同的服務提供者可以經自己服務註冊到etcd中,etcd並且會與註冊的服務進行監控檢查,服務請求這首先從etcd中獲取到可用的服務提供者真正的ip:port, 然後對此多組服務傳送請求,etcd在其中充當了負載均衡的功能.
分散式通知與協調
// 當etcd watch服務發現丟失,會通知服務檢查
// 控制器向etcd傳送啟動服務,etcd通知服務進行相應操作
// 當服務完成work會將狀態更新至etcd, etcd對應會通知使用者
分散式鎖,分散式佇列
// 有多個node, etcd根據每個node來建立對應的node的佇列,根據不同的佇列可以在etcd中找到對應的competitor
叢集監控與Leader競選
etcd可以根據raft演算法在多個node節點來選舉出leader.
單機部署
yum -y install etcd
systemctl enable etcd
grep -Ev "^#|^$" /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379"
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"
叢集部署
最好部署奇數位,能達到更好的叢集容錯
cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.43.233 etcd1
192.168.43.130 etcd2
192.168.43.246 etcd3
安裝etcd
yum -y install etcd
mkdir -p /data/app
chown etcd:etcd /data/app -R
systemctl enable etcd
etcd配置
引數說明
# —data-dir 指定節點的資料儲存目錄,這些資料包括節點ID,叢集ID,叢集初始化配置,Snapshot檔案,若未指定—wal-dir,還會儲存WAL檔案;
# —wal-dir 指定節點的was檔案的儲存目錄,若指定了該引數,wal檔案會和其他資料檔案分開儲存。
# —name 節點名稱
# —initial-advertise-peer-urls 告知叢集其他節點url.
# — listen-peer-urls 監聽URL,用於與其他節點通訊
# — advertise-client-urls 告知客戶端url, 也就是服務的url
# — initial-cluster-token 叢集的ID
# — initial-cluster 叢集中所有節點
etcd1
[root@etcd1 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v
ETCD_DATA_DIR="/data/app/etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.43.233:2380"
ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.233:2379"
ETCD_NAME="etcd1"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.43.233:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.233:2379"
ETCD_INITIAL_CLUSTER="etcd1=http://192.168.43.233:2380,etcd2=http://192.168.43.130:2380,etcd3=http://192.168.43.246:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-token"
ETCD_INITIAL_CLUSTER_STATE="new"
etcd2
[root@etcd2 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v
ETCD_DATA_DIR="/data/app/etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.43.130:2380"
ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.130:2379"
ETCD_NAME="etcd2"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.43.130:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.130:2379"
ETCD_INITIAL_CLUSTER="etcd1=http://192.168.43.233:2380,etcd2=http://192.168.43.130:2380,etcd3=http://192.168.43.246:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-token"
ETCD_INITIAL_CLUSTER_STATE="new"
etcd3
[root@etcd-3 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v
ETCD_DATA_DIR="/data/app/etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.43.246:2380"
ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.246:2379"
ETCD_NAME="etcd3"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.43.246:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://192.168.43.246:2379"
ETCD_INITIAL_CLUSTER="etcd1=http://192.168.43.233:2380,etcd2=http://192.168.43.130:2380,etcd3=http://192.168.43.246:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-token"
ETCD_INITIAL_CLUSTER_STATE="new"
驗證叢集服務
[root@etcd-3 ~]# systemctl status etcd
● etcd.service - Etcd Server
Loaded: loaded (/usr/lib/systemd/system/etcd.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2020-08-27 00:45:52 CST; 2min 12s ago
Main PID: 1385 (etcd)
CGroup: /system.slice/etcd.service
└─1385 /usr/bin/etcd --name=etcd3 --data-dir=/data...
Aug 27 00:45:52 etcd-3 etcd[1385]: ready to serve client requ...
Aug 27 00:45:52 etcd-3 etcd[1385]: serving insecure client re...
Aug 27 00:45:52 etcd-3 etcd[1385]: ready to serve client requ...
Aug 27 00:45:52 etcd-3 etcd[1385]: serving insecure client re...
Aug 27 00:45:52 etcd-3 systemd[1]: Started Etcd Server.
Aug 27 00:45:52 etcd-3 etcd[1385]: 2407f8cc1c4a982 initialzed...
Aug 27 00:45:52 etcd-3 etcd[1385]: established a TCP streamin...
Aug 27 00:45:52 etcd-3 etcd[1385]: established a TCP streamin...
Aug 27 00:45:52 etcd-3 etcd[1385]: established a TCP streamin...
Aug 27 00:45:53 etcd-3 etcd[1385]: established a TCP streamin...
Hint: Some lines were ellipsized, use -l to show in full.
[root@etcd-3 ~]# ss -atnlp |grep etcd
LISTEN 0 128 192.168.43.246:2379 *:* users:(("etcd",pid=1385,fd=7))
LISTEN 0 128 127.0.0.1:2379 *:* users:(("etcd",pid=1385,fd=6))
LISTEN 0 128 192.168.43.246:2380 *:* users:(("etcd",pid=1385,fd=5))
[root@etcd-3 ~]# etcdctl cluster-health
member 2407f8cc1c4a982 is healthy: got healthy result from http://127.0.0.1:2379
member 7d37bdfae6fd099b is healthy: got healthy result from http://127.0.0.1:2379
member 94362b802cdd1767 is healthy: got healthy result from http://127.0.0.1:2379
cluster is healthy
[root@etcd-3 ~]# etcdctl member list
2407f8cc1c4a982: name=etcd3 peerURLs=http://192.168.43.246:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.246:2379 isLeader=false
7d37bdfae6fd099b: name=etcd1 peerURLs=http://192.168.43.233:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.233:2379 isLeader=true
94362b802cdd1767: name=etcd2 peerURLs=http://192.168.43.130:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.130:2379 isLeader=false
動態發現啟動etcd叢集
https://developer.aliyun.com/article/765312
簡單使用
增加
set
[root@etcd-3 ~]# etcdctl set /testdir/testkey "hello"
hello
# 支援的選項包括
--ttl '0' 該鍵值的超時時間(單位為妙), 不配置(預設為0)則永不超時.
--swap-with-value value 若該鍵的值為value,則進行設定操作
--swap-with-index '0' 若該鍵現在值為索引,則進行設定操作
mk
# 如果給定的鍵不存在,則建立一個新的鍵值,例如:
[root@etcd-3 ~]# etcdctl mk /testdir/testkey2 "hello"
hello
# 當鍵存在就會報錯
[root@etcd-3 ~]# etcdctl mk /testdir/testkey "hello"
Error: 105: Key already exists (/testdir/testkey) [10]
# 支援的選項為:
--ttl '0' # 超時時間(單位為秒),不配置(預設為0),則永不超時
mkdir
# 如果給定的鍵目錄不存在,則建立一個新的鍵目錄.
[root@etcd-3 ~]# etcdctl mkdir youmen
[root@etcd-3 ~]# etcdctl mkdir youmen
Error: 105: Key already exists (/youmen) [12]
# 支援的選項
--ttl '0' # 超時時間(單位為秒),不配置(預設為0)則永不超時
setdir
# 建立一個鍵目錄,如果目錄不存在就建立,如果目錄存在更新目錄TTL
[root@etcd-3 ~]# etcdctl setdir testdir3
[root@etcd-3 ~]# etcdctl setdir testdir3
Error: 102: Not a file (/testdir3) [13]
# 支援的選項
--ttl '0' # 超時時間(單位為秒),不配置(預設為0)則永不超時
刪除
rm
# 刪除某個鍵值
[root@etcd-3 ~]# etcdctl rm /testdir/testkey hello
PrevNode.Value: hello
# 支援的選項為:
# --dir 如果鍵是個空目錄或鍵值對則刪除
# --recursive 刪除目錄和所有子健
# --with-value 檢查現有的值是否匹配
# --with-index '0' 檢查現有的index是否匹配
rmdir
# 刪除一個空目錄,或者鍵值對
[root@etcd-3 ~]# etcdctl rmdir testdir3
[root@etcd-3 ~]# etcdctl rmdir testdir3
Error: 100: Key not found (/testdir3) [15]
更新
update
# 當鍵存在時,更新值內容
[root@etcd-3 ~]# etcdctl set /testdir/testkey "hello"
hello
[root@etcd-3 ~]# etcdctl update /testdir/testkey "hello2"
hello2
updatedir
# 更新一個已經存在的目錄
etcdctl updatedir testdir2
查詢
get
# 獲取指定鍵的值
[root@etcd-3 ~]# etcdctl get /testdir/testkey
hello2
# 當鍵不存在時,則會報錯,例如
[root@etcd-3 ~]# etcdctl get /testdir/testkeydsf
Error: 100: Key not found (/testdir/testkeydsf) [18]
# 支援選項
# --sort 對結果進行排序
# --consistent 將請求發給主節點,保證獲取內容的一致性
ls
# 列出目標(預設為根目錄)下的鍵或者子目錄,預設不顯示子目錄中內容:
[root@etcd-3 ~]# etcdctl mk /testdir2/testkey "hello"
hello
[root@etcd-3 ~]# etcdctl ls /testdir2/
/testdir2/testkey
# 支援選項
# --sort 對結果進行排序
# --recursive 如果目錄下有子目錄,則遞迴輸出其中的內容-p ,對於輸出為目錄,在最後新增/進行區分
watch
# 檢測一個鍵值的變化,一旦鍵值發生更新,就會輸出最新的值並退出.
# 例如使用者更新testkey鍵值為hello watch
[root@etcd2 ~]# etcdctl get /testdir2/testkey
hello
[root@etcd2 ~]# etcdctl set /testdir2/testkey "hello watch"
hello watch
[root@etcd-3 ~]# etcdctl watch testdir2/testkey
hello watch
# --forever 一直檢測到使用者按CTRL+C退出
# --after-index '0' 在指定index之前一直監測
# --recursive 返回所有的鍵值和子鍵值
exec-watch
# 檢測一個鍵值的變化,一旦鍵值發生更新,就執行指定命令
[root@etcd-3 ~]# etcdctl get /testdir2/testkey
hello watch
[root@etcd-3 ~]# etcdctl set /testdir2/testkey "hello exec-watch"
hello exec-watch
[root@etcd2 ~]# etcdctl exec-watch testdir2/testkey -- sh -c 'ls /'
bin data etc lib media opt root sbin sys usr
boot dev home lib64 mnt proc run srv tmp var
# --after-index '0' 在指定index之前一直監測
# --recursive 返回所有鍵值和子鍵值
備份
# 備份etcd的資料
[root@etcd-3 ~]# etcdctl backup --data-dir /data/app/etcd --backup-dir /home/etcd_backup_2020_08_27
2020-08-27 10:48:47.833840 I | ignoring EntryConfChange raft entry
2020-08-27 10:48:47.833904 I | ignoring EntryConfChange raft entry
2020-08-27 10:48:47.833913 I | ignoring EntryConfChange raft entry
2020-08-27 10:48:47.833959 I | ignoring member attribute update on /0/members/7d37bdfae6fd099b/attributes
2020-08-27 10:48:47.833976 I | ignoring member attribute update on /0/members/94362b802cdd1767/attributes
2020-08-27 10:48:47.833990 I | ignoring member attribute update on /0/members/2407f8cc1c4a982/attributes
2020-08-27 10:48:47.834002 I | ignoring member attribute update on /0/members/2407f8cc1c4a982/attributes
# 支援選項
# --data-dir etcd的資料目錄
# --backup-dir 備份到指定路徑
member
# 通過list,add,remove命令列出,新增,刪除etcd例項到etcd叢集中
檢視叢集中存在節點
[root@etcd-3 ~]# etcdctl member list
2407f8cc1c4a982: name=etcd3 peerURLs=http://192.168.43.246:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.246:2379 isLeader=true
7d37bdfae6fd099b: name=etcd1 peerURLs=http://192.168.43.233:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.233:2379 isLeader=false
94362b802cdd1767: name=etcd2 peerURLs=http://192.168.43.130:2380 clientURLs=http://127.0.0.1:2379,http://192.168.43.130:2379 isLeader=false
刪除叢集中存在的節點
[root@etcd-3 ~]# etcdctl member remove 94362b802cdd1767
向叢集中新加節點
[root@etcd-3 ~]# etcdctl add etcd4 http://192.168.43.254:2380
節點遷移
在生產環境中,不可避免遇到機器硬體故障。當遇到硬體故障發生的時候,我們需要快速恢復節點。ETCD叢集可以做到在不丟失資料的,並且不改變節點ID的情況下,遷移節點。
# 1)停止待遷移節點上的etc程序;
# 2)將資料目錄打包複製到新的節點;
# 3)更新該節點對應叢集中peer url,讓其指向新的節點;
# 4)使用相同的配置,在新的節點上啟動etcd程序;
小結
etcd預設只儲存1000個歷史事件,不適合大量更新的場景,這樣會導致資料丟失,典型應用場景是配置管理和服務發現,這些場景都是讀多寫少的
相比於zookeeper,etcd使用起來簡單很多,不過要實現真正的服務發現功能,etcd還需要和其他工具(比如registerator,confd等)一起實現服務的自動註冊和更新
目前etcd還沒有圖形化工具
本文部分摘自