做者:kaliarch
連接: https://juejin.im/post/5e02fb...
背景:近期 k8s 應用中 etcd 的功能存在一些困惑,對其進行來單獨的學習,能更深刻理解 k8s 中的的一些特性。
etcd 是 CoreOS 團隊於 2013 年 6月發起的開源項目,它的目標是構建一個高可用的分佈式鍵值(key-value)數據庫。etcd 內部採用raft
協議做爲一致性算法,etcd 基於 Go 語言實現。前端
爲了保證數據的強一致性,etcd 集羣中全部的數據流向都是一個方向,從 Leader (主節點)流向 Follower,也就是全部 Follower 的數據必須與 Leader 保持一致,若是不一致會被覆蓋。node
用戶對於 etcd 集羣全部節點進行讀寫算法
假設三個節點的集羣,三個節點上均運行 Timer(每一個 Timer 持續時間是隨機的),Raft算法使用隨機 Timer 來初始化 Leader 選舉流程,第一個節點率先完成了 Timer,隨後它就會向其餘兩個節點發送成爲 Leader 的請求,其餘節點接收到請求後會以投票迴應而後第一個節點被選舉爲 Leader。數據庫
成爲 Leader 後,該節點會以固定時間間隔向其餘節點發送通知,確保本身還是Leader。有些狀況下當 Follower 們收不到 Leader 的通知後,好比說 Leader 節點宕機或者失去了鏈接,其餘節點會重複以前選舉過程選舉出新的 Leader。json
etcd 認爲寫入請求被 Leader 節點處理並分發給了多數節點後,就是一個成功的寫入。那麼多少節點如何斷定呢,假設總結點數是 N,那麼多數節點 Quorum=N/2+1
。關於如何肯定 etcd 集羣應該有多少個節點的問題,上圖的左側的圖表給出了集羣中節點總數(Instances)對應的 Quorum 數量,用 Instances 減去 Quorom 就是集羣中容錯節點(容許出故障的節點)的數量。segmentfault
因此在集羣中推薦的最少節點數量是3個,由於1和2個節點的容錯節點數都是0,一旦有一個節點宕掉整個集羣就不能正常工做了。後端
從 etcd 的架構圖中咱們能夠看到,etcd 主要分爲四個部分。centos
一般,一個用戶的請求發送過來,會經由 HTTP Server 轉發給 Store 進行具體的事務處理,若是涉及到節點的修改,則交給 Raft 模塊進行狀態的變動、日誌的記錄,而後再同步給別的 etcd 節點以確認數據提交,最後進行數據的提交,再次同步。安全
etcd 能夠用於服務的註冊與發現服務器
中間價已經後端服務在 etcd 中註冊,前端和中間價能夠很輕鬆的從 etcd 中發現相關服務器而後服務器之間根據調用關係相關綁定調用
後端多個無狀態相同副本的 app 能夠同事註冊到 etcd 中,前端能夠經過 haproxy 從etcd 中獲取到後端的 ip 和端口組,而後進行請求轉發,能夠用來故障轉移屏蔽後端端口已經後端多組app實例。
etcd 能夠充當消息中間件,生產者能夠往 etcd 中註冊 topic 併發送消息,消費者從etcd 中訂閱 topic,來獲取生產者發送至 etcd 中的消息。
後端多組相同的服務提供者能夠經本身服務註冊到 etcd 中,etcd 而且會與註冊的服務進行監控檢查,服務請求這首先從 etcd 中獲取到可用的服務提供者真正的 ip:port,而後對此多組服務發送請求,etcd 在其中充當了負載均衡的功能
當有多個競爭者 node 節點,etcd 做爲總控,在分佈式集羣中與一個節點成功分配 lock
有對個 node,etcd 根據每一個 node 來建立對應 node 的隊列,根據不一樣的隊列能夠在etcd 中找到對應的 competitor
etcd 能夠根據 raft 算法在多個 node 節點來選舉出 leader。
可使用二進制或源碼下載安裝,可是危害須要本身寫配置文件,如何要啓動須要本身寫服務啓動文件,推薦使用 yum 安裝方式
hostnamectl set-hostname etcd-1 wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm rpm -ivh epel-release-latest-7.noarch.rpm # yum 倉庫中的etcd版本爲3.3.11,若是須要最新版本的etcd能夠進行二進制安裝 yum -y install etcd systemctl enable etcd
能夠查看 yum 安裝的 etcd 的有效配置文件,根據本身的需求來修改數據存儲目錄,已經監聽端口 url/etcd 的名稱等
default.etcd/
目錄下http://localhost:2380
和集羣中其餘節點通訊http://localhost:2379
提供 HTTP API 服務,供客戶端交互default
[root@VM_0_8_centos tmp]# grep -Ev "^#|^$" /etc/etcd/etcd.conf ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_CLIENT_URLS="http://localhost:2379" ETCD_NAME="default" ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379" [root@VM_0_8_centos tmp]# systemctl status etcd
集羣部署最好部署奇數位,此能達到最好的集羣容錯
在此示例用三個節點來部署 etcd 集羣,各節點修改 hosts
cat >> /etc/hosts << EOF 172.16.0.8 etcd-0-8 172.16.0.14 etcd-0-14 172.16.0.17 etcd-0-17 EOF
三個節點均安裝 etcd
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm rpm -ivh epel-release-latest-7.noarch.rpm yum -y install etcd systemctl enable etcd mkdir -p /data/app/etcd/ chown etcd:etcd /data/app/etcd/
etcd-0-8配置:
[root@etcd-server ~]# hostnamectl set-hostname etcd-0-8 [root@etcd-0-8 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v ETCD_DATA_DIR="/data/app/etcd/" ETCD_LISTEN_PEER_URLS="http://172.16.0.8:2380" ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.8:2379" ETCD_NAME="etcd-0-8" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://172.16.0.8:2380" ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.8:2379" ETCD_INITIAL_CLUSTER="etcd-0-8=http://172.16.0.8:2380,etcd-0-17=http://172.16.0.17:2380,etcd-0-14=http://172.16.0.14:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-token" ETCD_INITIAL_CLUSTER_STATE="new"
etcd-0-14配置:
[root@etcd-server ~]# hostnamectl set-hostname etcd-0-14 [root@etcd-server ~]# mkdir -p /data/app/etcd/ [root@etcd-0.14 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v ETCD_DATA_DIR="/data/app/etcd/" ETCD_LISTEN_PEER_URLS="http://172.16.0.14:2380" ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.14:2379" ETCD_NAME="etcd-0-14" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://172.16.0.14:2380" ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.14:2379"ETCD_INITIAL_CLUSTER="etcd-0-8=http://172.16.0.8:2380,etcd-0-17=http://172.16.0.17:2380,etcd-0-14=http://172.16.0.14:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-token" ETCD_INITIAL_CLUSTER_STATE="new"
etcd-0-7配置:
[root@etcd-server ~]# hostnamectl set-hostname etcd-0-17 [root@etcd-server ~]# mkdir -p /data/app/etcd/ [root@etcd-0-17 ~]# egrep "^#|^$" /etc/etcd/etcd.conf -v ETCD_DATA_DIR="/data/app/etcd/" ETCD_LISTEN_PEER_URLS="http://172.16.0.17:2380" ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.17:2379" ETCD_NAME="etcd-0-17" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://172.16.0.17:2380"ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379,http://172.16.0.17:2379" ETCD_INITIAL_CLUSTER="etcd-0-8=http://172.16.0.8:2380,etcd-0-17=http://172.16.0.17:2380,etcd-0-14=http://172.16.0.14:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-token" ETCD_INITIAL_CLUSTER_STATE="new"
配置完成後啓動服務
systemctl start etcd
[root@etcd-0-8 default.etcd]# systemctl status etcd ● etcd.service - Etcd Server Loaded: loaded (/usr/lib/systemd/system/etcd.service; enabled; vendor preset: disabled) Active: active (running) since 二 2019-12-03 15:55:28 CST; 8s ago Main PID: 24510 (etcd) CGroup: /system.slice/etcd.service └─24510 /usr/bin/etcd --name=etcd-0-8 --data-dir=/data/app/etcd/ --listen-client-urls=http://172.16.0.8:2379 12月 03 15:55:28 etcd-0-8 etcd[24510]: set the initial cluster version to 3.0 12月 03 15:55:28 etcd-0-8 etcd[24510]: enabled capabilities for version 3.0 12月 03 15:55:30 etcd-0-8 etcd[24510]: peer 56e0b6dad4c53d42 became active 12月 03 15:55:30 etcd-0-8 etcd[24510]: established a TCP streaming connection with peer 56e0b6dad4c53d42 (stream Message reader) 12月 03 15:55:30 etcd-0-8 etcd[24510]: established a TCP streaming connection with peer 56e0b6dad4c53d42 (stream Message writer) 12月 03 15:55:30 etcd-0-8 etcd[24510]: established a TCP streaming connection with peer 56e0b6dad4c53d42 (stream MsgApp v2 reader) 12月 03 15:55:30 etcd-0-8 etcd[24510]: established a TCP streaming connection with peer 56e0b6dad4c53d42 (stream MsgApp v2 writer) 12月 03 15:55:32 etcd-0-8 etcd[24510]: updating the cluster version from 3.0 to 3.3 12月 03 15:55:32 etcd-0-8 etcd[24510]: updated the cluster version from 3.0 to 3.3 12月 03 15:55:32 etcd-0-8 etcd[24510]: enabled capabilities for version 3.3
查看端口監聽(若是未在本地監聽環回地址,那麼在本地使用etcdctl不能正常連入進去)
[root@etcd-0-8 default.etcd]# netstat -lntup |grep etcd tcp 0 0 172.16.0.8:2379 0.0.0.0:* LISTEN 25167/etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 25167/etcd tcp 0 0 172.16.0.8:2380 0.0.0.0:* LISTEN 25167/etcd
查看集羣狀態(能夠看到etcd-0-17)
[root@etcd-0-8 default.etcd]# etcdctl member list 2d2e457c6a1a76cb: name=etcd-0-8 peerURLs=http://172.16.0.8:2380 clientURLs=http://127.0.0.1:2379,http://172.16.0.8:2379 isLeader=false 56e0b6dad4c53d42: name=etcd-0-14 peerURLs=http://172.16.0.14:2380 clientURLs=http://127.0.0.1:2379,http://172.16.0.14:2379 isLeader=true d2d2e9fc758e6790: name=etcd-0-17 peerURLs=http://172.16.0.17:2380 clientURLs=http://127.0.0.1:2379,http://172.16.0.17:2379 isLeader=false [root@etcd-0-8 ~]# etcdctl cluster-health member 2d2e457c6a1a76cb is healthy: got healthy result from http://127.0.0.1:2379 member 56e0b6dad4c53d42 is healthy: got healthy result from http://127.0.0.1:2379 member d2d2e9fc758e6790 is healthy: got healthy result from http://127.0.0.1:2379 cluster is healthy
指定某個鍵的值。例如:
$ etcdctl set /testdir/testkey "Hello world" Hello world #支持的選項包括: --ttl '0' 該鍵值的超時時間(單位爲秒),不配置(默認爲0)則永不超時 --swap-with-value value 若該鍵如今的值是value,則進行設置操做 --swap-with-index '0' 若該鍵如今的索引值是指定索引,則進行設置操做
若是給定的鍵不存在,則建立一個新的鍵值。例如:
$ etcdctl mk /testdir/testkey "Hello world" Hello world #當鍵存在的時候,執行該命令會報錯,例如: $ etcdctl mk /testdir/testkey "Hello world" Error: 105: Key already exists (/testdir/testkey) [8] #支持的選項爲: --ttl '0' 超時時間(單位爲秒),不配置(默認爲 0)。則永不超時
若是給定的鍵目錄不存在,則建立一個新的鍵目錄。例如:
$ etcdctl mkdir testdir2 #支持的選項爲: --ttl '0' 超時時間(單位爲秒),不配置(默認爲0)則永不超時。
建立一個鍵目錄。若是目錄不存在就建立,若是目錄存在更新目錄TTL。
$ etcdctl setdir testdir3 #支持的選項爲: --ttl '0' 超時時間(單位爲秒),不配置(默認爲0)則永不超時。
刪除某個鍵值。例如:
$ etcdctl rm /testdir/testkeyPrevNode.Value: Hello #當鍵不存在時,則會報錯。例如: $ etcdctl rm /testdir/testkey Error: 100: Key not found (/testdir/testkey) [7] #支持的選項爲: --dir 若是鍵是個空目錄或者鍵值對則刪除 --recursive 刪除目錄和全部子鍵 --with-value 檢查現有的值是否匹配 --with-index '0'檢查現有的index是否匹配
刪除一個空目錄,或者鍵值對。
$ etcdctl setdir dir1 $ etcdctl rmdir dir1 #若目錄不空,會報錯: $ etcdctl set /dir/testkey hihi $ etcdctl rmdir /dir Error: 108: Directory not empty (/dir) [17]
當鍵存在時,更新值內容。例如:
$ etcdctl update /testdir/testkey "Hello" Hello #當鍵不存在時,則會報錯。例如: $ etcdctl update /testdir/testkey2 "Hello" Error: 100: Key not found (/testdir/testkey2) [6] #支持的選項爲: --ttl '0' 超時時間(單位爲秒),不配置(默認爲 0)則永不超時。
更新一個已經存在的目錄。
$ etcdctl updatedir testdir2 #支持的選項爲: --ttl '0' 超時時間(單位爲秒),不配置(默認爲0)則永不超時。
獲取指定鍵的值。例如:
$ etcdctl get /testdir/testkey Hello world #當鍵不存在時,則會報錯。例如: $ etcdctl get /testdir/testkey2 Error: 100: Key not found (/testdir/testkey2) [5] #支持的選項爲: --sort 對結果進行排序 --consistent 將請求發給主節點,保證獲取內容的一致性。
列出目錄(默認爲根目錄)下的鍵或者子目錄,默認不顯示子目錄中內容。
例如:
$ etcdctl ls/testdir/testdir2/dir $ etcdctl ls dir/dir/testkey #支持的選項包括: --sort 將輸出結果排序 --recursive 若是目錄下有子目錄,則遞歸輸出其中的內容-p 對於輸出爲目錄,在最後添加/進行區分
監測一個鍵值的變化,一旦鍵值發生更新,就會輸出最新的值並退出。
例如:用戶更新testkey鍵值爲Hello watch。
$ etcdctl get /testdir/testkey Hello world $ etcdctl set /testdir/testkey "Hello watch" Hello watch $ etcdctl watch testdir/testkey Hello watch
複製代碼支持的選項包括:
--forever 一直監測直到用戶按CTRL+C退出 --after-index '0' 在指定index以前一直監測 --recursive 返回全部的鍵值和子鍵值
監測一個鍵值的變化,一旦鍵值發生更新,就執行給定命令。
例如:用戶更新testkey鍵值。
$ etcdctl exec-watch testdir/testkey -- sh -c 'ls' config Documentation etcd etcdctl README-etcdctl.md README.md READMEv2-etcdctl.md
支持的選項包括:
--after-index '0' 在指定 index 以前一直監測 --recursive 返回全部的鍵值和子鍵值
備份etcd的數據。
$ etcdctl backup --data-dir /var/lib/etcd --backup-dir /home/etcd_backup
支持的選項包括:
--data-dir etcd的數據目錄 --backup-dir 備份到指定路徑
經過list
、add
、remove
命令列出、添加、刪除 etcd 實例到 etcd 集羣中。
查看集羣中存在的節點
$ etcdctl member list 8e9e05c52164694d: name=dev-master-01 peerURLs=http://localhost:2380 clientURLs=http://localhost:2379 isLeader=true
刪除集羣中存在的節點
$ etcdctl member remove 8e9e05c52164694d Removed member 8e9e05c52164694d from cluster
向集羣中新加節點
$ etcdctl member add etcd3 http://192.168.1.100:2380 Added member named etcd3 with ID 8e9e05c52164694d to cluster
# 設置一個key值 [root@etcd-0-8 ~]# etcdctl set /msg "hello k8s" hello k8s # 獲取key的值 [root@etcd-0-8 ~]# etcdctl get /msg hello k8s # 獲取key值的詳細信息 [root@etcd-0-8 ~]# etcdctl -o extended get /msg Key: /msg Created-Index: 12 Modified-Index: 12 TTL: 0 Index: 12 hello k8s # 獲取不存在的key回報錯 [root@etcd-0-8 ~]# etcdctl get /xxzx Error: 100: Key not found (/xxzx) [12] # 設置key的ttl,過時後會被自動刪除 [root@etcd-0-8 ~]# etcdctl set /testkey "tmp key test" --ttl 5 tmp key test [root@etcd-0-8 ~]# etcdctl get /testkey Error: 100: Key not found (/testkey) [14] # key 替換操做 [root@etcd-0-8 ~]# etcdctl get /msg hello k8s [root@etcd-0-8 ~]# etcdctl set --swap-with-value "hello k8s" /msg "goodbye" goodbye [root@etcd-0-8 ~]# etcdctl get /msg goodbye # mk 僅當key不存在時建立(set對同一個key會覆蓋) [root@etcd-0-8 ~]# etcdctl get /msg goodbye [root@etcd-0-8 ~]# etcdctl mk /msg "mktest" Error: 105: Key already exists (/msg) [18] [root@etcd-0-8 ~]# etcdctl mk /msg1 "mktest" mktest # 建立自排序的key [root@etcd-0-8 ~]# etcdctl mk --in-order /queue s1s1 [root@etcd-0-8 ~]# etcdctl mk --in-order /queue s2s2 [root@etcd-0-8 ~]# etcdctl ls --sort /queue/queue/00000000000000000021 /queue/00000000000000000022 [root@etcd-0-8 ~]# etcdctl get /queue/00000000000000000021 s1 # 更新key值 [root@etcd-0-8 ~]# etcdctl update /msg1 "update test" update test [root@etcd-0-8 ~]# etcdctl get /msg1 update test # 更新key的ttl及值 [root@etcd-0-8 ~]# etcdctl update --ttl 5 /msg "aaa" aaa # 建立目錄 [root@etcd-0-8 ~]# etcdctl mkdir /testdir # 刪除空目錄 [root@etcd-0-8 ~]# etcdctl mkdir /test1 [root@etcd-0-8 ~]# etcdctl rmdir /test1 # 刪除非空目錄 [root@etcd-0-8 ~]# etcdctl get /testdir/test dir: is a directory [root@etcd-0-8 ~]# [root@etcd-0-8 ~]# etcdctl rm --recursive /testdir # 列出目錄內容 [root@etcd-0-8 ~]# etcdctl ls / /tmp /msg1 /queue [root@etcd-0-8 ~]# etcdctl ls /tmp /tmp/a /tmp/b # 遞歸列出目錄的內容 [root@etcd-0-8 ~]# etcdctl ls --recursive / /msg1 /queue /queue/00000000000000000021 /queue/00000000000000000022 /tmp /tmp/b /tmp/a # 監聽key,當key發生改變的時候打印出變化 [root@etcd-0-8 ~]# etcdctl watch /msg1 xxx [root@VM_0_17_centos ~]# etcdctl update /msg1 "xxx" xxx # 監聽某個目錄,當目錄中任何 node 改變的時候,都會打印出來 [root@etcd-0-8 ~]# etcdctl watch --recursive /[update] /msg1 xxx [root@VM_0_17_centos ~]# etcdctl update /msg1 "xxx" xxx # 一直監聽,除非 `CTL + C` 致使退出監聽 [root@etcd-0-8 ~]# etcdctl watch --forever / # 監聽目錄,當發生變化時執行一條命令 [root@etcd-0-8 ~]# etcdctl exec-watch --recursive / -- sh -c "echo change" change # backup [root@etcd-0-14 ~]# etcdctl backup --data-dir /data/app/etcd --backup-dir /root/etcd_backup 2019-12-04 10:25:16.113237 I | ignoring EntryConfChange raft entry2019-12-04 10:25:16.113268 I | ignoring EntryConfChange raft entry 2019-12-04 10:25:16.113272 I | ignoring EntryConfChange raft entry2019-12-04 10:25:16.113293 I | ignoring member attribute update on /0/members/2d2e457c6a1a76cb/attributes 2019-12-04 10:25:16.113299 I | ignoring member attribute update on /0/members/d2d2e9fc758e6790/attributes 2019-12-04 10:25:16.113305 I | ignoring member attribute update on /0/members/56e0b6dad4c53d42/attributes 2019-12-04 10:25:16.113310 I | ignoring member attribute update on /0/members/56e0b6dad4c53d42/attributes 2019-12-04 10:25:16.113314 I | ignoring member attribute update on /0/members/2d2e457c6a1a76cb/attributes 2019-12-04 10:25:16.113319 I | ignoring member attribute update on /0/members/d2d2e9fc758e6790/attributes 2019-12-04 10:25:16.113384 I | ignoring member attribute update on /0/members/56e0b6dad4c53d42/attributes # 使用v3版本 [root@etcd-0-14 ~]# export ETCDCTL_API=3 [root@etcd-0-14 ~]# etcdctl --endpoints="http://172.16.0.8:2379,http://172.16.0.14:2379,http://172.16.0.17:2379" snapshot save mysnapshot.db Snapshot saved at mysnapshot.db [root@etcd-0-14 ~]# etcdctl snapshot status mysnapshot.db -w json {"hash":928285884,"revision":0,"totalKey":5,"totalSize":20480}
若有錯誤或其它問題,歡迎小夥伴留言評論、指正。若有幫助,歡迎點贊+轉發分享。
歡迎你們關注民工哥的公衆號:民工哥技術之路