關於基於etcd服務的開發記錄

etcd作爲持久化服務的開發與傳統基於關係型數據庫的開發有所區別,與基於Nosql數據庫和redis的開發也不同,以下作記錄:

  • etcd 主要用於非頻繁更新的數據,如meta data等。 且提供訂閱watchAPI, 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更多介紹和原理見此, 其中這裏etcdZooKeeperConsul等作比較。這裏也有一篇不錯的中文參考。

  • etcd暴露的apiv2v3其區別見此, 推薦使用v3

  • 關於etcd的選舉機制, 數據一致性機制和集羣容錯能力等的相關注意項, 如etcd集羣節點的增加註意點, 詳見etcd FAQ 。文中有關於etcd集羣的節點數推薦爲奇數的理由敘述,在這裏概括一下:

    • 同一etcd集羣內,數據一致性以大多數節點(majority )爲依據實施仲裁機制(quorum), majority = (n/2)+1。 如3節點的集羣,majority爲2, 4節點的集羣, majority爲3。若集羣的majority不能達到要求,則會失去仲裁quorum, 引起數據丟失 。這一點容易引起誤解, 這裏以一個例子來闡述。如3節點集羣在啓動時,etcd-1, etcd-2etcd-3均狀態正常,此時majority已設定爲2, 但在一段時間後,etcd-3 節點故障, 失去通信,但其仍登記在集羣, 只是狀態爲不可用。etcd-1etcd-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-3etcd-4quorum丟失。因此正確做法應該是先剔除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可以與遠程或本地的etcdetcdctletcd的安裝包內, 詳見。使用舉例如下:
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.例子運行機器是k8smaster節點。

  • etcd 幾乎沒有任何數據結構, 類似redissethashMap 等, 但一般使用時, 可以通過對key值的設計實現key之間的關聯。如kubernetes中要獲取所有namespaces, 其設計思路是將所有namespacekey/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作爲存儲層,實現各種ObjectCRUD操作, 涉及到一致性的例子是, 當多客戶端在同一時刻對相同對象(相同namespace)進行更新等操作時, kubernetes的應對和實現邏輯。 從更新資源的Restful APIhandler實現, 源碼見此, 到update 邏輯實現見此, 及此, 最後到持久化實現, 見此。可見k8s對於重要資源的更新,作了很多保證更新有效性的操作。

  • 相關議題:kubernetesnamespaceetcd 3.2版本推出的namespace不是同一個概念。

Reference

https://yq.aliyun.com/articles/738055?spm=a2c4e.11155472.0.0.560075355p5Gc7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章