1. 程式人生 > >etcd集群部署與遇到的坑(轉)

etcd集群部署與遇到的坑(轉)

集群搭建 ESS 參數 nbsp get 存儲目錄 有時 snapshot 添加

在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
/usr/local/etcd/etcd \
-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 的形式強行拉起一個,然後以添加新成員的方式恢復這個集群,這是目前的幾種解決辦法

etcd集群部署與遇到的坑(轉)