用Kubernetes搭建Etcd集群和WebUI
今天用這篇文章帶大家在自己的電腦上搭建一個Kubernetes Etcd集群,Kubernetes本身的功能就依賴Etcd實現(xiàn),不過并不會開放給我們的程序使用,所以需要自己單獨搭建。
Etcd現(xiàn)在是分布式服務(wù)架構(gòu)中的重要組件,它由 CNCF 孵化托管, 在微服務(wù)和 Kubernates 集群中不僅可以作為服務(wù)注冊與發(fā)現(xiàn),還是一個用于配置共享的分布式鍵值存儲,采用 raft 算法,實現(xiàn)分布式系統(tǒng)數(shù)據(jù)的可用性和一致性。
一般用Go語言開發(fā)的gRPC服務(wù)會使用Etcd實現(xiàn)服務(wù)發(fā)現(xiàn)和注冊。此外一些重要的配置也會存儲在Etcd里通過讓程序監(jiān)聽Key的變更來實現(xiàn)無需重啟應(yīng)用的配置更新。
關(guān)于為什么要使用Etcd我們不做過多介紹,現(xiàn)在切入正題。安裝Etcd的方式比較多,如果想直接把Etcd集群安裝在機器上而不是Kubernetes里可以通過 goreman 工具。不過因為我電腦上安裝了Minikube,所以想盡量把所有東西都運行在Kubernetes里這樣未來換電腦也就不用發(fā)愁需要安裝那么多工具集了。除了演示在Kubernetes里安裝運行Etcd集群外,還會安裝一個Etcd的Web UI服務(wù),讓我們能夠通過瀏覽器查詢和設(shè)置Etcd的Key-Value,這個Etcd Web UI服務(wù)同樣是運行在Kubernetes里,相信通過今天文章的學(xué)習(xí)你也一定感受到Kubernetes的便捷和學(xué)到不少知識。
準(zhǔn)備工作
在開始跟著文章里的步驟安裝Etcd前,需要先確保自己的電腦里安裝了Minikube 這個單節(jié)點Kubernetes集群。我以前的文章里有詳細(xì)介紹過安裝步驟,我把他放在這里供大家參考。
Kubernetes 安裝Etcd
在Kubernetes里安裝Etcd的方法有兩種,一種是原始的通過StatefulSet控制器,也就是有狀態(tài)應(yīng)用來編排Etcd的節(jié)點,這種需要配置Pod使用的鏡像,配置文件和啟動命令。通過無頭服務(wù),在集群內(nèi)部為Pod提供名稱到IP的映射,以及NodePort類型的服務(wù)向集群外暴露客戶端端口。還有一種是使用coreos提供Etcd Operator直接安裝,很多細(xì)節(jié)都為我們直接處理好了。
在這里我們使用第一種用StatefulSet創(chuàng)建Etcd節(jié)點和Service對外暴露客戶端端口的安裝方式。
Service 設(shè)置DNS和暴露端口
首先我們來創(chuàng)建為Etcd集群的Pod提供Pod名稱到IP映射的無頭服務(wù)。
---
apiVersion: v1
kind: Service
metadata:
name: etcd
namespace: etcd
annotations:
# Create endpoints also if the related pod isn't ready
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
spec:
ports:
- port: 2379
name: client
- port: 2380
name: peer
clusterIP: None
selector:
component: etcd
這里要創(chuàng)建的無頭服務(wù)的名稱是 etcd 。因為StatefulSet編排的Pod名稱永遠(yuǎn)是PodName-序號,到時候集群內(nèi)部各個Etcd節(jié)點配置的通信方式就可以用 "PodName-序號.etcd:2380" 這種形式來代替使用"節(jié)點IP:2380"。
2380是Etcd服務(wù)端的端口,而對外提供服務(wù)的客戶端端口是2379,因此還需要有一個NodePort類型的Service向集群外部暴露客戶端對2379端口的訪問。
---
apiVersion: v1
kind: Service
metadata:
name: etcd-client
namespace: etcd
spec:
ports:
- name: http
nodePort: 30453
port: 2379
targetPort: 2379
protocol: TCP
type: NodePort
selector:
component: etcd
創(chuàng)建這兩個Service:kubectl apply -f resources/services.yml -n etcd
關(guān)于Kubernetes Service 和 StatefulSet 的作用原理、各種配置的詳細(xì)解釋可以參考我公眾號里以前的文章:
Kubernetes Service學(xué)習(xí)筆記和實踐練習(xí)
深入理解StatefulSet,用Kubernetes編排有狀態(tài)應(yīng)用
配置Etcd節(jié)點Pod
我們通過StatefulSet編排創(chuàng)建3個Etcd節(jié)點的Pod,創(chuàng)建出來后上面的那兩個Service會根據(jù)Pod的標(biāo)簽component=etcd找到它們,把節(jié)點加入到自己的服務(wù)端點列表中。
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
labels:
component: etcd
spec:
serviceName: etcd
replicas: 3
selector:
matchLabels:
component: etcd
template:
metadata:
name: etcd
labels:
component: etcd
spec:
volumes:
- name: etcd-storage
emptyDir: {}
containers:
- name: etcd
image: quay.io/coreos/etcd:latest
ports:
- containerPort: 2379
name: client
- containerPort: 2380
name: peer
volumeMounts:
- name: etcd-storage
mountPath: /var/run/etcd/default.etcd
Pod的啟動命令里要配置上每個節(jié)點點的IP和端口,上面說了無頭服務(wù)的配置可以通過PodName-序號.etcd 的方式解析出IP,這里對應(yīng)的就是:
etcd-0.etcd
etcd-1.etcd
etcd-2.etcd
不過為了靈活性,我參考了國外一位網(wǎng)友分享的方式通過啟動時執(zhí)行shell腳本的方式,動態(tài)根據(jù)Etcd集群節(jié)點數(shù)量來設(shè)置啟動命令里的 PeersUrl等配置
env:
- name: CLUSTER_SIZE
value: "3"
- name: SET_NAME
value: "etcd"
- name: MINIKUBE_IP
value: "$MINIKUBE_IP"
- name: MINIKUBE_PORT
value: "$MINIKUBE_PORT"
command:
- /bin/sh
- -ecx
- |
IP=$(hostname -i)
PEERS=""
for i in $(seq 0 $((${CLUSTER_SIZE} - 1))); do
PEERS="${PEERS}${PEERS:+,}${SET_NAME}-${i}=http://${SET_NAME}-${i}.${SET_NAME}:2380"
done
exec etcd --name ${HOSTNAME} \
--listen-peer-urls http://${IP}:2380 \
--listen-client-urls http://${IP}:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://${HOSTNAME}.${SET_NAME}:2379,http://${MINIKUBE_IP}:${MINIKUBE_PORT} \
--initial-advertise-peer-urls http://${HOSTNAME}.${SET_NAME}:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster ${PEERS} \
--initial-cluster-state new \
--data-dir /var/run/etcd/default.etcd
StatefulSet的創(chuàng)建命令為:
cat resources/etcd.yml.tmpl | resources/config.bash | kubectl apply -n etcd -f -提供了一個shell腳本來設(shè)置MINIKUBE_PORT 這兩個在容器里無法獲得的環(huán)境變量。
創(chuàng)建Etcd集群所使用的 yaml 資源聲明文件和具體的操作步驟都已經(jīng)放到了Github上,大家可以按照里面的命令進(jìn)行操作。鏈接地址:https://github.com/kevinyan815/LearningKubernetes/tree/master/etcd
測試安裝成果
測試安裝是否成功也簡單,觀測Pod在Kubernetes里都正常啟動起來后我們往Etcd里 set 一個鍵值,看能不能再查詢出來就行了。

接下來我們在說說怎么給Etcd做個Web UI,畢竟一些為應(yīng)用程序預(yù)準(zhǔn)備的配置如果要用命令行一條條 set 進(jìn)去的話也太麻煩了。
給Etcd集群做個Web UI
做這個Web UI,說來話長,花的時間比上面搭建Etcd集群多多了,大概花了兩三天反復(fù)嘗試才搞定。
Web UI我使用了 e3w 這個項目
項目地址:https://github.com/soyking/e3w
原本這個項目是有提供docker鏡像和docker-compose容器編排運行的,不過我實在不想再在我電腦上安裝這么多工具集了就一直想把它改造成能在 Kubernetes 里運行的方式。
看了下這個項目的源碼,啟動的時候會去讀取 /app/conf 目錄下的config.default.ini 配置文件,WebUI服務(wù)的端口號默認(rèn)配置的是8080,有了這兩個信息后我們就可以通過Deployment創(chuàng)建Pod來放置Web UI服務(wù),通過Service暴露Web UI服務(wù)供集群外部訪問的端口了。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: e3w-deployment
namespace: etcd
labels:
app: e3w
spec:
replicas: 1
selector:
matchLabels:
app: etcd-client-e3w
template:
metadata:
labels:
app: etcd-client-e3w
spec:
containers:
- name: e3w-app-container
image: soyking/e3w:latest
ports:
- name: e3w-server-port
containerPort: 8080
---
kind: Service
apiVersion: v1
metadata:
name: e3w-service
namespace: etcd
spec:
type: NodePort
selector:
app: etcd-client-e3w
ports:
- protocol: TCP
targetPort: e3w-server-port
nodePort: 30081
port: 80
至于配置文件,我的設(shè)想是把配置放到ConfigMap里,再把ConfigMap里的配置項作為文件掛載在原來配置文件的路徑上。這樣做的好處有兩個:
不需要單獨在機器上創(chuàng)建一個具體路徑的配置文件,減少了Pod對宿主機上hostPath的依賴。 不需要按照e3w項目里推薦的自定義方式那樣修改項目里的配置文件重新打Docker鏡像才能連接上自己的Etcd集群。
下面 ConfigMap 里的 e3w-config.default.ini 就是我們要作為文件掛載到容器里的配置項。
apiVersion: v1
kind: ConfigMap
metadata:
name: e3w-configmap
namespace: etcd
labels:
config: e3w.ini
data:
e3w-config.default.ini: |
[app]
port=8080
auth=false
[etcd]
root_key=root
dir_value=
addr=etcd-0.etcd.etcd.svc.cluster.local:2379,etcd-1.etcd.etcd.svc.cluster.local:2379,etcd-2.etcd.etcd.svc.cluster.local:2379
username=
password=
cert_file=
key_file=
ca_file=
不過,在這一步上實屬費了不少時間,主要是把 ConfigMap 的一個配置項作為文件掛載到容器里除了需要在 volumeMounts.MountPath 上配置完整的目錄和文件名外還需要用上 subPath 這個配置。
spec:
containers:
volumeMounts:
- name: e3w-configmap-volume
mountPath: /app/conf/config.default.ini
subPath: config.default.ini
volumes:
- name: e3w-configmap-volume
configMap:
name: e3w-configmap
items:
- key: e3w-config.default.ini
path: config.default.ini
這又是一個小知識點,關(guān)于Volume掛載時 subPath 的應(yīng)用場景等后面再說(大家想聽的話下篇就安排,記得點贊啊)。
配置文件搞定后,再看一下Pod運行的狀態(tài),就不再是Error了。

上面創(chuàng)建的Etcd集群里的三個基點和e3w的WebUI服務(wù)都能正常運行。
通過WebUI我們可以查看Etcd集群的運行狀態(tài)

以及更方便地通過UI界面管理Key-Value:

整體上感覺這個Web UI服務(wù)體驗上還不錯,很多功能都有。
Etcd Web UI服務(wù)的 yaml 定義文件我也放到了GitHub上,鏈接地址:https://github.com/kevinyan815/LearningKubernetes/tree/master/e3w
總結(jié)
今天的文章,感覺前面安裝Etcd集群的內(nèi)容沒有什么新鮮的知識,都是以前講過的知識點的實際應(yīng)用。在介紹安裝Etcd Web UI服務(wù)時倒是用到了兩個新的知識點,我們通過將 configMap 的某一個配置項作為配置文件掛載到容器里的方式既避免了修改 e3w 項目源代代碼重新打Docker鏡像,也避免了在宿主機上單獨管理配置文件的麻煩。
推薦閱讀
