etcd環境安裝與使用

etcd簡介

etcd是開源的、高可用的分佈式key-value存儲系統,可用於配置共享和服務的註冊和發現,它專註於:

  • 簡單:定義清晰、面向用戶的API(gRPC)

  • 安全:可選的客戶端TLS證書自動認證

  • 快速:支持每秒10,000次寫入

  • 可靠:基於Raft算法確保強一致性

etcd與redis差異

etcd和redis都支持鍵值存儲,也支持分佈式特性,redis支持的數據格式更加豐富,但是他們兩個定位和應用場景不一樣,關鍵差異如下:

  • redis在分佈式環境下不是強一致性的,可能會丟失數據,或者讀取不到最新數據

  • redis的數據變化監聽機制沒有etcd完善

  • etcd強一致性保證數據可靠性,導致性能上要低於redis

  • etcd和ZooKeeper是定位類似的項目,跟redis定位不一樣

為什麼用 etcd 而不用ZooKeeper?

相較之下,ZooKeeper有如下缺點:

  • 複雜:ZooKeeper的部署維護複雜,管理員需要掌握一系列的知識和技能;而 Paxos 強一致性算法也是素來以複雜難懂而聞名於世;另外,ZooKeeper的使用也比較複雜,需要安裝客戶端,官方只提供了 Java 和 C 兩種語言的接口。

  • 難以維護:Java 編寫。這裡不是對 Java 有偏見,而是 Java 本身就偏向於重型應用,它會引入大量的依賴。而運維人員則普遍希望保持強一致、高可用的機器集群儘可能簡單,維護起來也不易出錯。

  • 發展緩慢:Apache 基金會項目特有的「Apache Way」在開源界飽受爭議,其中一大原因就是由於基金會龐大的結構以及鬆散的管理導致項目發展緩慢。

而 etcd 作為一個後起之秀,其優點也很明顯。

  • 簡單:使用 Go 語言編寫部署簡單;使用 HTTP 作為接口使用簡單;使用 Raft 算法保證強一致性讓用戶易於理解。

  • 數據持久化:tcd 默認數據一更新就進行持久化。

  • 安全:etcd 支持 SSL 客戶端安全認證。

單機部署

(1)到etcd的github地址,下載最新的安裝包(目前最新版本:v3.4.7)

下載地址://github.com/etcd-io/etcd/releases/

(2)解壓,把etcdetcdctl文件複製到已經配置了環境變量的目錄中

  • 方法一:把etcdetcdctl文件複製到GOBIN目錄下。

  • 方法二:在環境變量里添加etcdetcdctl文件所在的目錄。

(3)驗證是否安裝成功

$ etcd --version
etcd Version: 3.4.7
Git SHA: e694b7bb0
Go Version: go1.12.17
Go OS/Arch: linux/amd64

正常顯示etcd版本信息,則證明安裝成功。

API學習

etcdctl用於與etcd交互的控制台程序。API版本可以通過ETCDCTL_API環境變量設置為2或3版本。默認情況下,v3.4以上的etcdctl使用v3 APIv3.3及更早的版本默認使用v2 API

注意:用v2 API創建的任何key將不能通過v3 API查詢。同樣,用v3 API創建的任何key將不能通過v2 API查詢。

運行etcd,在終端輸入:etcd

在另一個終端運行ctcdctl測試。

#查看默認API版本
$ etcdctl version  
etcdctl version: 3.4.7
API version: 3.4  #v3 API

#寫入key:/test/foo value:hello etcd (雙引號可去掉)
$ etcdctl put /test/foo "hello etcd" 
OK
$ etcdctl get /test/foo
/test/foo
hello etcd

#手動切換到v2 API
$ export ETCDCTL_API=2  
$ etcdctl --version
etcdctl version: 3.4.7
API version: 2

$ etcdctl get /test/foo
Error:  client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint      #查詢不到/test/foo的值

寫入key

$ etcdctl put foo bar
OK

讀取key值

$ etcdctl get foo
foo
bar

