Etcd 是一個使用一致性哈希算法(Raft)在分佈式環境下的 key/value 存儲服務。利用 Etcd 的特性,應用程序能夠在集羣中共享信息、配置或做服務發現,Etcd 會在集羣的各個節點中複製這些數據並保證這些數據始終正確。html
System Requirements >= 8v CPU + 16GB RAM + 50GB SSDnode
靜態就是在配置服務以前已經知道了節點的地址和集羣的大小git
############################ # Build the latest version ############################ # 1.下載項目並編譯 $ git clone https://github.com/etcd-io/etcd.git && cd etcd $ ./build To build a vendored etcd from the master branch via go get: # 2.設置GOPATH環境變量 $ export GOPATH='/Users/example/go' $ go get -v go.etcd.io/etcd $ go get -v go.etcd.io/etcd/etcdctl # 3.啓動服務 $ ./bin/etcd $ $GOPATH/bin/etcd # 4.簡單使用 $ ./bin/etcdctl put foo bar OK
################################## # Running etcd in standalone mode ################################## # 1.設置啓動的Node地址 $ export NODE1='172.16.176.52' # 2.建立一個邏輯存儲 $ docker volume create --name etcd-data # 3.啓動etcd服務 # 正式的ectd端口是2379用於客戶端鏈接,而2380用於夥伴通信 # --data-dir: 到數據目錄的路徑 # --initial-advertise-peer-urls: 集羣中節點間通信的URL地址 # --listen-peer-urls: 集羣中節點間通信的URL地址 # --advertise-client-urls: 客戶端監聽的URL地址 # --listen-client-urls: 客戶端監聽的URL地址 # --initial-cluster: 啓動初始化集羣配置 $ docker run -p 2379:2379 -p 2380:2380 --name etcd \ --volume=etcd-data:/etcd-data \ quay.io/coreos/etcd:latest \ /usr/local/bin/etcd \ --data-dir=/etcd-data --name node1 \ --initial-advertise-peer-urls http://${NODE1}:2380 \ --listen-peer-urls http://0.0.0.0:2380 \ --advertise-client-urls http://${NODE1}:2379 \ --listen-client-urls http://0.0.0.0:2379 \ --initial-cluster node1=http://${NODE1}:2380 # 4.列出如今集羣中的服務狀態 $ etcdctl --endpoints=http://${NODE1}:2379 member list
################################ # Running a 3 node etcd cluster ################################ # node1 docker run -p 2379:2379 -p 2380:2380 --name etcd-node-1 \ --volume=/var/lib/etcd:/etcd-data \ quay.io/coreos/etcd:latest \ /usr/local/bin/etcd \ --data-dir=/etcd-data \ --initial-advertise-peer-urls "http://10.20.30.1:2380" \ --listen-peer-urls "http://0.0.0.0:2380" \ --advertise-client-urls "http://10.20.30.1:2379" \ --listen-client-urls "http://0.0.0.0:2379" \ --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \ --initial-cluster-state "new" \ --initial-cluster-token "my-etcd-token" # node2 docker run -p 2379:2379 -p 2380:2380 --name etcd-node-2 \ --volume=/var/lib/etcd:/etcd-data \ quay.io/coreos/etcd:latest \ /usr/local/bin/etcd \ --data-dir=/etcd-data \ --initial-advertise-peer-urls "http://10.20.30.2:2380" \ --listen-peer-urls "http://0.0.0.0:2380" \ --advertise-client-urls "http://10.20.30.2:2379" \ --listen-client-urls "http://0.0.0.0:2379" \ --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \ --initial-cluster-state "new" \ --initial-cluster-token "my-etcd-token" # node3 docker run -p 2379:2379 -p 2380:2380 --name etcd-node-3 \ --volume=/var/lib/etcd:/etcd-data \ quay.io/coreos/etcd:latest \ /usr/local/bin/etcd \ --data-dir=/etcd-data \ --initial-advertise-peer-urls "http://10.20.30.3:2380" \ --listen-peer-urls "http://0.0.0.0:2380" \ --advertise-client-urls "http://10.20.30.3:2379" \ --listen-client-urls "http://0.0.0.0:2379" \ --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \ --initial-cluster-state "new" \ --initial-cluster-token "my-etcd-token" # run etcdctl using API version 3 docker exec etcd /bin/sh -c "export ETCDCTL_API=3 && /usr/local/bin/etcdctl put foo bar"
# 編輯docker-compose.yml文件 version: "3.6" services: node1: image: quay.io/coreos/etcd volumes: - node1-data:/etcd-data expose: - 2379 - 2380 networks: cluster_net: ipv4_address: 172.16.238.100 environment: - ETCDCTL_API=3 command: - /usr/local/bin/etcd - --data-dir=/etcd-data - --name - node1 - --initial-advertise-peer-urls - http://172.16.238.100:2380 - --listen-peer-urls - http://0.0.0.0:2380 - --advertise-client-urls - http://172.16.238.100:2379 - --listen-client-urls - http://0.0.0.0:2379 - --initial-cluster - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380 - --initial-cluster-state - new - --initial-cluster-token - docker-etcd node2: image: quay.io/coreos/etcd volumes: - node2-data:/etcd-data networks: cluster_net: ipv4_address: 172.16.238.101 environment: - ETCDCTL_API=3 expose: - 2379 - 2380 command: - /usr/local/bin/etcd - --data-dir=/etcd-data - --name - node2 - --initial-advertise-peer-urls - http://172.16.238.101:2380 - --listen-peer-urls - http://0.0.0.0:2380 - --advertise-client-urls - http://172.16.238.101:2379 - --listen-client-urls - http://0.0.0.0:2379 - --initial-cluster - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380 - --initial-cluster-state - new - --initial-cluster-token - docker-etcd node3: image: quay.io/coreos/etcd volumes: - node3-data:/etcd-data networks: cluster_net: ipv4_address: 172.16.238.102 environment: - ETCDCTL_API=3 expose: - 2379 - 2380 command: - /usr/local/bin/etcd - --data-dir=/etcd-data - --name - node3 - --initial-advertise-peer-urls - http://172.16.238.102:2380 - --listen-peer-urls - http://0.0.0.0:2380 - --advertise-client-urls - http://172.16.238.102:2379 - --listen-client-urls - http://0.0.0.0:2379 - --initial-cluster - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380 - --initial-cluster-state - new - --initial-cluster-token - docker-etcd volumes: node1-data: node2-data: node3-data: networks: cluster_net: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24
# 使用啓動集羣 docker-compose up -d # 以後使用以下命令登陸到任一節點測試etcd集羣 docker exec -it node1 bash # etcdctl member list 422a74f03b622fef, started, node1, http://172.16.238.100:2380, http://172.16.238.100:2379 ed635d2a2dbef43d, started, node2, http://172.16.238.101:2380, http://172.16.238.101:2379 daf3fd52e3583ffe, started, node3, http://172.16.238.102:2380, http://172.16.238.102:2379
--name #指定節點名稱 --data-dir #指定節點的數據存儲目錄,用於保存日誌和快照 --addr #公佈的 IP 地址和端口;默認爲 127.0.0.1:2379 --bind-addr #用於客戶端鏈接的監聽地址;默認爲–addr 配置 --peers #集羣成員逗號分隔的列表;例如 127.0.0.1:2380,127.0.0.1:2381 --peer-addr #集羣服務通信的公佈的 IP 地址;默認爲 127.0.0.1:2380 -peer-bind-addr #集羣服務通信的監聽地址;默認爲-peer-addr 配置 --wal-dir #指定節點的 wal 文件的存儲目錄,若指定了該參數 wal 文件會和其餘數據文件分開存儲 --listen-client-urls #監聽 URL;用於與客戶端通信 --listen-peer-urls #監聽 URL;用於與其餘節點通信 --initial-advertise-peer-urls #告知集羣其餘節點 URL --advertise-client-urls #告知客戶端 URL --initial-cluster-token #集羣的 ID --initial-cluster #集羣中全部節點 --initial-cluster-state new #表示從無到有搭建 etcd 集羣 --discovery-srv #用於 DNS 動態服務發現,指定 DNS SRV 域名 --discovery #用於 etcd 動態發現,指定 etcd 發現服務的 URL
etcd 的數據存儲有點像 PG 數據庫的存儲方式github
etcd 目前支持 V2 和 V3 兩個大版本,這兩個版本在實現上有比較大的不一樣,一方面是對外提供接口的方式,另外一方面就是底層的存儲引擎,V2 版本的實例是一個純內存的實現,全部的數據都沒有存儲在磁盤上,而 V3 版本的實例就支持了數據的持久化。算法
咱們都知道 etcd 爲咱們提供了 key/value 的服務目錄存儲。docker
# 設置鍵值對 $ etcdctl set name escape # 獲取方式 $ etcdctl get name escape
使用 etcd 以後,咱們會疑問數據都存儲到的那裏呢?數據默認會存放在 /var/lib/etcd/default/ 目錄。咱們會發現數據所在的目錄,會被分爲兩個文件夾中,分別是 snap 和 wal目錄。數據庫
# 目錄結構 $ tree /var/lib/etcd/default/ default └── member ├── snap │ ├── 0000000000000006-0000000000046ced.snap │ ├── 0000000000000006-00000000000493fe.snap │ ├── 0000000000000006-000000000004bb0f.snap │ ├── 0000000000000006-000000000004e220.snap │ └── 0000000000000006-0000000000050931.snap └── wal └── 0000000000000000-0000000000000000.wal
使用 WAL 進行數據的存儲使得 etcd 擁有兩個重要功能,那就是故障快速恢復和數據回滾/重作。安全
既然有了 WAL 實時存儲了全部的變動,爲何還須要 snapshot 呢?隨着使用量的增長,WAL 存儲的數據會暴增。爲了防止磁盤很快就爆滿,etcd 默認每 10000 條記錄作一次 snapshot 操做,通過 snapshot 之後的 WAL 文件就能夠刪除。而經過 API 能夠查詢的歷史 etcd 操做默認爲 1000 條。bash
首次啓動時,etcd 會把啓動的配置信息存儲到 data-dir 參數指定的數據目錄中。配置信息包括本地節點的ID、集羣ID和初始時集羣信息。用戶須要避免 etcd 從一個過時的數據目錄中從新啓動,由於使用過時的數據目錄啓動的節點會與集羣中的其餘節點產生不一致。因此,爲了最大化集羣的安全性,一旦有任何數據損壞或丟失的可能性,你就應該把這個節點從集羣中移除,而後加入一個不帶數據目錄的新節點。服務器
保證一致性的共識算法
在每個分佈式系統中,etcd 每每都扮演了很是重要的地位,因爲不少服務配置發現以及配置的信息都存儲在 etcd 中,因此整個集羣可用性的上限每每就是 etcd 的可用性,而使用 3 ~ 5 個 etcd 節點構成高可用的集羣每每都是常規操做。
正是由於 etcd 在使用的過程當中會啓動多個節點,如何處理幾個節點之間的分佈式一致性就是一個比較有挑戰的問題了。解決多個節點數據一致性的方案其實就是共識算法,etcd 使用的就是 Raft 共識算法。
Raft 從一開始就被設計成一個易於理解和實現的共識算法,它在容錯和性能上與 Paxos 協議比較相似,區別在於它將分佈式一致性的問題分解成了幾個子問題,而後一一進行解決。
每個 Raft 集羣中都包含多個服務器,在任意時刻,每一臺服務器只可能處於 Leader、Follower 以及 Candidate 三種狀態;在處於正常的狀態時,集羣中只會存在一個 Leader 狀態,其他的服務器都是 Follower 狀態。
全部的 Follower 節點都是被動的,它們不會主動發出任何的請求,只會響應 Leader 和 Candidate 發出的請求。對於每個用戶的可變操做,都會被路由給 Leader 節點進行處理,除了 Leader 和 Follower 節點以外,Candidate 節點其實只是集羣運行過程當中的一個臨時狀態。
Raft 集羣中的時間也被切分紅了不一樣的幾個任期(Term),每個任期都會由 Leader 的選舉開始,選舉結束後就會進入正常操做的階段,直到 Leader 節點出現問題纔會開始新一輪的選擇。
每個服務器都會存儲當前集羣的最新任期,它就像是一個單調遞增的邏輯時鐘,可以同步各個節點之間的狀態,當前節點持有的任期會隨着每個請求被傳遞到其餘的節點上。Raft 協議在每個任期的開始時都會從一個集羣中選出一個節點做爲集羣的 Leader 節點,這個節點會負責集羣中的日誌的複製以及管理工做。
咱們將 Raft 協議分紅三個子問題:節點選舉、日誌複製以及安全性。
服務發現是 etcd 服務的主要的用途之一
服務發現要解決的也是分佈式系統中最多見的問題之一,即在同一個分佈式集羣中的進程或服務,要如何才能找到對方並創建鏈接。本質上來講,服務發現就是想要了解集羣中是否有進程在監聽 UDP 或 TCP 端口,而且經過名字就能夠查找和鏈接。要解決服務發現的問題,須要有下面三大支柱,缺一不可。
平常開發集羣管理功能中,若是要設計能夠動態調整集羣大小。那麼首先就要支持服務發現,就是說當一個新的節點啓動時,能夠將本身的信息註冊給 master,而後讓 master 把它加入到集羣裏,關閉以後也能夠把本身從集羣中刪除。etcd 提供了很好的服務註冊與發現的基礎功,咱們採用 etcd 來作服務發現時,能夠把精力用於服務自己的業務處理上。
etcd 在鍵的組織上採用了層次化的空間結構,相似於文件系統中目錄的概念,數據庫操做圍繞對鍵值和目錄的 CRUD 完整生命週期的管理。etcdctl 是一個命令行的客戶端,它提供了一下簡潔的命令,可理解爲命令工具集,能夠方便咱們在對服務進行測試或者手動修改數據庫內容。etcdctl 與其餘 xxxctl 的命令原理及操做相似,如 systemctl 等等。
# 列出etcd集羣中的實例 $ etcdctl member list # 添加etcd集羣中的實例 $ etcdctl member add <實例> # 刪除etcd集羣中的實例 $ etcdctl member remove <實例> # 更新etcd集羣中的實例 $ etcdctl member update <實例>
--name #指定節點名稱
Etcd 集羣備份和數據恢復以及優化運維
etcd 被設計爲能承受集羣自動從臨時失敗(例如機器重啓)中恢復,並且對於一個有 N 個成員的集羣能允許 (N-1)/2 的持續失敗。當一個成員持續失敗時,不論是由於硬件失敗或者磁盤損壞,它丟失到集羣的訪問。若是集羣持續丟失超過 (N-1)/2 的成員,則它只能悲慘的失敗,無可救藥的失去法定人數(quorum)。一旦法定人數丟失,集羣沒法達到一致性而致使沒法繼續接收更新。爲了從災難失敗中恢復數據,etcd v3 提供快照和修復工具來重建集羣而不丟失 v3 鍵數據。
因爲 v3 版本的 etcd 證書是基於 IP 的,因此每次新增 etcd 節點都須要從新制做證書。爲了咱們更方便的使用,能夠查看這個連接來 etcd 證書製做。詳情: https://github.com/cloudflare...
恢復集羣,首先須要來自 etcd 成員的鍵空間的快照。快速能夠是用 etcdctl snapshot save 命令從活動成員中獲取。或者是從 etcd 數據目錄複製 member/snap/db 文件。例如,下列命令快照在 $ENDPOINT 上服務的鍵空間到文件 snapshot.db。
# 集羣備份etcd數據快照 # $ENDPOINT => http://10.20.30.1:2379 $ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db # 在單節點etcd上執行下面的命令就能夠對etcd進行數據備份 # 每兩個小時備份一次數據並上傳到S3上,並保留最近兩天的數據 $ ETCDCTL_API=3 etcdctl snapshot save /var/lib/etcd_backup/etcd_$(date "+%Y%m%d%H%M%S").db
爲了恢復集羣,使用以前任意節點上備份的快照 "db" 文件。恢復的手,可使用 etcdctl snapshot restore 命令來恢復 etcd 數據目錄,此時全部成員應該使用相同的快照恢復。由於恢復數據死後,會覆蓋某些快照元數據(特別是成員ID和集羣ID)信息,集羣內的成員可能會丟失它以前的標識。所以爲了從快照啓動集羣,恢復必須啓動一個新的邏輯集羣。
在恢復時,快照完整性的檢驗是可選的。若是快照是經過 etcdctl snapshot save 獲得的話,使用 etcdctl snapshot restore 命令恢復的時候,會檢查 hash 值的完整性。若是快照是從數據目錄複製而來,則沒有完整性校驗,所以它只能經過使用 --skip-hash-check 來恢復。
下面爲一個 3 成員的集羣建立新的 etcd 數據目錄:
$ etcdctl snapshot restore snapshot.db \ --name m1 \ --initial-cluster m1=http:/host1:2380,m2=http://host2:2380,m3=http://host3:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls http://host1:2380 $ etcdctl snapshot restore snapshot.db \ --name m2 \ --initial-cluster m1=http:/host1:2380,m2=http://host2:2380,m3=http://host3:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls http://host2:2380 $ etcdctl snapshot restore snapshot.db \ --name m3 \ --initial-cluster m1=http:/host1:2380,m2=http://host2:2380,m3=http://host3:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls http://host3:2380
下一步, 用新的數據目錄啓動 etcd 服務,如今恢復的集羣可使用並提供來自快照的鍵空間服務。
$ etcd \ --name m1 \ --listen-client-urls http://host1:2379 \ --advertise-client-urls http://host1:2379 \ --listen-peer-urls http://host1:2380 & $ etcd \ --name m2 \ --listen-client-urls http://host2:2379 \ --advertise-client-urls http://host2:2379 \ --listen-peer-urls http://host2:2380 & $ etcd \ --name m3 \ --listen-client-urls http://host3:2379 \ --advertise-client-urls http://host3:2379 \ --listen-peer-urls http://host3:2380 &
做者: Escape
https://escapelife.github.io/...