從零開始搭建ETCD分散式一致性儲存系統
什麼是ETCD
Etcd是一個高可用的 Key/Value 儲存系統,用於服務發現的基礎註冊和通知,通過註冊和監聽,實現基礎的服務發現。
Etcd是coreos開發的分散式服務系統,內部採用raft協議作為一致性演算法
Etcd構建自身高可用叢集主要有三種形式:
- 靜態發現: 預先已知 Etcd 叢集中有哪些節點,在啟動時直接指定好Etcd的各個node節點地址
- Etcd動態發現: 通過已有的Etcd叢集作為資料互動點,然後在擴充套件新的叢集時實現通過已有叢集進行服務發現的機制
- DNS動態發現: 通過DNS查詢方式獲取其他節點地址資訊
下面會給大家介紹etcd的使用,主要包括下面幾步
- 安裝
- 單機執行
- 叢集搭建
- 監聽功能watch(服務發現原理)
- curl呼叫API
一.安裝
下載地址:https://github.com/coreos/etcd/releases/
mkdir -p /home/chenqionghe/test/etcd/ cd !$ # 下載 wget https://github.com/etcd-io/etcd/releases/download/v3.3.12/etcd-v3.3.12-linux-amd64.tar.gz # 解壓安裝 tar zxvf etcd-v3.3.12-linux-amd64.tar.gz cd etcd-v3.3.12-linux-amd64 ## 將啟動檔案和命令管理檔案拷貝到 PATH找到的路徑中 cp etcd /usr/local/bin cp etcdctl /usr/local/bin cp -r etcd-v3.2.5-linux-amd64 /usr/local/etcd#將軟體放置到常用目錄下
啟動引數解釋
--name etcd叢集中的節點名,這裡可以隨意,可區分且不重複就行 --listen-peer-urls 監聽的用於節點之間通訊的url,可監聽多個,叢集內部將通過這些url進行資料互動(如選舉,資料同步等) --initial-advertise-peer-urls 建議用於節點之間通訊的url,節點間將以該值進行通訊。 --listen-client-urls 監聽的用於客戶端通訊的url,同樣可以監聽多個。 --advertise-client-urls 建議使用的客戶端通訊url,該值用於etcd代理或etcd成員與etcd節點通訊。 --initial-cluster-token etcd-cluster-1 節點的token值,設定該值後集群將生成唯一id,併為每個節點也生成唯一id,當使用相同配置檔案再啟動一個叢集時,只要該token值不一樣,etcd叢集就不會相互影響。 --initial-cluster 也就是叢集中所有的initial-advertise-peer-urls 的合集 --initial-cluster-state new 新建叢集的標誌
二.單機執行
直接啟動
etcd
etcd預設監聽的是localhost的2379埠,既只監聽了lo裝置,這樣會導致啟動後集群中的其他機器無法訪問
因此我們可以在啟動的時候將預設的localhost改成0.0.0.0l,確保etcd監聽了所有網絡卡。
etcd -listen-client-urls="http://0.0.0.0:2379" --advertise-client-urls="http://0.0.0.0:2379"
*** 注意:etcd有要求,如果--listen-client-urls被設定了,那麼就必須同時設定--advertise-client-urls,所以即使設定和預設相同,也必須顯式設定***
我們來使用curl來測試一下,是否可以遠端訪問,這裡我的機器IP是10.211.55.25
➜~ curl -Lhttp://10.211.55.25:2379/version {"etcdserver":"3.3.12","etcdcluster":"3.3.0"}%
ok,我們可以看到etcd已經可以正常訪問了
三.叢集搭建
這裡我們採用Static方式,準備三臺機器,ip如下(都已經安裝etcd)
node1 10.211.55.2 node2 10.211.55.25 node3 10.211.55.26
進入node1,建立並執行run.sh,指令碼內容如下
#!/usr/bin/env bash #節點名稱 ETCD_NAME=node-1 #本機IP地址 LOCAL_IP=10.211.55.2 #ETCD儲存目錄 ETCD_DATA_DIR=/usr/local/etcd/data #初始化名稱 INITIAL_CLUSTER_TOKEN=cqh-test-cluster #初始化群集列表 INITIAL_CLUSTER="node-1=http://10.211.55.2:2380,node-2=http://10.211.55.25:2380,node-3=http://10.211.55.26:2380" #初始化狀態 INITIAL_CLUSTER_STATE=new #開始執行 etcd --name ${ETCD_NAME} --data-dir ${ETCD_DATA_DIR} \ --initial-advertise-peer-urls http://${LOCAL_IP}:2380 \ --listen-peer-urls http://${LOCAL_IP}:2380 \ --listen-client-urls http://${LOCAL_IP}:2379,http://127.0.0.1:2379 \ --advertise-client-urls http://${LOCAL_IP}:2379 \ --initial-cluster-token ${INITIAL_CLUSTER_TOKEN} \ --initial-cluster ${INITIAL_CLUSTER} \ --initial-cluster-state ${INITIAL_CLUSTER_STATE}
進入node2,建立並執行run.sh,指令碼內容和node1差不多(只修改了ETCD_NAME和LOCAL_IP)
#!/usr/bin/env bash #節點名稱 ETCD_NAME=node-2 #本機IP地址 LOCAL_IP=10.211.55.25 #ETCD儲存目錄 ETCD_DATA_DIR=/usr/local/etcd/data #初始化名稱 INITIAL_CLUSTER_TOKEN=cqh-test-cluster #初始化群集列表 INITIAL_CLUSTER="node-1=http://10.211.55.2:2380,node-2=http://10.211.55.25:2380,node-3=http://10.211.55.26:2380" #初始化狀態 INITIAL_CLUSTER_STATE=new #開始執行 etcd --name ${ETCD_NAME} --data-dir ${ETCD_DATA_DIR} \ --initial-advertise-peer-urls http://${LOCAL_IP}:2380 \ --listen-peer-urls http://${LOCAL_IP}:2380 \ --listen-client-urls http://${LOCAL_IP}:2379,http://127.0.0.1:2379 \ --advertise-client-urls http://${LOCAL_IP}:2379 \ --initial-cluster-token ${INITIAL_CLUSTER_TOKEN} \ --initial-cluster ${INITIAL_CLUSTER} \ --initial-cluster-state ${INITIAL_CLUSTER_STATE}
進入node3,建立並執行run.sh
#!/usr/bin/env bash #節點名稱 ETCD_NAME=node-3 #本機IP地址 LOCAL_IP=10.211.55.26 #ETCD儲存目錄 ETCD_DATA_DIR=/usr/local/etcd/data #初始化名稱 INITIAL_CLUSTER_TOKEN=cqh-test-cluster #初始化群集列表 INITIAL_CLUSTER="node-1=http://10.211.55.2:2380,node-2=http://10.211.55.25:2380,node-3=http://10.211.55.26:2380" #初始化狀態 INITIAL_CLUSTER_STATE=new #開始執行 etcd --name ${ETCD_NAME} --data-dir ${ETCD_DATA_DIR} \ --initial-advertise-peer-urls http://${LOCAL_IP}:2380 \ --listen-peer-urls http://${LOCAL_IP}:2380 \ --listen-client-urls http://${LOCAL_IP}:2379,http://127.0.0.1:2379 \ --advertise-client-urls http://${LOCAL_IP}:2379 \ --initial-cluster-token ${INITIAL_CLUSTER_TOKEN} \ --initial-cluster ${INITIAL_CLUSTER} \ --initial-cluster-state ${INITIAL_CLUSTER_STATE}
我們看到3臺都啟動成功了

