大家好,我是
三十一
[0],本次分享分布式神器
etcd
[1]的 5 種部署方式和 10 種使用姿勢,閱讀全文預計花費 15 分鐘。
在學習 etcd 之前,我們先來聊一聊
etcd 名字的由來
[2]。etcd 中的 etc 取自 unix 系統的
/etc
目錄,再加上一個
d
代表
distributed system
就組成了 etcd。在 unix 系統中 /etc 目錄用于存儲系統的配置資料,單從名字看 etcd 可用于存儲分布式系統的配置資料,有時候也把 etcd 簡單了解為分布式 /etc 配置目錄。
etcd 簡介
etcd 是一個可靠的分布式 key-value 存儲系統,主要用于
配置共享
和
服務注冊和發現
,具有以下特性:
- 簡單:基于 gRPC 定義了清晰、面向使用者的 API。
- 安全:支援可選的用戶端 TLS 證書自動認證特性。
- 快速:支援每秒 10000 次的寫入。
- 可靠:基于 Raft 算法協定保證一緻性。
etcd 使用 Go 語言開發,底層基于 Raft 共識算法管理高可用的複制日志。目前已經被許多公司用于關鍵生産項目,比如:Kubernetes、locksmith、vulcand、Doorman 等。
當然,也有其他元件可以提供配置共享和服務注冊和發現的功能,比如最為廣泛和大家最為熟知的 Zookeeper,也被很多 Java 系的知名開源項目認可和使用,比如:Hadoop、HBase、Kafka 等。
但 etcd 是唯一一個可以媲美甚至超越 Zookeeper 的元件。
相較之下,Zookeeper 有如下缺點[3]:
- 複雜:Zookeeper 基于 ZAB 協定,屬于類 Paxos 協定,而 Paxos 算法素以複雜難懂聞名;Zookeeper 的使用也比較複雜,需要安裝用戶端,而官方目前隻提供了 Java 和 C 兩種語言接口。
- 發展慢:由于基金會龐大的結構以及松散的管理,導緻項目發展緩慢。
而 etcd 作為後起之秀,其優點也很明顯:
- 簡單:使用 Go 語言編寫部署簡單;使用 gRPC 定義接口,支援跨語言、跨平台特性;使用了易于使用者了解的 Raft 算法保證一緻性,優于 Paxos 算法。
- 發展快:etcd 正處于高速疊代開發中。
- 性能優越:官方提供的基準測試資料中,etcd 叢集可以支援每秒 10000+ 次的寫入,性能優于 Zookeeper。
- 安全性:etcd 支援 TLS 通路,而 ZooKeeper 在權限控制方面做得略顯粗糙。
環境搭建
一、本地安裝包部署
「下載下傳」:下載下傳最新的安裝包(目前最新:v3.5.4),下載下傳位址:https://github.com/etcd-io/etcd/releases/
「安裝」:在解壓後的檔案目錄下
etcd
、
etcdctl
分别為安裝包和用戶端的編譯後的執行檔案,可使用三種方法進行運作配置。
- 方法一:解壓目錄下直接運作
- 方法二:把
、etcd
檔案複制到etcdctl
目錄下。GOBIN
- 方法三:在環境變量裡添加
、etcd
檔案所在的目錄。etcdctl
注:運作過程中,可能會涉及權限問題,授權即可。
「驗證」:
# 驗證 etcd 版本
$ etcd --version
etcd Version: 3.5.4
Git SHA: 08407ff76
Go Version: go1.16.15
Go OS/Arch: darwin/amd64
# 驗證 etcdctl 版本
$ etcdctl version
etcdctl version: 3.5.4
API version: 3.5
複制
二、本地編譯部署
「下載下傳」:使用以下指令克隆代碼
# 下載下傳最新版
$ git clone https://github.com/etcd-io/etcd.git
# 指定版本下載下傳
$ git clone -b v3.5.4 https://github.com/etcd-io/etcd.git
複制
「編譯安裝」:
# 編譯
$ cd etcd
$ make build
# 安裝
$ export PATH="$PATH:`pwd`/bin"
複制
「驗證」:
# 驗證 etcd 版本
$ etcd --version
# 驗證 etcdctl 版本
$ etcdctl version
# 驗證 etcdutl 版本
$ etcdutl version
複制
三、本地叢集部署
首先需要安裝
goreman
元件,它基于 Procfile 配置檔案管理 etcd 應用程序。
$ go install github.com/mattn/goreman@latest
複制
「啟動叢集」:源碼目錄下
Procfile
腳本已經建構好了本地示範叢集,直接運作啟動即可
$ goreman start
複制
「驗證」
$ etcdctl member list
8211f1d0f64f3269, started, infra1, http://127.0.0.1:12380, http://127.0.0.1:2379, false
91bc3c398fb3c146, started, infra2, http://127.0.0.1:22380, http://127.0.0.1:22379, false
fd422379fda50e48, started, infra3, http://127.0.0.1:32380, http://127.0.0.1:32379, false
複制
該腳本建立包含 3 個 etcd 成員節點的叢集,每個叢集成員都接收鍵值的讀取和寫入。也可以按照
Procfile.learner
腳本指導,學習叢集新增節點的操作。
四、Docker 單機部署
此處通過
docker-compose
進行實驗配置。
「鏡像拉取」
$ docker pull bitnami/etcd:3.5.2
複制
「編輯docker-compose.yml」
version: '3.5'
services:
etcd:
container_name: builder-etcd
image: bitnami/etcd:3.5.2
ports:
- 2379:2379
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1002
- ETCD_QUOTA_BACKEND_BYTES=4294967296
volumes:
- ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data:/bitnami/etcd
networks:
default:
name: builder_dev
複制
「啟動服務」
$ docker-compose -f docker-compose.yml up
複制
「驗證」:驗證叢集節點的版本
$ docker exec -it builder-etcd /bin/bash -c "etcd --version"
etcd Version: 3.5.2
Git SHA: 99018a77b
Go Version: go1.16.3
Go OS/Arch: linux/amd64
複制
五、Docker 叢集部署
「編輯docker-compose.yml」
version: '3.5'
services:
etcd1:
container_name: builder-etcd1
image: bitnami/etcd:3.5.2
ports:
- 12379:2379
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1002
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_NAME=etcd1
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd1:2380
- ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd1:2379
- ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
volumes:
- ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data1:/bitnami/etcd
etcd2:
container_name: builder-etcd2
image: bitnami/etcd:3.5.2
ports:
- 22379:2379
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1002
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_NAME=etcd2
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd2:2380
- ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd2:2379
- ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
volumes:
- ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data2:/bitnami/etcd
etcd3:
container_name: builder-etcd3
image: bitnami/etcd:3.5.2
ports:
- 32379:2379
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1002
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_NAME=etcd3
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd3:2380
- ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd3:2379
- ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
volumes:
- ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data3:/bitnami/etcd
networks:
default:
name: builder_dev
複制
部署配置檔案 docker-compose.yml 細節,詳見
etcd docker-compose yml
[4]
「啟動服務」
$ docker-compose -f docker-compose.yml up
複制
「驗證」:驗證叢集節點的版本
$ docker exec -it builder-etcd1 /bin/bash -c "etcd --version"
$ docker exec -it builder-etcd2 /bin/bash -c "etcd --version"
$ docker exec -it builder-etcd3 /bin/bash -c "etcd --version"
# 輸出
etcd Version: 3.5.2
Git SHA: 99018a77b
Go Version: go1.16.3
Go OS/Arch: linux/amd64
複制
API 學習
此處,通過使用
etcdctl
[5]進行 API 學習驗證,etcdctl 是一個用于與 etcd 伺服器互動的指令行工具。
「1.檢視版本」
$ etcdctl version
etcdctl version: 3.6.0-alpha.0
API version: 3.6
複制
「2.寫入 key」
$ etcdctl put foo bar
OK
複制
「3.讀取 key」
$ etcdctl get foo
foo
bar
# 隻是擷取值
$ etcdctl get foo --print-value-only
bar
複制
「4.批量取值」
$ etcdctl put foo1 bar1
$ etcdctl put foo3 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
複制
「5.删除 key」
# 删除 foo 的值
$ etcdctl del foo
1
# 删除 foo 到 foo2 且不包括 foo2 的值
$ etcdctl del foo foo2
1
# 删除字首為 foo 的所有值
$ etcdctl del --prefix foo
2
複制
「6.監聽」
# 監聽 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
複制
「7.設定租約」當一個 key 被綁定到一個租約上時,它的生命周期與租約的生命周期即綁定。
# 設定10秒後過期時間
$ etcdctl lease grant 10
lease 32698142c52a170a granted with TTL(10s)
# 把 foo 和租約綁定,設定成 10 秒後過期
$ etcdctl put --lease=32698142c52a170a foo bar
OK
$ etcdctl get foo
foo
bar
# 10 秒後,擷取不到 foo
$ etcdctl get foo
# 傳回空
複制
「8.撤銷租約」通過租約 ID 撤銷租約,撤銷租約将删除其所有綁定的 key。
$ etcdctl lease grant 10
lease 32698142c52a170c granted with TTL(10s)
$ etcdctl put --lease=32698142c52a170c foo bar
OK
# 撤銷租約
$ etcdctl lease revoke 32698142c52a170c
lease 32698142c52a170c revoked
$ etcdctl get foo
# 傳回空
複制
「9.續約」通過重新整理 TTL 值來保持租約的有效,使其不會過期。
# 設定 10 秒後過期租約
$ etcdctl lease grant 10
lease 32698142c52a170f granted with TTL(10s)
# 把 foo 和租約綁定,設定成 10 秒後過期
$ etcdctl put foo bar --lease=32698142c52a170f
# 自動定時執行續約,續約成功後每次租約為 10 秒
$ etcdctl lease keep-alive 32698142c52a170f
lease 32695410dcc0ca06 keepalived with TTL(10)
lease 32695410dcc0ca06 keepalived with TTL(10)
lease 32695410dcc0ca06 keepalived with TTL(10)
...
複制
「10.檢視租約」檢視租約資訊,以便續租或檢視租約是否仍然存在或已過期
# 設定 50 秒 TTL
$ etcdctl lease grant 50
lease 32698142c52a1711 granted with TTL(50s)
# zoo1 綁定 32698142c52a1711 租約
$ etcdctl put --lease=32698142c52a1711 zoo1 val1
OK
# 檢視租約,remaining(32s) 剩餘有效時間32秒;--keys 擷取租約綁定的 key
$ etcdctl lease timetolive --keys 32698142c52a1711
lease 32698142c52a1711 granted with TTL(50s), remaining(32s), attached keys([zoo1])
複制
注:一個租約支援綁定多個 key
$ etcdctl lease grant 50
lease 32698142c52a1713 granted with TTL(50s)
$ etcdctl put --lease=32698142c52a1713 zoo1 val1
OK
$ etcdctl put --lease=32698142c52a1713 zoo2 val2
OK
$ etcdctl put --lease=32698142c52a1713 zoo3 val3
OK
複制
租約過期後,所有 key 值都會被删除,是以:
- 當租約隻綁定了一個 key 時,想删除這個 key,最好的辦法是撤銷它的租約,而不是直接删除這個 key。
- 當租約沒有綁定key時,應主動把它撤銷掉,單純删除 key 後,續約操作持續進行,會造成記憶體洩露。
# 方法一:直接删除`key`
# 設定租約并綁定 zoo1
$ etcdctl lease grant 50
lease 32698142c52a1715 granted with TTL(50s)
$ etcdctl --lease=32698142c52a1715 put zoo1 val1
OK
# 續約
$ etcdctl lease keep-alive 32698142c52a1715
lease 32698142c52a1715 keepalived with TTL(50)
# 另一個控制台執行:etcdctl del zoo1
# 單純删除 key 後,續約操作持續進行,會造成記憶體洩露
lease 32698142c52a1715 keepalived with TTL(50)
lease 32698142c52a1715 keepalived with TTL(50)
lease 32698142c52a1715 keepalived with TTL(50)
...
# 方法二:撤銷`key`的租約
# 設定租約并綁定 zoo1
$ etcdctl lease grant 50
lease 32698142c52a1717 granted with TTL(50s)
$ etcdctl --lease=32698142c52a1717 put zoo1 val1
OK
# 續約
$ etcdctl lease keep-alive 32698142c52a1717
lease 32698142c52a1717 keepalived with TTL(50)
lease 32698142c52a1717 keepalived with TTL(50)
# 另一個控制台執行:etcdctl lease revoke 32698142c52a1717
# 續約撤銷并退出
lease 32698142c52a1717 expired or revoked.
$ etcdctl get zoo1
# 傳回空
複制
以上,就是今天的全部内容,幾乎包含了 etcd 安裝和使用的各種姿勢,歡迎各位号友敬請嘗試。
References
- [0] 三十一: http://www.lee31.cn/assets/image/ThirtyOneLee.jpeg
- [1] etcd: https://github.com/etcd-io/etcd
- [2] etcd 名字的由來: https://etcd.io/docs/v3.5/faq/
- [3] https://www.infoq.cn/article/etcd-interpretation-application-scenario-implement-principle/
- [4] etcd docker-compose yml: https://github.com/liyaodev/docker-compose
- [5] etcdctl: https://etcd.io/docs/v3.5/dev-guide/interacting_v3/