在k8s集群中使用了etcd作為數據中心,在實際操作中遇到了一些坑。今天記錄一下,為了以后更好操作。
ETCD參數說明
- —data-dir 指定節點的數據存儲目錄,這些數據包括節點ID,集群ID,集群初始化配置,Snapshot文件,若未指定—wal-dir,還會存儲WAL文件;
- —wal-dir 指定節點的was文件的存儲目錄,若指定了該參數,wal文件會和其他數據文件分開存儲。
- —name 節點名稱
- —initial-advertise-peer-urls 告知集群其他節點url.
- — listen-peer-urls 監聽URL,用於與其他節點通訊
- — advertise-client-urls 告知客戶端url, 也就是服務的url
- — initial-cluster-token 集群的ID
- — initial-cluster 集群中所有節點
節點遷移
在生產環境中,不可避免遇到機器硬件故障。當遇到硬件故障發生的時候,我們需要快速恢復節點。ETCD集群可以做到在不丟失數據的,並且不改變節點ID的情況下,遷移節點。
具體辦法是:
- 1)停止待遷移節點上的etc進程;
- 2)將數據目錄打包復制到新的節點;
- 3)更新該節點對應集群中peer url,讓其指向新的節點;
- 4)使用相同的配置,在新的節點上啟動etcd進程
-
etcd配置
-
node1
編輯etcd啟動腳本
/usr/local/etcd/start.sh
/usr/local/etcd/etcd -name niub1 -debug \ -initial-advertise-peer-urls http://niub-etcd-1:2380 \ -listen-peer-urls http://niub-etcd-1:2380 \ -listen-client-urls http://niub-etcd-1:2379,http://127.0.0.1:2379 \ -advertise-client-urls http://niub-etcd-1:2379 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster niub1=http://niub-etcd-1:2380,niub2=http://niub-etcd-2:2380,niub3=http://niub-etcd-3:2380 \ -initial-cluster-state new >> /niub/etcd_log/etcd.log 2>&1 &
node2
編輯etcd啟動腳本
/usr/local/etcd/start.sh
/usr/local/etcd/etcd -name niub2 -debug \ -initial-advertise-peer-urls http://niub-etcd-2:2380 \ -listen-peer-urls http://niub-etcd-2:2380 \ -listen-client-urls http://niub-etcd-2:2379,http://127.0.0.1:2379 \ -advertise-client-urls http://niub-etcd-2:2379 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster niub1=http://niub-etcd-1:2380,niub2=http://niub-etcd-2:2380,niub3=http://niub-etcd-3:2380 \ -initial-cluster-state new >> /niub/etcd_log/etcd.log 2>&1 &
node3
編輯etcd啟動腳本
/usr/local/etcd/start.sh
/usr/local/etcd/etcd -name niub3 -debug \ -initial-advertise-peer-urls http://niub-etcd-3:2380 \ -listen-peer-urls http://niub-etcd-3:2380 \ -listen-client-urls http://niub-etcd-3:2379,http://127.0.0.1:2379 \ -advertise-client-urls http://niub-etcd-3:2379 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster niub1=http://niub-etcd-1:2380,niub2=http://niub-etcd-2:2380,niub3=http://niub-etcd-3:2380 \ -initial-cluster-state new >> /niub/etcd_log/etcd.log 2>&1 &
防火牆
在這3台node服務器開放2379、2380端口,命令:
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 2379 -j ACCEPT iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 2380 -j ACCEPT
haproxy配置
haproxy配置過程略 編輯
/etc/haproxy/haproxy.cfg
文件,增加:frontend etcd bind 10.10.0.14:2379 mode tcp option tcplog default_backend etcd log 127.0.0.1 local3 backend etcd balance roundrobin fullconn 1024 server etcd1 10.10.0.11:2379 check port 2379 inter 300 fall 3 server etcd2 10.10.0.12:2379 check port 2379 inter 300 fall 3 server etcd3 10.10.0.13:2379 check port 2379 inter 300 fall 3
檢查etcd服務運行狀態
使用curl訪問:
curl http://10.10.0.14:2379/v2/members
返回以下結果為正常(3個節點):
{
"members": [ { "id": "1f890e0c67371d24", "name": "niub1", "peerURLs": [ "http://niub-etcd-1:2380" ], "clientURLs": [ "http://niub-etcd-1:2379" ] }, { "id": "b952ccccefdd8a93", "name": "niub3", "peerURLs": [ "http://niub-etcd-3:2380" ], "clientURLs": [ "http://niub-etcd-3:2379" ] }, { "id": "d6dbdb24d5bfc20f", "name": "niub2", "peerURLs": [ "http://niub-etcd-2:2380" ], "clientURLs": [ "http://niub-etcd-2:2379" ] } ] }
etcd備份
使用etcd自帶命令etcdctl
進行etc備份,腳本如下:
#!/bin/bash date_time=`date +%Y%m%d` etcdctl backup --data-dir /usr/local/etcd/niub3.etcd/ --backup-dir /niub/etcd_backup/${date_time} find /niub/etcd_backup/ -ctime +7 -exec rm -r {} \;
etcdctl操作
更新一個節點
如果你想更新一個節點的 IP(peerURLS),首先你需要知道那個節點的 ID。你可以列出所有節點,找出對應節點的 ID。
$ etcdctl member list 6e3bd23ae5f1eae0: name=node2 peerURLs=http://localhost:23802 clientURLs=http://127.0.0.1:23792 924e2e83e93f2560: name=node3 peerURLs=http://localhost:23803 clientURLs=http://127.0.0.1:23793 a8266ecf031671f3: name=node1 peerURLs=http://localhost:23801 clientURLs=http://127.0.0.1:23791
在本例中,我們假設要更新 ID 為 a8266ecf031671f3
的節點的 peerURLs 為:http://10.0.1.10:2380
$ etcdctl member update a8266ecf031671f3 http://10.0.1.10:2380 Updated member with ID a8266ecf031671f3 in cluster
刪除一個節點
假設我們要刪除 ID 為 a8266ecf031671f3
的節點
$ etcdctl member remove a8266ecf031671f3
Removed member a8266ecf031671f3 from cluster
執行完后,目標節點會自動停止服務,並且打印一行日志:
etcd: this member has been permanently removed from the cluster. Exiting.
如果刪除的是 leader
節點,則需要耗費額外的時間重新選舉 leader
。
增加一個新的節點
增加一個新的節點分為兩步:
-
通過
etcdctl
或對應的 API 注冊新節點 -
使用恰當的參數啟動新節點
先看第一步,假設我們要新加的節點取名為 infra3, peerURLs
是 http://10.0.1.13:2380
$ etcdctl member add infra3 http://10.0.1.13:2380 added member 9bf1b35fc7761a23 to cluster ETCD_NAME="infra3" ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380" ETCD_INITIAL_CLUSTER_STATE=existing
etcdctl
在注冊完新節點后,會返回一段提示,包含3個環境變量。然后在第二部啟動新節點的時候,帶上這3個環境變量即可。
$ export ETCD_NAME="infra3" $ export ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380" $ export ETCD_INITIAL_CLUSTER_STATE=existing $ etcd -listen-client-urls http://10.0.1.13:2379 -advertise-client-urls http://10.0.1.13:2379 -listen-peer-urls http://10.0.1.13:2380 -initial-advertise-peer-urls http://10.0.1.13:2380 -data-dir %data_dir%
這樣,新節點就會運行起來並且加入到已有的集群中了。
值得注意的是,如果原先的集群只有1個節點,在新節點成功啟動之前,新集群並不能正確的形成。因為原先的單節點集群無法完成leader
的選舉。
直到新節點啟動完,和原先的節點建立連接以后,新集群才能正確形成。
服務故障恢復
在使用etcd集群的過程中,有時會出現少量主機故障,這時我們需要對集群進行維護。然而,在現實情況下,還可能遇到由於嚴重的設備 或網絡的故障,導致超過半數的節點無法正常工作。
在etcd集群無法提供正常的服務,我們需要用到一些備份和數據恢復的手段。etcd背后的raft,保證了集群的數據的一致性與穩定性。所以我們對etcd的恢復,更多的是恢復etcd的節點服務,並還原用戶數據。
首先,從剩余的正常節點中選擇一個正常的成員節點, 使用 etcdctl backup
命令備份etcd數據。
$ ./etcdctl backup --data-dir /var/lib/etcd -backup-dir /tmp/etcd_backup
$ tar -zcxf backup.etcd.tar.gz /tmp/etcd_backup
這個命令會將節點中的用戶數據全部寫入到指定的備份目錄中,但是節點ID,集群ID等信息將會丟失, 並在恢復到目的節點時被重新。這樣主要是防止原先的節點意外重新加入新的節點集群而導致數據混亂。
然后將Etcd數據恢復到新的集群的任意一個節點上, 使用 --force-new-cluster
參數啟動Etcd服務。這個參數會重置集群ID和集群的所有成員信息,其中節點的監聽地址會被重置為localhost:2379, 表示集群中只有一個節點。
$ tar -zxvf backup.etcd.tar.gz -C /var/lib/etcd
$ etcd --data-dir=/var/lib/etcd --force-new-cluster ...
啟動完成單節點的etcd,可以先對數據的完整性進行驗證, 確認無誤后再通過Etcd API修改節點的監聽地址,讓它監聽節點的外部IP地址,為增加其他節點做准備。例如:
用etcd命令找到當前節點的ID。
$ etcdctl member list
98f0c6bf64240842: name=cd-2 peerURLs=http://127.0.0.1:2580 clientURLs=http://127.0.0.1:2579
由於etcdctl不具備修改成員節點參數的功能, 下面的操作要使用API來完成。
$ curl http://127.0.0.1:2579/v2/members/98f0c6bf64240842 -XPUT \
-H "Content-Type:application/json" -d '{"peerURLs":["http://127.0.0.1:2580"]}'
注意,在Etcd文檔中, 建議首先將集群恢復到一個臨時的目錄中,從臨時目錄啟動etcd,驗證新的數據正確完整后,停止etcd,在將數據恢復到正常的目錄中。
最后,在完成第一個成員節點的啟動后,可以通過集群擴展的方法使用 etcdctl member add
命令添加其他成員節點進來。
擴展etcd集群
在集群中的任何一台etcd節點上執行命令,將新節點注冊到集群:
1
|
curl http:
//127
.0.0.1:2379
/v2/members
-XPOST -H
"Content-Type: application/json"
-d
'{"peerURLs": ["http://192.168.73.172:2380"]}'
|
在新節點上啟動etcd容器,注意-initial-cluster-state參數為existing
1
2
3
4
5
6
7
8
9
10
11
|
-name etcd03 \
-advertise-client-urls http:
//192
.168.73.150:2379,http:
//192
.168.73.150:4001 \
-listen-client-urls http:
//0
.0.0.0:2379 \
-initial-advertise-peer-urls http:
//192
.168.73.150:2380 \
-listen-peer-urls http:
//0
.0.0.0:2380 \
-initial-cluster-token etcd-cluster \
-initial-cluster
"etcd01=http://192.168.73.140:2380,etcd02=http://192.168.73.137:2380,etcd03=http://192.168.73.150:2380"
\
-initial-cluster-state existing
|
任意節點執行健康檢查:
1
2
3
4
|
[root@docker01 ~]
# etcdctl cluster-health
member 2bd5fcc327f74dd5 is healthy: got healthy result from http:
//192
.168.73.140:2379
member c8a9cac165026b12 is healthy: got healthy result from http:
//192
.168.73.137:2379
cluster is healthy
|
Etcd數據遷移
數據遷移
在 gzns-inf-platform53.gzns.baidu.com 機器上運行着一個 etcd 服務器,其 data-dir 為 /var/lib/etcd/。我們要以 /var/lib/etcd 中的數據為基礎,搭建一個包含三個節點的高可用的 etcd 集群,三個節點的主機名分別為:
gzns-inf-platform53.gzns.baidu.com
gzns-inf-platform56.gzns.baidu.com
gzns-inf-platform60.gzns.baidu.com
初始化一個新的集群
我們先分別在上述三個節點上創建 /home/work/etcd/data-dir/ 文件夾當作 etcd 集群每個節點的數據存放目錄。然后以 gzns-inf-platform60.gzns.baidu.com 節點為起點創建一個單節點的 etcd 集群,啟動腳本 force-start-etcd.sh 如下:
#!/bin/bash # Don't start it unless etcd cluster has a heavily crash ! ../bin/etcd --name etcd2 --data-dir /home/work/etcd/data-dir --advertise-client-urls
http://gzns-inf-platform60.gzns.baidu.com:2379,http://gzns-inf-platform60.gzns.baidu.com:4001
--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 --initial-advertise-peer-urls
http://gzns-inf-platform60.gzns.baidu.com:2380 --listen-peer-urls http://0.0.0.0:2380
--initial-cluster-token etcd-cluster-1 --initial-cluster etcd2=http://gzns-inf-platform60.gzns.baidu.com:2380
--force-new-cluster > ./log/etcd.log 2>&1
這一步的 --force-new-cluster 很重要,可能是為了抹除舊 etcd 的一些屬性信息,從而能成功的創建一個單節點 etcd 的集群。
這時候通過
etcdctl member list
查看 peerURLs 指向的是不是 http://gzns-inf-platform60.gzns.baidu.com:2380
? 如果不是,需要更新這個 etcd 的 peerURLs 的指向,否則這樣在加入新的節點時會失敗的。
我們手動更新這個 etcd 的 peerURLs 指向
etcdctl member update ce2a822cea30bfca http://gzns-inf-platform60.gzns.baidu.com:2380
添加etcd1成員
然后添加 gzns-inf-platform56.gzns.baidu.com 節點上的 etcd1 成員
etcdctl member add etcd1 http://gzns-inf-platform56.gzns.baidu.com:2380
注意要先添加 etcd1 成員后,再在 gzns-inf-platform56.gzns 機器上啟動這個 etcd1 成員
這時候我們登陸上 gzns-inf-platform56.gzns.baidu.com 機器上啟動這個 etcd1 實例,啟動腳本 force-start-etcd.sh 如下:
#!/bin/bash # Don't start it unless etcd cluster has a heavily crash ! ../bin/etcd --name etcd1 --data-dir /home/work/etcd/data-dir --advertise-client-urls http://gzns-inf-platform56.gzns.baidu.com:2379,http://gzns-inf-platform56.gzns.baidu.com:4001
--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 --initial-advertise-peer-urls http://gzns-inf-platform56.gzns.baidu.com:2380
--listen-peer-urls http://0.0.0.0:2380 --initial-cluster-token etcd-cluster-1 --initial-cluster etcd2=http://gzns-inf-platform60.gzns.baidu.com:2380,
etcd1=http://gzns-inf-platform56.gzns.baidu.com:2380 --initial-cluster-state existing > ./log/etcd.log 2>&1
注意在這個節點上我們先把 data-dir 文件夾中的數據刪除(如果有內容的情況下),然后設置 --initial-cluster和 --initial-cluster-state。
添加 etcd0 成員
這時候我們可以通過
etcdctl member list
觀察到我們新加入的節點了,然后我們再以類似的步驟添加第三個節點 gzns-inf-platform53.gzns.baidu.com上 的 etcd0 實例
etcdctl member add etcd0 http://gzns-inf-platform53.gzns.baidu.com:2380
然后登陸到 gzns-inf-platform53.gzns.baidu.com 機器上啟動 etcd0 這個實例,啟動腳本 force-start-etcd.sh 如下:
#!/bin/bash # Don't start it unless etcd cluster has a heavily crash ! ../bin/etcd --name etcd0 --data-dir /home/work/etcd/data-dir --advertise-client-urls http://gzns-inf-platform53.gzns.baidu.com:2379,http://gzns-inf-platform53.gzns.baidu.com:4001
--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 --initial-advertise-peer-urls http://gzns-inf-platform53.gzns.baidu.com:2380 --listen-peer-urls http://0.0.0.0:2380
--initial-cluster-token etcd-cluster-1 --initial-cluster etcd2=http://gzns-inf-platform60.gzns.baidu.com:2380,etcd1=http://gzns-inf-platform56.gzns.baidu.com:2380,
etcd0=http://gzns-inf-platform53.gzns.baidu.com:2380 --initial-cluster-state existing > ./log/etcd.log 2>&1
過程同加入 etcd1 的過程相似,這樣我們就可以把單節點的 etcd 數據遷移到一個包含三個 etcd 實例組成的集群上了。
大體思路
先通過 --force-new-cluster 強行拉起一個 etcd 集群,抹除了原有 data-dir 中原有集群的屬性信息(內部猜測),然后通過加入新成員的方式擴展這個集群到指定的數目。
高可用etcd集群方式(可選擇)
上面數據遷移的過程一般是在緊急的狀態下才會進行的操作,這時候可能 etcd 已經停掉了,或者節點不可用了。在一般情況下如何搭建一個高可用的 etcd 集群呢,目前采用的方法是用 supervise 來監控每個節點的 etcd 進程。
在數據遷移的過程中,我們已經搭建好了一個包含三個節點的 etcd 集群了,這時候我們對其做一些改變,使用supervise 重新拉起這些進程。
首先登陸到 gzns-inf-platform60.gzns.baidu.com 節點上,kill 掉 etcd 進程,編寫 etcd 的啟動腳本 start-etcd.sh,其中 start-etcd.sh 的內容如下:
#!/bin/bash
../bin/etcd --name etcd2 --data-dir /home/work/etcd/data-dir --advertise-client-urls http://gzns-inf-platform60.gzns.baidu.com:2379,http://gzns-inf-platform60.gzns.baidu.com:4001
--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 --initial-advertise-peer-urls http://gzns-inf-platform60.gzns.baidu.com:2380 --listen-peer-urls http://0.0.0.0:2380
--initial-cluster-token etcd-cluster-1 --initial-cluster etcd2=http://gzns-inf-platform60.gzns.baidu.com:2380,etcd1=http://gzns-inf-platform56.gzns.baidu.com:2380,
etcd0=http://gzns-inf-platform53.gzns.baidu.com:2380 --initial-cluster-state existing
> ./log/etcd.log 2>&1
然后使用 supervise 執行 start-etcd.sh 這個腳本,使用 supervise 啟動 start-etcd.sh 的啟動腳本 etcd_control 如下:
#!/bin/sh if [ $# -ne 1 ]; then echo "$0: start|stop" fi work_path=`dirname $0` cd ${work_path} work_path=`pwd` supervise=${work_path}/supervise/bin/supervise64.etcd mkdir -p ${work_path}/supervise/status/etcd case "$1" in start) killall etcd supervise64.etcd ${supervise} -f "sh ./start-etcd.sh" \ -F ${work_path}/supervise/conf/supervise.conf \ -p ${work_path}/supervise/status/etcd echo "START etcd daemon ok!" ;; stop) killall etcd supervise64.etcd if [ $? -ne 0 ] then echo "STOP etcd daemon failed!" exit 1 fi echo "STOP etcd daemon ok!"
這里為什么不直接用 supervise 執行 etcd 這個命令呢,反而以一個 start-etcd.sh 腳本的形式啟動這個 etcd 呢?原因在於我們需要將 etcd 的輸出信息重定向到文件中,
如果直接在 supervise 的 command 進行重定向,將發生錯誤。
分別登陸到以下兩台機器
- gzns-inf-platform56.gzns.baidu.com
- gzns-inf-platform53.gzns.baidu.com
上進行同樣的操作,注意要針對每個節點的不同修改對應的etcd name 和 peerURLs 等。
常見問題
1、etcd 讀取已有的 data-dir 數據而啟動失敗,常常表現為cluster id not match什么的
可能原因是新啟動的 etcd 屬性與之前的不同,可以嘗 --force-new-cluster 選項的形式啟動一個新的集群
2、etcd 集群搭建完成后,通過 kubectl get pods 等一些操作發生錯誤的情況
目前解決辦法是重啟一下 apiserver 進程
3、還是 etcd啟動失敗的錯誤,大多數情況下都是與data-dir 有關系,data-dir 中記錄的信息與 etcd啟動的選項所標識的信息不太匹配造成的
如果能通過修改啟動參數解決這類錯誤就最好不過的了,非常情況下的解決辦法:
- 一種解決辦法是刪除data-dir文件
- 一種方法是復制其他節點的data-dir中的內容,以此為基礎上以 --force-new-cluster 的形式強行拉起一個,然后以添加新成員的方式恢復這個集群,這是目前的幾種解決辦法