接下來我們操作在node2上面執行操作
root@ubuntu:~# etcdctl member list 2033c1336b929ca7: name=node-3 peerURLs=http://10.211.55.26:2380 clientURLs=http://10.211.55.26:2379 isLeader=true edc51d36208cfbcf: name=node-2 peerURLs=http://10.211.55.25:2380 clientURLs=http://10.211.55.25:2379 isLeader=false f09a9dba19a725e2: name=node-1 peerURLs=http://10.211.55.2:2380 clientURLs=http://10.211.55.2:2379 isLeader=false
可以看到叢集已經生效了
然後我們再來測試一下,在ndoe2上執行操作
etcdctl set /cqh muscle
看看node1和node3是否能保持資料一致
可以看到在node1和node3中都能能夠正確的獲取/cqh的值
四.監聽功能watch演示
-
etcdctl watch key
觀察一個值的變化,觀察到變化後,列印值並watch退出
- etcdctl watch key -f
永久觀察值的變化,觀察到變化後,列印直到Ctrl+C退出
-
etcdctl exec-watch key -- sh -c 'pwd'
監聽到值有變化,就執行指定的命令(且不退出執行的可以是shell命令)
五.API操作演示
- 建立鍵值
➜~ curl http://10.211.55.25:2379/v2/keys/cqh -XPUT -d value="陳瓊和1" {"action":"set","node":{"key":"/cqh","value":"陳瓊和","modifiedIndex":14,"createdIndex":14},"prevNode":{"key":"/cqh","value":"陳瓊和","modifiedIndex":13,"createdIndex":13}}
- 建立目錄
➜~ curl http://10.211.55.25:2379/v2/keys/gym -XPUT -d dir=true {"action":"set","node":{"key":"/gym","dir":true,"modifiedIndex":12,"createdIndex":12}}
- 獲取鍵值
➜~ curl http://10.211.55.25:2379/v2/keys/cqh {"action":"get","node":{"key":"/cqh","value":"陳瓊和","modifiedIndex":14,"createdIndex":14}}
- 建立鍵值帶ttl
➜~ curl http://10.211.55.25:2379/v2/keys/hero -XPUT -d value="超人" -d ttl=5
- 建立有序鍵值
curl http://10.211.55.25:2379/v2/keys/fitness -XPOST -d value="bench_press" curl http://10.211.55.25:2379/v2/keys/fitness -XPOST -d value="dead_lift" curl http://10.211.55.25:2379/v2/keys/fitness -XPOST -d value="deep_squat"
獲取剛建立的fitness
curl http://10.211.55.25:2379/v2/keys/fitness {"action":"create","node":{"key":"/fitness/00000000000000000020","value":"bench_press","modifiedIndex":20,"createdIndex":20}} {"action":"create","node":{"key":"/fitness/00000000000000000021","value":"dead_lift","modifiedIndex":21,"createdIndex":21}} {"action":"create","node":{"key":"/fitness/00000000000000000022","value":"deep_squat","modifiedIndex":22,"createdIndex":22}} {"action":"get","node":{"key":"/fitness","dir":true,"nodes":[{"key":"/fitness/00000000000000000022","value":"deep_squat","modifiedIndex":22,"createdIndex":22},{"key":"/fitness/00000000000000000020","value":"bench_press","modifiedIndex":20,"createdIndex":20},{"key":"/fitness/00000000000000000021","value":"dead_lift","modifiedIndex":21,"createdIndex":21}],"modifiedIndex":20,"createdIndex":20}}
- 刪除鍵
curl http://10.211.55.25:2379/v2/keys/cqh -XDELETE
- 列出所有叢集成員
curl http://10.211.55.25:2379/v2/members
返回
[ { "id": "2033c1336b929ca7", "name": "node-3", "peerURLs": [ "http://10.211.55.26:2380" ], "clientURLs": [ "http://10.211.55.26:2379" ] }, { "id": "edc51d36208cfbcf", "name": "node-2", "peerURLs": [ "http://10.211.55.25:2380" ], "clientURLs": [ "http://10.211.55.25:2379" ] }, { "id": "f09a9dba19a725e2", "name": "node-1", "peerURLs": [ "http://10.211.55.2:2380" ], "clientURLs": [ "http://10.211.55.2:2379" ] } ]
- 統計資訊-檢視leader
curl http://10.211.55.25:2379/v2/stats/leader
返回
{ "leader": "2033c1336b929ca7", "followers": { "edc51d36208cfbcf": { "latency": { "current": 0.002104, "average": 0.00535051111111111, "standardDeviation": 0.0059011238840004894, "minimum": 0.001839, "maximum": 0.029456 }, "counts": { "fail": 0, "success": 45 } }, "f09a9dba19a725e2": { "latency": { "current": 0.004161, "average": 0.005481136363636365, "standardDeviation": 0.008282444790560192, "minimum": 0.001468, "maximum": 0.047605 }, "counts": { "fail": 1, "success": 44 } } } }
- 節點自身資訊
curl http://10.211.55.26:2379/v2/stats/self
返回
{ "name": "node-3", "id": "2033c1336b929ca7", "state": "StateLeader", "startTime": "2019-03-09T15:25:54.715490011+08:00", "leaderInfo": { "leader": "2033c1336b929ca7", "uptime": "38m12.85630387s", "startTime": "2019-03-09T15:25:56.01655006+08:00" }, "recvAppendRequestCnt": 0, "sendAppendRequestCnt": 90 }
- 檢視叢集執行狀態
curl http://10.211.55.26:2379/v2/stats/store
返回
{ "getsSuccess": 3, "getsFail": 29, "setsSuccess": 28, "setsFail": 0, "deleteSuccess": 0, "deleteFail": 0, "updateSuccess": 0, "updateFail": 0, "createSuccess": 3, "createFail": 0, "compareAndSwapSuccess": 0, "compareAndSwapFail": 0, "compareAndDeleteSuccess": 0, "compareAndDeleteFail": 0, "expireCount": 0, "watchers": 0 }