1. 程式人生 > >Kubernetes高階實踐:Master高可用方案設計和踩過的那些坑

Kubernetes高階實踐:Master高可用方案設計和踩過的那些坑

今天我將為大家介紹如何構建Kubernetes Master High Availability環境。此次分享內容是我在工作中經驗總結,如果有不正確的或者需要改進的地方,歡迎各位大神指正。

Kubernetes作為容器編排管理系統,通過Scheduler、Replication Controller等元件實現了應用層的高可用,但是針對Kubernetes叢集,還需要實現Master元件的高可用。

)的實踐,但是社群的高可用方案中採用的GCE的External Loadbalancer,並未論述如何實現External Loadbalancer,而且也並沒有將Kubernetes叢集元件容器化。所以,我們的高可用方案在社群高可用方案的基礎之上進行了如下兩個方面的提升:

第一,除了kubelet之外,Kubernetes所有元件容器化;

第二,通過haproxy和keepalived構建Loadbalancer實現Master的高可用。

下面我們分四個章節來詳細論述Kubernetes Master High Availability環境的搭建。

1. HA Master整體架構

2. 核心技術點和難點

3. 實踐中的遇到的那些坑

4. 社群關於HA Master的未來發展

一、HA Master整體架構

我們已經成功將支援Master High Availability的Kubernetes叢集部署到企業私有云平臺,底層採用的是Ubuntu 14.04作業系統。下面是一個典型的部署環境:

Static Pods是由其所在節點上的kubelet直接管理,而不需要通過Apiserver來監視它們。Static Pods的資源型別只能是Pod,而且不與任何的Replication Controller相關聯,它們完全由kubelet來監視,並且當它們異常停止的時候由該kubelet負責重啟它們。

(haproxy, keepalived):這裡表示我們將haproxy和keepalived放置在同一個pod中。

1.1 kubelet對static pod高可用的支援

我們需要為kubelet程序配置一個manifests監視目錄:

  • 如果有新的yaml/manifest檔案新增到該目錄,kubelet則根據yaml/manifest檔案建立一個新的static pod;

  • 如果我們把某個yaml/manifest檔案從該目錄刪除,kubelet則會刪除由該yaml/manifest檔案所產生的static pod;

  • 如果該目錄下的yaml/manifest檔案有更新,kubelet則會刪除原來的static pod,而根據更新後的yaml/manifest檔案重新建立一個新的static pod;

  • 如果manifests目錄下的檔案沒有任何變化,但是其下某個yaml/manifest檔案所產生的static pod錯誤退出或者被誤刪後,kubelet仍然會根據該yaml/manifest檔案重新建立一個新的static pod。

這樣,kubelet在一定程度上保證了static pod的高可用。

1.2 kubelet程序的高可用

kubelet通過manifests監視目錄保證了static pod的高可用,但是如果kubelet程序本身錯誤退出或者被誤刪後,誰來負責重新啟動kubelet程序呢?

在Linux系統中,我們可以通過Monit、Upstart、Systemd、Supervisor等工具實現對服務的監控保證服務的高可用。

在Ubuntu 14.04作業系統中,我們將kubelet做成系統服務,利用Upstart來保證kubelet服務的高可用,下面是kubelet服務基於Upstart的服務啟動指令碼/etc/init/kubelet.conf:

 

其中:

  • respawn: 該命令設定服務或任務異常停止時將自動啟動。除stop命令外的停止都是異常停止。

  • respawn limit該命令設定服務或任務異常停止後重啟次數和間隔時間。

1.3Master High Availability Kubernetes整體架構圖


從架構圖中我們可以看到:

1) Upstart保證docker服務和kubelet服務的高可用,而Kubernetes的其他元件將以static pod的方式由kubelet保證高可用。

2) 兩臺lb節點通過haproxy和keepalived構建出一個External Loadbalancer,並提供VIP供客戶端訪問。

3) Haproxy配置成“SSL Termination”方式,外網client通過HTTPS請求訪問叢集,而內網client則可以通過HTTPS/HTTP請求訪問。

4) Kubernetes高可用叢集通過flannel static pod構建一個Overlay網路,使叢集中的docker容器能夠通過Kubernetes Cluster IP進行通訊。

二、核心技術點和難點

2.1 執行在特權模式的元件

Kubernetes叢集中的一些元件需要通過核心模組來為叢集提供服務,因此這些元件需要執行在特權模式下,以便能訪問相應的核心模組。

2.1.1. 開啟特權模式

為了支援docker容器在特權模式下執行,我們需要開啟Kubernetes叢集的特權模式許可權:

這裡主要體現在kubelet服務和apiserver服務。

1) Kubelet service

kubelet服務需要開啟特權模式許可權,以便允許docker容器向kubelet請求以特權模式執行。

2) Apiserver static pod

apiserver static pod需要開啟特權模式許可權,以便執行在特權模式下的docker容器能夠訪問apiserver服務。

2.1.2. 執行在特權模式下的docker容器

執行在特權模式下的docker容器,在yaml檔案中需要新增如下欄位:

這裡主要體現在kubeproxy服務、flannel服務和keepalived服務。

1) Kubeproxy static pod

kubeproxy需要通過Iptables設定防火牆規則。

2) Flannel static pod

flannel需要訪問vxlan、openvswitch等路由資料報文。

3) Keepalived static pod

keepalived需要訪問IP_VS核心模組來建立VIP。

2.2Static pod必須執行在主機網路下

如上所述的這些以static pod形式存在的Kubernetes叢集元件,必須工作在主機網路下:

雖然Overlay網路是為了讓不同節點間的docker容器進行通訊,而上述以static pod形式存在的元件也都是docker容器,但是它們之間的心跳和資訊交流都需要通過主機網路而不是類似於flannel等的Overlay網路。理由如下:

  1. 這些static pods不同於應用的pods,它們的穩定保障了Kubernetes叢集的穩定性,它們之間的心跳和資訊交流都是通過它們配置檔案中的靜態IP地址進行的,而docker/flannel網路是動態的,我們無法保證docker/flannel網路中IP地址的穩定性,同時也無法事先知道IP地址。

  2. kubeproxy、flannel、haproxy需要通過主機網路修改路由規則,從而使主機上的服務能被其他主機訪問。

  3. haproxy需要將外網請求重定向到內網後端伺服器上,也必須需要主機網路。

2.3External Loadbalancer部署要點

對於如何配置haproxy和keepalived,網路上有非常多的資源,所以這裡不在論述。下面我們來分析一下部署過程中的一些要點。

External Loadbalancer由至少兩臺lb node組成,通過haproxy和keepalived pod實現Master的負載均衡,對外提供統一的VIP。

我們可以將haproxy和keepalived分別放置在不同的pod中,也可以將它們放置在同一個pod中。考慮到keepalived需要監測haproxy的狀態,我們會把haproxy和keepalived放在一起做成一個loadbalancer pod。

2.3.1. lb node配置

1) 使能核心IPVS模組

由於keepalived需要通過IPVS模組實現路由轉發,所以我們需要使能核心IPVS模組。

從Linux核心版本2.6起,ip_vs code已經被整合進了核心中,因此,只要在編譯核心的時候選擇了ipvs的功能,Linux即能支援LVS。因此我們只需要配置作業系統啟動時自動載入IPVS模組:

 

我們可以通過如下命令檢視ip_vs模組是否成功載入:

 

如果沒有載入,我們可以通過modprobe命令載入該模組:

 

2) 修改核心引數

為了使keepalived將資料包轉發到真實的後端伺服器,每一個lb node都需要開啟IP轉發功能:

 

另外,keepalived設定的VIP有可能為非本地IP地址,所以我們還需要使能非本地IP地址繫結功能:

2.3.2. keepalived監測haproxy狀態的方法

對於普通程序來說, keepalived程序可以通過“killall -0 haproxy”命令檢測haproxy程序是否正常執行(注: Sending the signal 0 to a given PID just checks if any process with the given PID is running)。

然而在docker容器環境下,各容器都有自己的PidNamespace和NetworkNamespace,我們就需要開啟haproxy的健康檢查頁面,然後keepalived通過健康檢查頁面的URL來檢測haproxy目前是否正常執行。

haproxy健康檢查頁面配置:

 

keepalived對haproxy的狀態檢測:

2.3.3. haproxy SSL配置

haproxy代理ssl配置有兩種方式:

  1. haproxy本身提供SSL證書,後面的web伺服器走正常的http協議;

  2. haproxy本身只提供代理,直接轉發client端的HTTPS請求到後端的web伺服器。注意:這種模式下“mode”必須是“tcp”模式, 即僅支援4層代理。

考慮到:第一,使用者親和性訪問需要7層代理的支援;第二,loadbalancer和master走的都是叢集內網。所以本實踐採用了第一種方式,配置如下:

 

2.3.4. haproxy配置:haproxy.cfg

 

2.3.5. keepalived配置:keepalived.conf

1) lb-1上keepalived配置

2) lb-2上keepalived配置

lb-2跟lb-1的配置差不多,除了下面兩個欄位:

2.4flannel網路設定

2.4.1 Master節點flannel網路設定

對於Master節點,需要等待Etcd Pod叢集啟動完後,先在Master上建立Flannel網路,然後Flannel Pod客戶端才可以從Etcd中獲取到各個Master節點的IP網段,獲取到IP網段後會在主機上產生檔案:“ /var/lib/flannel/subnet.env”,然後根據該檔案修改docker啟動引數:


並重啟docker服務。

2.4.2 非Master節點flannel網路設定

對於非Master節點,待Loadbalancer起來之後,Node節點能夠訪問Apiserver之後,Flannel Pod客戶端才能從Etcd獲取到該Node節點的IP網段,並且同樣會在主機上產生檔案:“ /var/lib/flannel/subnet.env”。然後修改docker啟動引數,並重啟docker服務。

三、實踐中的遇到的那些坑

3.1官網“haproxy docker image”的坑

Docker Hub上“haproxy image”的“docker-entrypoint.sh”內容如下:

 

問題就出在“haproxy-systemd-wrapper”。如果執行命令:“haproxy -f /etc/haproxy/haproxy.cfg”, 而實際上執行的是經過“haproxy-systemd-wrapper”包裝後的命令:

執行命令“haproxy -f /etc/haproxy/haproxy.cfg”時,真正執行的是: “/usr/local/sbin/haproxy -p /run/haproxy.pid -f /etc/haproxy/haproxy.cfg -Ds”,對於“-Ds”選項, 官網是這麼描述的:

原來,“haproxy”經過“haproxy-systemd-wrapper”包裝後在後臺執行,而docker container不允許程序後臺執行,否則docker容器將該啟動命令執行完後就退出了。官網image的這個坑很大。

所以,當我們用官網“haproxy image”的時候,就需要用haproxy的完全路徑來執行。比如在yaml檔案中:

 

3.2. haproxy container exited with 137

首先137退出碼錶示,其他程序向haproxy container發起了“kill”訊號,導致haproxy container退出,容器日誌如下:

其次,當通過“docker run”命令執行haproxy container,使用的命令與yaml檔案中的一樣,而且照樣輸出上述的“WARNING”,但是容器卻不退出。

然後,無奈之下,我試著先將這個“WARNING”解決:這個錯誤是由於haproxy.cfg中添加了SSL證書導致的, 可以通過設定引數“default-dh-param”解決:

當我解決這個“WARNING”之後,奇蹟出現了,haproxy container奇蹟般的正常運行了。原來在容器的世界,一個“WARNING”也不能疏忽。

四、社群關於HA Master的未來發展

熟悉kubelet配置引數的都知道,我們在給kubelet配置apiserver的時候,可以通過“--api-servers”指定多個:

這看起來似乎已經做到apiserver的高可用配置了,但是實際上當第一個apiserver掛掉之後, 不能成功的連線到後面的apiserver,也就是說目前仍然只有第一個apiserver起作用。

如果上述問題解決之後, 似乎不需要額外的loadbalancer也能實現master的高可用了,但是,除了kubelet需要配置apiserver,controller manager和scheduler都需要配置apiserver,目前我們還只能通過“--master”配置一個apiserver,無法支援多個apiserver。

社群後續打算支援multi-master配置,實現Kubernetes Master的高可用,而且計劃在Kubernetes 1.4版本中合入。

即使將來社群實現了通過multi-master配置的高可用方式,本次分享的Master High Availability仍然非常有意義,因為在私有云場景中,External Loadbalancer除了實現Master的高可用和負載均衡外,還可以針對Worker Node實現Nodeport請求的負載均衡,從而不僅實現了應用的高可用訪問,同時也大大提高了應用的訪問速度和效能。

參考連結:

Q&A

Q1:請問訪問Docker時,k8s節點上的kubeproxy效能瓶頸如何破?

A1:這是個好問題。在我們的私有云場景,通過k8s節點上kubeproxy的請求,大部分都是來自外網,我們在每個節點上都開了NodePort。然後,仍然利用Externel LoadBalancer做NodePort的負載均衡,這樣,可以避免單節點上Kubeproxy負載過重。

Q2:那麼在2.3“External Loadbalancer部署要點”這一節中整合lvs就是解決這個問題的嗎?

A2:嗯,那個就是為了實現LoadBalancer負載均衡用的。因為,我們已經將LoadBalancer也容器化了,在我們的叢集裡面,一個LoadBalancer就是一個Pod,這樣,我們可以很快啟動一個針對NodePort負載均衡的LoadBalancer。甚至,可以為任何其他服務,迅速啟動LoadBalancer。另外,k8s社群後面會把kubelet也容器化。

Q3:為什麼執行命令要經過“haproxy-systemd-wrapper”包裝,是出於什麼目的?

A3:這個是haproxy官網包裝的,在我的分享中,“-Ds passe en daemon systemd. This patch adds a new option "-Ds"which is exactly like "-D", but instead of forking n times to get n jobs running and thenexiting, prefers to wait for all the children it just created. With this done,haproxy becomes more systemd-compliant, without changing anything for other systems”這段英文已經解釋了,這個包裝中最重要的就是加了“-Ds” 引數。