以etcd作爲持久化服務的開發與傳統基於關係型數據庫的開發有所區別,與基於Nosql數據庫和redis的開發也不同,以下作記錄:
-
etcd 主要用於非頻繁更新的數據,如
meta data
等。 且提供訂閱watch
的API
, redis也有類似功能 -
etcd
本質上是一個有序的k-v
存儲,key
值用btree
存儲在內存,value
存在磁盤。 -
etcd
採用raft算法作爲一致性算法, 因此其瓶頸在於單個Leader
的問題,不能支撐較大的訪問流量(QPS
)。分佈式一致性問題是計算機科學的重要議題, 關於raft算法的基礎原理見此 -
etcd
的性能benckmark
官方測試見此, 可見其在可接受的延時下(假定位50ms
)的, 合理客戶端數(1 ~ 200 +
)下,其讀寫QPS
約在2000 - 16000
範圍內。 -
由於
etcd
側重於元數據的一致性存儲及訪問, 常用於服務發現或支撐元數據服務的存儲層, 如其get
接口會返回除k-v
外的額外信息, e.g."revision":xxx
, 用於顯示該k-v
被修改過的次數,在開發中, 可將其應用在業務邏輯中, kubernetes的典型例子見此, 其對etcd
的使用和理解是etcd
使用的最佳實踐之一。 -
etcd
支持事務,每次事務完成只形成一次revision
, 關於etcd事務的詳細描述見此, transaction本質上與redis 的pipeline不同,pipeling
內的命令不一定滿足事務原子性。 -
關於
etcd
的更多介紹和原理見此, 其中這裏將etcd
與ZooKeeper
和Consul
等作比較。這裏也有一篇不錯的中文參考。 -
關於
etcd
的選舉機制, 數據一致性機制和集羣容錯能力等的相關注意項, 如etcd
集羣節點的增加註意點, 詳見etcd FAQ 。文中有關於etcd
集羣的節點數推薦爲奇數的理由敘述,在這裏概括一下:- 同一
etcd
集羣內,數據一致性以大多數節點(majority
)爲依據實施仲裁機制(quorum
),majority = (n/2)+1
。 如3節點的集羣,majority
爲2, 4節點的集羣,majority
爲3。若集羣的majority
不能達到要求,則會失去仲裁quorum
, 引起數據丟失 。這一點容易引起誤解, 這裏以一個例子來闡述。如3節點集羣在啓動時,etcd-1
,etcd-2
和etcd-3
均狀態正常,此時majority
已設定爲2, 但在一段時間後,etcd-3
節點故障, 失去通信,但其仍登記在集羣, 只是狀態爲不可用。etcd-1
和etcd-2
仍正常運作, 仍滿足majority
爲2的要求,quorum
正常。 若此後etcd-2
節點故障,則會導致不滿足majority
爲2的要求,quorum
丟失。 Failure Tolerance
爲集羣能容忍的最大故障節點數。Failure Tolerance = n - majroity
。承接上一點的例子, 可得奇數節點集羣和偶數節點集羣的Failure Tolerance
均爲1。在Failure Tolerance
相同的情況下, 節點數越多,其故障的期望值越高。因此推薦奇數節點數集羣。- 對於故障節點數已經達到
Failure Tolerance
的集羣,直接增加新的etcd
節點的做法風險很高。舉例,3節點集羣, 故障節點爲etcd-3
, 此時新增etcd-4
, 使得集羣節點數變爲4,majority
變爲3。若此時etcd-4
配置錯誤, 則故障節點爲etcd-3
和etcd-4
,quorum
丟失。因此正確做法應該是先剔除etcd-3
,再增加etcd-4
。 - 集羣節點數越多,
Failure Tolerance
越高, 但節點間數據同步開銷越大。雖然理論上etcd
集羣節點數無限制,在最佳實踐中推薦5個或7個節點。
- 同一
-
用
docker
搭建實驗環境的etcd
很簡單, 參見, 也可對docker-compose.yml
稍作修改, 使其暴露端口, 方便調試
version: '2'
services:
etcd:
image: bitnami/etcd:3
environment:
- ALLOW_NONE_AUTHENTICATION=yes
volumes:
- etcd_data:/bitnami
ports:
- "2379:2379"
- "2380:2380"
...
- 使用
etcdctl
可以與遠程或本地的etcd
,etcdctl
在etcd
的安裝包內, 詳見。使用舉例如下:
ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key get / --prefix --keys-only
以上etcdctl
列出kubernentes
中在etcd
中存儲的所有key/value
.例子運行機器是k8s
的master
節點。
etcd
幾乎沒有任何數據結構, 類似redis
的set和 hashMap 等, 但一般使用時, 可以通過對key
值的設計實現key
之間的關聯。如kubernetes
中要獲取所有namespaces
, 其設計思路是將所有namespace
的key
以/foo/bar/..
的形式來表達集合包含關係, 如:
ETCDCTL_API=3 etcdctl ... get /registry/namespace --prefix --keys-only
/registry/namespaces/cattle-system
/registry/namespaces/default
/registry/namespaces/kube-node-lease
/registry/namespaces/kube-public
/registry/namespaces/kube-system
/registry/namespaces/label-system
/registry/namespaces/nginx-ingress
這裏/registry/namespace --prefix
獲取了所有以/registry/namespace
爲前綴的key
。
-
key-value
中的value
一般爲字節數組, 業務實現中可以對於重要的元數據, 在put
的時候用加密算法加密,get
用加密算法解密。 -
對刪除和更新
key-value
等操作, 應符合事務操作規範, e.g. :
for {
txnResp, err := s.client.KV.Txn(ctx).If(
clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev),
).Then(
clientv3.OpDelete(key),
).Else(
clientv3.OpGet(key),
).Commit()
if err != nil {
return err
}
if !txnResp.Succeeded {
getResp = (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
continue
}
return. ..
}
這裏使用了事務和循環去確保所刪除的對象爲最後一次更新的對象。
-
相關議題:
kubernetes
使用etcd
作爲存儲層,實現各種Object
的CRUD
操作, 涉及到一致性的例子是, 當多客戶端在同一時刻對相同對象(相同namespace
)進行更新等操作時,kubernetes
的應對和實現邏輯。 從更新資源的Restful API
的handler
實現, 源碼見此, 到update 邏輯實現見此, 及此, 最後到持久化實現, 見此。可見k8s
對於重要資源的更新,作了很多保證更新有效性的操作。
Reference
https://yq.aliyun.com/articles/738055?spm=a2c4e.11155472.0.0.560075355p5Gc7