1. 程式人生 > 實用技巧 >01 . etcd簡介原理,應用場景及部署,簡單使用

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還沒有圖形化工具

本文部分摘自

https://segmentfault.com/a/1190000023047434