徹底搞懂 etcd 系列文章(三):etcd 集羣運維部署

0 專輯概述

etcd 是雲原生架構中重要的基礎組件,由 CNCF 孵化託管。etcd 在微服務和 Kubernates 集羣中不僅可以作爲服務註冊與發現,還可以作爲 key-value 存儲的中間件。

《徹底搞懂 etcd 系列文章》將會從 etcd 的基本功能實踐、API 接口、實現原理、源碼分析,以及實現中的踩坑經驗等幾方面具體展開介紹 etcd。預計會有 20 篇左右的文章,筆者將會每週持續更新,歡迎關注。

1 etcd 集羣部署

在生產環境中,爲了整個集羣的高可用,etcd 正常都會集羣部署,避免單點故障。本節將會介紹如何進行 etcd 集羣部署。引導 etcd 集羣的啓動有以下三種機制:

  • 靜態
  • etcd 動態發現
  • DNS 發現

靜態啓動 etcd 集羣要求每個成員都知道集羣中的另一個成員。 在許多情況下,羣集成員的 IP 可能會提前未知。在這些情況下,可以在發現服務的幫助下引導 etcd 羣集。

下面我們將會分別介紹這幾種方式。

2 靜態方式啓動 etcd 集羣

單機安裝

如果想要在一臺機器上實踐 etcd 集羣的搭建,可以通過 goreman 工具。

goreman 是一個 Go 語言編寫的多進程管理工具,是對 Ruby 下廣泛使用的 foreman 的重寫(foreman 原作者也實現了一個 Go 版本:forego,不過沒有 goreman 好用)。

我們需要確認 Go 安裝環境,然後直接執行:

go get github.com/mattn/goreman

編譯後的文件放在 $GOPATH/bin 中,$GOPATH/bin目錄已經添加到了系統 $PATH 中,所以我們可以方便執行命令 goreman 命令。下面就是編寫 Procfile 腳本,我們啓動三個 etcd,具體對應如下:

HostName ip 客戶端交互端口 peer 通信端口
infra1 127.0.0.1 12379 12380
infra2 127.0.0.1 22379 22380
infra3 127.0.0.1 32379 32380

Procfile 腳本如下:

etcd1: etcd --name infra1 --listen-client-urls http://127.0.0.1:12379 --advertise-client-urls http://127.0.0.1:12379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd2: etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd3: etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr

配置項說明:

  • --name:etcd集羣中的節點名,這裏可以隨意,可區分且不重複就行
  • --listen-peer-urls:監聽的用於節點之間通信的url,可監聽多個,集羣內部將通過這些url進行數據交互(如選舉,數據同步等)
  • --initial-advertise-peer-urls:建議用於節點之間通信的url,節點間將以該值進行通信。
  • --listen-client-urls:監聽的用於客戶端通信的url,同樣可以監聽多個。
  • --advertise-client-urls:建議使用的客戶端通信 url,該值用於 etcd 代理或 etcd 成員與 etcd 節點通信。
  • --initial-cluster-token: etcd-cluster-1,節點的 token 值,設置該值後集羣將生成唯一 id,併爲每個節點也生成唯一 id,當使用相同配置文件再啓動一個集羣時,只要該 token 值不一樣,etcd 集羣就不會相互影響。
  • --initial-cluster:也就是集羣中所有的 initial-advertise-peer-urls 的合集。
  • --initial-cluster-state:new,新建集羣的標誌

注意上面的腳本,etcd 命令執行時需要根據本地實際的安裝地址進行配置。下面我們啓動 etcd 集羣。

goreman -f /opt/procfile start

使用如上的命令啓動啓動 etcd 集羣,啓動完成之後查看集羣內的成員。

$ etcdctl --endpoints=http://localhost:22379  member list

8211f1d0f64f3269, started, infra1, http://127.0.0.1:12380, http://127.0.0.1:12379, false
91bc3c398fb3c146, started, infra2, http://127.0.0.1:22380, http://127.0.0.1:22379, false
fd422379fda50e48, started, infra3, http://127.0.0.1:32380, http://127.0.0.1:32379, false

我們在單機搭建的僞集羣成功,需要注意的是在集羣啓動時,我們是通過靜態的方式指定集羣的成員,在實際環境中,集羣成員的 ip 可能不會提前知道。這時候就需要採用動態發現的機制。

docker 啓動集羣

etcd 使用 gcr.io/etcd-development/etcd 作爲容器的主要加速器, quay.io/coreos/etcd 作爲輔助的加速器。可惜這兩個加速器我們都沒法訪問,如果下載不了,可以使用筆者提供的地址:

docker pull bitnami/etcd:3.4.7

然後將拉取的鏡像重新 tag:

docker image tag bitnami/etcd:3.4.7 quay.io/coreos/etcd:3.4.7

鏡像設置好之後,我們啓動 3 個節點的 etcd 集羣,腳本命令如下:

REGISTRY=quay.io/coreos/etcd

# For each machine
ETCD_VERSION=3.4.7
TOKEN=my-etcd-token
CLUSTER_STATE=new
NAME_1=etcd-node-0
NAME_2=etcd-node-1
NAME_3=etcd-node-2
HOST_1= 192.168.202.128
HOST_2= 192.168.202.129
HOST_3= 192.168.202.130
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
DATA_DIR=/var/lib/etcd

# For node 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

注意,上面的腳本是部署在三臺機器上面,每臺機器執行對應的腳本即可。在運行時可以指定 API 版本:

docker exec etcd /bin/sh -c "export ETCDCTL_API=3 && /usr/local/bin/etcdctl put foo bar"

docker 的安裝方式比較簡單,讀者根據需要可以定製一些配置。

3 動態發現啓動 etcd 集羣

如前面所述,在實際環境中,集羣成員的 ip 可能不會提前知道。在這種情況下,需要使用自動發現來引導 etcd 集羣,而不是指定靜態配置,這個過程被稱爲發現。我們啓動三個 etcd,具體對應如下:

HostName ip 客戶端交互端口 peer 通信端口
etcd1 192.168.202.128 2379 2380
etcd2 192.168.202.129 2379 2380
etcd3 192.168.202.130 2379 2380

協議的原理

Discovery service protocol 幫助新的 etcd 成員使用共享 URL 在集羣引導階段發現所有其他成員。

該協議使用新的發現令牌來引導一個唯一的 etcd 集羣。一個發現令牌只能代表一個 etcd 集羣。只要此令牌上的發現協議啓動,即使它中途失敗,也不能用於引導另一個 etcd 集羣。

提示:Discovery service protocol 僅用於集羣引導階段,不能用於運行時重新配置或集羣監視。

Discovery protocol 使用內部 etcd 集羣來協調新集羣的引導程序。首先,所有新成員都與發現服務交互,並幫助生成預期的成員列表。之後,每個新成員使用此列表引導其服務器,該列表執行與 --initial-cluster 標誌相同的功能,即設置所有集羣的成員信息。

獲取 discovery 的 token

生成將標識新集羣的唯一令牌。 在以下步驟中,它將用作發現鍵空間中的唯一前綴。 一種簡單的方法是使用uuidgen:

UUID=$(uuidgen)

指定集羣的大小

獲取令牌時,必須指定羣集大小。 發現服務使用該大小來了解何時發現了最初將組成集羣的所有成員。

curl -X PUT http://10.0.10.10:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3

我們需要把該 url 地址 http://10.0.10.10:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 作爲 --discovery 參數來啓動 etcd。

節點會自動使用 http://10.0.10.10:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 目錄進行 etcd 的註冊和發現服務。

公共發現服務

當我們本地沒有可用的 etcd 集羣,etcd 官網提供了一個可以公網訪問的 etcd 存儲地址。我們可以通過如下命令得到 etcd 服務的目錄,並把它作爲 --discovery 參數使用。

公共發現服務 discovery.etcd.io 以相同的方式工作,但是有一層修飾,可以提取醜陋的 URL,自動生成 UUID,並提供針對過多請求的保護。公共發現服務在其上仍然使用 etcd 羣集作爲數據存儲。

$ curl http://discovery.etcd.io/new?size=3

http://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

以動態發現方式啓動集羣

etcd 發現模式下,啓動 etcd 的命令如下:

# etcd1 啓動
$ /opt/etcd/bin/etcd  --name etcd1 --initial-advertise-peer-urls http://192.168.202.128:2380 \
  --listen-peer-urls http://192.168.202.128:2380 \
  --data-dir /opt/etcd/data \
  --listen-client-urls http://192.168.202.128:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.202.128:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

# etcd2 啓動
 /opt/etcd/bin/etcd  --name etcd2 --initial-advertise-peer-urls http://192.168.202.129:2380 \
  --listen-peer-urls http://192.168.202.129:2380 \
  --data-dir /opt/etcd/data \
  --listen-client-urls http://192.168.202.129:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.202.129:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

# etcd3 啓動
 /opt/etcd/bin/etcd  --name etcd3 --initial-advertise-peer-urls http://192.168.202.130:2380 \
    --listen-peer-urls http://192.168.202.130:2380 \
    --data-dir /opt/etcd/data \
    --listen-client-urls http://192.168.202.130:2379,http://127.0.0.1:2379 \
    --advertise-client-urls http://192.168.202.130:2379 \
    --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

需要注意的是,在我們完成了集羣的初始化後,這些信息就失去了作用。當需要增加節點時,需要使用 etcdctl 進行操作。爲了安全,每次啓動新 etcd 集羣時,都使用新的 discovery token 進行註冊。另外,如果初始化時啓動的節點超過了指定的數量,多餘的節點會自動轉化爲 Proxy 模式的 etcd。

結果驗證

集羣啓動好之後,進行驗證,我們看一下集羣的成員:

$ /opt/etcd/bin/etcdctl member list
# 結果如下
    40e2ac06ca1674a7, started, etcd3, http://192.168.202.130:2380, http://192.168.202.130:2379, false
    c532c5cedfe84d3c, started, etcd1, http://192.168.202.128:2380, http://192.168.202.128:2379, false
    db75d3022049742a, started, etcd2, http://192.168.202.129:2380, http://192.168.202.129:2379, false

結果符合預期,再看下節點的健康狀態:

$ /opt/etcd/bin/etcdctl  --endpoints="http://192.168.202.128:2379,http://192.168.202.129:2379,http://192.168.202.130:2379"  endpoint  health
# 結果如下
    http://192.168.202.128:2379 is healthy: successfully committed proposal: took = 3.157068ms
    http://192.168.202.130:2379 is healthy: successfully committed proposal: took = 3.300984ms
    http://192.168.202.129:2379 is healthy: successfully committed proposal: took = 3.263923ms

可以看到,集羣中的三個節點都是健康的正常狀態。以動態發現方式啓動集羣成功。

4 DNS自發現模式

etcd 還支持使用 DNS SRV 記錄進行啓動。實際上是利用 DNS 的 SRV 記錄不斷輪訓查詢實現。DNS SRV 是 DNS 數據庫中支持的一種資源記錄的類型,它記錄了計算機與所提供服務信息的對應關係。

4.1 Dnsmasq 安裝

這裏使用 Dnsmasq 創建 DNS 服務。Dnsmasq 提供 DNS 緩存和 DHCP 服務、Tftp 服務功能。作爲域名解析服務器,Dnsmasq 可以通過緩存 DNS 請求來提高對訪問過的網址的連接速度。Dnsmasq 輕量且易配置,適用於個人用戶或少於 50 臺主機的網絡。此外它還自帶了一個 PXE 服務器。

當接受到一個 DNS 請求時,Dnsmasq 首先會查找 /etc/hosts 這個文件,然後查找 /etc/resolv.conf 中定義的外部 DNS。配置 Dnsmasq 爲 DNS 緩存服務器,同時在 /etc/hosts 文件中加入本地內網解析,這樣使得內網機器查詢時就會優先查詢 hosts 文件,這就等於將 /etc/hosts 共享給全內網機器使用,從而解決內網機器互相識別的問題。相比逐臺機器編輯 hosts 文件或者添加 Bind DNS 記錄,可以只編輯一個 hosts 文件。

基於筆者使用的 Centos 7 的主機,首先安裝 Dnsmasq:

yum install dnsmasq

安裝好之後,進行配置,所有的配置都在一個文件中完成 /etc/dnsmasq.conf。我們也可以在 /etc/dnsmasq.d 中自己寫任意名字的配置文件。

配置上游服務器地址

resolv-file 配置 Dnsmasq 額外的上游的 DNS 服務器,如果不開啓就使用 Linux 主機默認的 /etc/resolv.conf 裏的 nameserver。

$ vim /etc/dnsmasq.conf

# 增加如下的內容:
resolv-file=/etc/resolv.dnsmasq.conf
srv-host=_etcd-server._tcp.blueskykong.com,etcd1.blueskykong.com,2380,0,100
srv-host=_etcd-server._tcp.blueskykong.com,etcd2.blueskykong.com,2380,0,100
srv-host=_etcd-server._tcp.blueskykong.com,etcd3.blueskykong.com,2380,0,100

在 dnsmasq.conf 中相應的域名記錄,配置了我們所涉及的三臺服務器,分別對應 etcd1,etcd2,etcd3。

在指定文件中增加轉發 DNS 的地址
$ vim /etc/resolv.dnsmasq.conf

nameserver 8.8.8.8
nameserver 8.8.4.4

這兩個免費的 DNS服務,大家應該不陌生。讀者可以根據本地實際網絡進行配置。

本地啓用 Dnsmasq 解析
$ vim /etc/resolv.conf

nameserver 127.0.0.1

將 Dnsmasq 解析配置到本地,這很好理解。

添加解析記錄

分別爲各個域名配置相關的 A 記錄指向 etcd 核心節點對應的機器 IP。添加解析記錄有三種方式:使用系統默認 hosts、使用自定義 hosts 文件、使用自定義 conf。這裏我們使用比較簡單的第一種方式。

$ vim /etc/hosts

# 增加如下的內容解析
192.168.202.128 etcd1.blueskykong.com
192.168.202.129 etcd2.blueskykong.com
192.168.202.130 etcd3.blueskykong.com

啓動服務

service dnsmasq start

啓動好之後,我們進行驗證:

  • DNS 服務器上 SRV 記錄查詢,查詢到的結果如下:

    $ dig @192.168.202.128 +noall +answer SRV _etcd-server._tcp.blueskykong.com
    
    _etcd-server._tcp.blueskykong.com. 0 IN SRV     0 100 2380 etcd2.blueskykong.com.
    _etcd-server._tcp.blueskykong.com. 0 IN SRV     0 100 2380 etcd1.blueskykong.com.
    _etcd-server._tcp.blueskykong.com. 0 IN SRV     0 100 2380 etcd3.blueskykong.com.
    
  • 使查詢域名解析結果

    $ dig @192.168.202.128 +noall +answer etcd1.blueskykong.com etcd2.blueskykong.com etcd3.blueskykong.com
    
    etcd1.blueskykong.com.  0       IN      A       192.168.202.128
    etcd2.blueskykong.com.  0       IN      A       192.168.202.129
    etcd3.blueskykong.com.  0       IN      A       192.168.202.130
    

至此,我們已成功安裝好 Dnsmasq。下面我們基於 DNS 發現啓動 etcd 集羣。

啓動集羣

做好了上述兩步 DNS 的配置,就可以使用 DNS 啓動 etcd 集羣了。需要刪除ETCD_INITIAL_CLUSTER 配置(用於靜態服務發現),並指定 DNS SRV 域名(ETCD_DISCOVERY_SRV)。配置 DNS 解析的 url 參數爲 -discovery-srv,其中 etcd1 節點地啓動命令如下:

$ /opt/etcd/bin/etcd   --name etcd1 \
--discovery-srv blueskykong.com \
--initial-advertise-peer-urls http://etcd1.blueskykong.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--data-dir /opt/etcd/data \
--initial-cluster-state new \
--advertise-client-urls http://etcd1.blueskykong.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380

etcd 羣集成員可以使用域名或 IP 地址進行廣播,啓動的過程將解析 DNS 記錄。--initial-advertise-peer-urls 中的解析地址必須與 SRV 目標中的解析地址匹配。etcd 成員讀取解析的地址,以查找其是否屬於 SRV 記錄中定義的羣集。

我們驗證基於 DNS 發現啓動集羣的正確性,查看集羣的成員列表:

$ /opt/etcd/bin/etcdctl member list

# 結果如下:
40e2ac06ca1674a7, started, etcd3, http://192.168.202.130:2380, http://etcd3.blueskykong.com:2379, false
c532c5cedfe84d3c, started, etcd1, http://192.168.202.128:2380, http://etcd1.blueskykong.com:2379, false
db75d3022049742a, started, etcd2, http://192.168.202.129:2380, http://etcd2.blueskykong.com:2379, false

可以看到,結果輸出 etcd 集羣有三個成員,符合預期。下面我們使用 IP 地址的方式,繼續驗證集羣節點的狀態。

$ /opt/etcd/bin/etcdctl  --endpoints="http://192.168.202.128:2379,http://192.168.202.129:2379,http://192.168.202.130:2379"  endpoint  health

# 結果如下:
http://192.168.202.129:2379 is healthy: successfully committed proposal: took = 2.933555ms
http://192.168.202.128:2379 is healthy: successfully committed proposal: took = 7.252799ms
http://192.168.202.130:2379 is healthy: successfully committed proposal: took = 7.415843ms

更多的 etcd 集羣操作,讀者可以自行嘗試,筆者不在此一一展開。

5 小結

本文在上一篇文章單機安裝 etcd 的基礎上進行了補充,主要介紹了 etcd 集羣的多種安裝啓動方式:靜態單體,靜態 docker,動態發現以及 DNS 發現的啓動方式。這麼多的安裝姿勢,都是爲了我們實際的使用,下一篇我們將具體進入 etcdctl 的使用講解。

訂閱最新文章,歡迎關注我的公衆號

推薦閱讀

  1. etcd 與 Zookeeper、Consul 等其它 k-v 組件的對比
  2. 徹底搞懂 etcd 系列文章(一):初識 etcd
  3. 徹底搞懂 etcd 系列文章(二):etcd 的多種安裝姿勢

參考

etcd docs

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