#只是獲取值
$ etcdctl get foo --print-value-only
bar
$ etcdctl put foo1 bar1
$ etcdctl put foo2 bar2
$ etcdctl put foo3 bar3

#獲取從foo到foo3的值,不包括foo3
$ etcdctl get foo foo3 --print-value-only 
bar
bar1
bar2

# 獲取前綴為foo的值
$ etcdctl get --prefix foo --print-value-only
bar
bar1
bar2
bar3

#獲取符合前綴的前兩個值
$ etcdctl get --prefix --limit=2 foo --print-value-only
bar
bar1

刪除key

#刪除foo
$ etcdctl del foo
1

#刪除foo到foo2,不包括foo2
$ etcdctl del foo foo2
1
#刪除key前綴為foo的
$ etcdctl del --prefix foo
2

監視值變化

#監視foo單個key
$ etcdctl watch foo
#另一個控制台執行: etcdctl put foo bar
PUT
foo
bar

#同時監視多個值
$ etcdctl watch -i
$ watch foo
$ watch zoo
# 另一個控制台執行: etcdctl put foo bar
PUT
foo
bar
# 另一個控制台執行: etcdctl put zoo val
PUT
zoo
val

#監視foo前綴的key
$ etcdctl watch --prefix foo
#另一個控制台執行: etcdctl put foo1 bar1
PUT
foo1
bar1
#另一個控制台執行: etcdctl put fooz1 barz1
PUT
fooz1
barz1

設置租約(Grant leases)

當一個key被綁定到一個租約上時,它的生命周期與租約的生命周期綁定。

#設置60秒後過期時間
$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)

#把foo和租約綁定,設置成60秒後過期
$ etcdctl put --lease=32695410dcc0ca06 foo bar
OK
$ etcdctl get foo
foo
bar

#60秒後,獲取不到foo
$ etcdctl get foo
#返回空

主動撤銷租約(Revoke leases)

通過租賃ID(此處指:32695410dcc0ca06)撤銷租約。撤銷租約將刪除其所有綁定的key

$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)
$ etcdctl put foo bar --lease=32695410dcc0ca06
OK

#主動撤銷租約
$ etcdctl lease revoke 32695410dcc0ca06
lease 32695410dcc0ca06 revoked

$ etcdctl get foo
#返回空

續租約(Keep leases alive)

通過刷新其TTL來保持租約的有效,使其不會過期。

#設置60秒後過期租約
$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)

#把foo和租約綁定,設置成60秒後過期
$ etcdctl put foo bar --lease=32695410dcc0ca06

#續租約,自動定時執行續租約,續約成功後每次租約為60秒
$ etcdctl lease keep-alive 32695410dcc0ca06
lease 32695410dcc0ca06 keepalived with TTL(60)
lease 32695410dcc0ca06 keepalived with TTL(60)
lease 32695410dcc0ca06 keepalived with TTL(60)
...

獲取租約信息(Get lease information)

獲取租約信息,以便續租或查看租約是否仍然存在或已過期

#設置500秒TTL
$ etcdctl lease grant 500
lease 694d5765fc71500b granted with TTL(500s)

#keyzoo1綁定694d5765fc71500b租約
$ etcdctl put zoo1 val1 --lease=694d5765fc71500b
OK

#查看租約信息,remaining(132s)剩餘有效時間132秒;--keys獲取租約綁定的key
$ etcdctl lease timetolive --keys 694d5765fc71500b
lease 694d5765fc71500b granted with TTL(500s), remaining(132s), attached keys([zoo1])

值得注意的地方,一個租約可以綁定多個key

$ etcdctl lease grant 500
lease 694d5765fc71500b granted with TTL(500s)

$ etcdctl put zoo1 val1 --lease=694d5765fc71500b
OK

$ etcdctl put zoo2 val2 --lease=694d5765fc71500b
OK

當租約過期後,所有key值會被刪除。

當一個租約只綁定了一個key時,想刪除這個key,最好的辦法是撤銷它的租約,而不是直接刪除這個key

看下面這個例子:

#方法一:直接刪除`key`
#設置租約並綁定zoo1
$ etcdctl lease grant 60
lease 694d71f80ed8bf1e granted with TTL(60s)
$ etcdctl put zoo1 val1 --lease=694d71f80ed8bf1e
OK

#續租約
$ etcdctl lease keep-alive 694d71f80ed8bf1e
lease 694d71f80ed8bf1e keepalived with TTL(60)

#另一個控制台執行:etcdctl del zoo1

#單純刪除key後,續約操作還會一直進行,造成內存泄露
lease 694d71f80ed8bf1e keepalived with TTL(60)
lease 694d71f80ed8bf1e keepalived with TTL(60)
lease 694d71f80ed8bf1e keepalived with TTL(60)
...
方法二:撤銷`key`的租約
#設置租約並綁定zoo1
$ etcdctl lease grant 60
lease 694d71f80ed8bf1e granted with TTL(60s)
$ etcdctl put zoo1 val1 --lease=694d71f80ed8bf1e
OK

#續租約
$ etcdctl lease keep-alive 694d71f80ed8bf1e
lease 694d71f80ed8bf1e keepalived with TTL(60)
lease 694d71f80ed8bf1e keepalived with TTL(60)

#另一個控制台執行:etcdctl lease revoke 694d71f80ed8bf1e

#續約操作並退出
lease 694d71f80ed8bf1e expired or revoked.

當租約沒有綁定key時,應主動把它撤銷掉。

應用場景

根據以上特性和API,etcd有應用場景以下應用場景:

場景一:服務發現

服務發現要解決的也是分佈式系統中最常見的問題之一,即在同一個分佈式集群中的進程或服務,要如何才能找到對方並建立連接。本質上來說,服務發現就是想要了解集群中是否有進程在監聽 udp 或 tcp 端口,並且通過名字就可以查找和連接。

場景二:配置中心

etcd的應用場景優化都是圍繞存儲的東西是「配置」 來設定的。

  • 配置的數據量通常都不大,所以默認etcd的存儲上限是1GB
  • 配置通常對歷史版本信息是比較關心的,所以etcd會保存 版本(revision) 信息
  • 配置變更是比較常見的,並且業務程序會需要實時知道,所以etcd提供了watch機制,基本就是實時通知配置變化
  • 配置的準確性一致性極其重要,所以etcd採用raft算法,保證系統的CP
  • 同一份配置通常會被大量客戶端同時訪問,針對這個做了grpc proxy對同一個key的watcher做了優化
  • 配置會被不同的業務部門使用,提供了權限控制和namespace機制

場景三:負載均衡

此處指的負載均衡均為軟負載均衡,分佈式系統中,為了保證服務的高可用以及數據的一致性,通常都會把數據和服務部署多份,以此達到對等服務,即使其中的某一個服務失效了,也不影響使用。由此帶來的壞處是數據寫入性能下降,而好處則是數據訪問時的負載均衡。因為每個對等服務節點上都存有完整的數據,所以用戶的訪問流量就可以分流到不同的機器上。

場景四:分佈式鎖

因為 etcd 使用 Raft 算法保持了數據的強一致性,某次操作存儲到集群中的值必然是全局一致的,所以很容易實現分佈式鎖。

場景五:集群監控與 Leader 競選

通過 etcd 來進行監控實現起來非常簡單並且實時性強。

  • 前面幾個場景已經提到 Watcher 機制,當某個節點消失或有變動時,Watcher 會第一時間發現並告知用戶。
  • 節點可以設置TTL key,比如每隔 30s 發送一次心跳使代表該機器存活的節點繼續存在,否則節點消失。

這樣就可以第一時間檢測到各節點的健康狀態,以完成集群的監控要求。

另外,使用分佈式鎖,可以完成 Leader 競選。這種場景通常是一些長時間 CPU 計算或者使用 IO 操作的機器,只需要競選出的 Leader 計算或處理一次,就可以把結果複製給其他的 Follower。從而避免重複勞動,節省計算資源。

參考:

Tags: