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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM