Skip to content

我的 K8S DevOps 實驗環境 - 控制平面端點

Published: 10 分鐘

本文延續「我的 K8S DevOps 實驗環境」,針對其中的控制平面端點(control-plane endpoint)加以說明。作為高可用 K8S 叢集的必要元件,控制平面端點需要負責提供 API 服務的負載均衡(load balancing)以及錯誤轉移(failover)機制,山姆鍋藉由 HAProxy 以及 Keepalived 來實現控制平面端點。

K8S 控制平面元件

工作負載(workload)的高可用(high availability)由K8S負責提供,所以,高可用 K8S 叢集主要在確保 K8S 控制平面元件的冗余,也就是確保服務(e.g. API server)可以容錯,使得叢集內的排程工作持續順利進行。


下圖由 Kubernetes 官方文件所提供,這裡主要目的在於展示控制平面元件。

Components of Kubernetes

下面各小節簡介控制平面元件以及說明該元件採用的高可用模式。

API Server

提供K8S 的 API 服務,屬於無狀態(stateless)元件,所以,叢集中的每個 API Server 實例(instance)都可以提供服務。本文所謂的「控制平面端點」就是為了隱藏 API Server 實例的故障以及將請求分散給不同的實例來均衡負載。

Kube Controller Manager

Controller 負責確保叢集內某類特定資源的狀態與 API Server 回傳的狀態一致,Controller Manager 提供這些 controller 的執行支援。Controller Manager 的高可用採用 leader-followers 模式,同一時間只有作為 leader 的 Controller Manager 實際在運作,其餘實例在現任 leader 故障時重新選舉新 leader 接替。

Scheduler

負責叢集中工作負載的主要排程服務,例如,決定 Pod 要配置到哪個節點。同一時間只有一個 Scheduler 在負責排程工作,其餘實例作為熱備援(hot standby)可以隨時接替工作。

Etcd

分散式資料存儲,叢集內的資源資料與狀態都存放在此。由於 Etcd 的元件內部使用 Raft 共識演算法,元件分別扮演 leader 或者follower 角色。Etcd 叢集寫入(write)操作都由 leader 元件接受並將結果複製到叢集中的所有 follower 元件。由於 leader 選舉採用多數決(quorum),因此,Etcd 叢集需由奇數個實例組成(至少3個,生產環境建議為5個)。 Etcd 叢集可以選擇部署在與其它 K8S控制元件分開的節點,但此實驗環境選擇較簡單的架構,每一台控制節點(master/control-plane node)都部署上述元件的實例。

雖然此實驗環境使用 Etcd 作為叢集高可用資料存儲,但不同的 K8S 發行版本可能採用別的方案。例如,K3S 也可以支援 MySQL/PostgreSQL 作為資料存儲。

控制平面端點

從架構圖可以得知,多數K8S 的控制平面元件,包括 Kubelet 守護行程(daemon)以及客戶端程式等都需要呼叫 API Server。由於有多個 API Server 實例,需要提供一個統一固定的存取點來簡化K8S API 客戶端呼叫(e.g. kubelet 只能設定一個 API server 端點)以及隱藏故障移轉過程的影響。傳統上,對於一組提供相同服務的守護行程(daemons)都是透過服務負載均衡器(server loadbalancer)來分散請求到不同副本(replica)。有了服務負載均衡器雖然可以增加服務請求的數量以及效率,但如果負載均衡器故障則會影響到客戶端。所以,常利用虛擬 IP 來讓兩個以上的負載均衡器可以互相備援。

此實驗環境分別採用 Keepalived 來實現虛擬 IP 位址,以及透過 HAProxy 來提供 API 服務的負載均衡。從山姆鍋開始研究 Linux HA 開始,這個組合就是常常出現的方案且是經得起時間考驗的。

Keepalived 安裝設定

透過 ARP 協定,節點可以廣播所屬網卡的 IP 位址讓其它節點可以找到 IP 所對應的網卡硬體位址(MAC Address)以完成封包的路由(routing)。根據服務節點的健康狀態,可以選擇性地由不同節點來廣播 ARP 封包從而實現虛擬 IP 的錯誤移轉。Keepalived 作為支援 VRRP 的 Linux 高可用元件,除了可以實現前述的虛擬 IP 功能外,當發生錯誤轉移時可以呼叫自訂的腳本。這點對於無法在網卡層面支援虛擬 IP 的環境(如 DigitalOcean 等雲端服務),但支援 API 來動態綁定浮動(floating) IP的情境可以達成與虛擬 IP 相同的目的。因此,在機房或者雲端自建K8S叢集都有機會可以使用 HAProxy + Keepalived 來實現高可用的負載均衡服務。但即使如此,除非有特別考量,山姆鍋還是傾向於使用雲端託管的K8S服務的。

如果只是為了高可用,其實可以只使用 Keepalived 來提供。基於此實驗環境目標之一是盡可能接近生產環境設定,所以也採用負載均衡器以充分利用系統效能。

Keepalived 以系統套件安裝並以守護行程(daemon)方式執行:

apt-get install -y keepalived

下面是 /etc/keepalived/keepalived.conf 設定檔內容樣板(template):

global_defs {
  default_interface ${SPK_KEEPALIVED_INTERFACE}
}
vrrp_instance VI_1 {
    interface ${SPK_KEEPALIVED_INTERFACE}
    virtual_router_id 101
    nopreempt
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip ${API_ADV_ADDRESS}
    unicast_peer {
      ${SPK_LAB_NETWORK}.11
      ${SPK_LAB_NETWORK}.12
      ${SPK_LAB_NETWORK}.13
    }
    virtual_ipaddress {
        ${SPK_KEEPALIVED_VIRTUAL_IP}
    }
}

其中,

  • ${SPK_KEEPALIVED_INTERFACE}: 是虛擬 IP 預計綁定的網卡。由於 Vagrant 設定的虛擬機中,第一張網卡是 NAT 模式,此實驗環境使用第二張網卡作為叢集內節點的通訊用途,第二張網卡的預設名稱是 eth1

  • ${API_ADV_ADDRESS}:此 IP 位址就是 Keepalived 實例所在的控制節點 IP 位址。

  • ${SPK_KEEPALIVED_VIRTUAL_IP}:此變數就是 API 服務的虛擬 IP 位址,預設是 172.42.42.10

  • ${SPK_LAB_NETWORK}.11 ~ .13: 基於雲端環境多數不支援 IP Multicast 的假設下,Keepalived 透過 unicast 來進行對等點(peer)偵測工作。

注意: Keepalived 設定 nopreempt,且沒有設定 priority(所以每個實例優先權相同)。這樣做的目的是為了避免較高優先權的 keepalived 恢復後將 IP 轉移回去(failback),造成現有的 TCP 連線中斷。

設定修改完成後,重啟 Keepalived 守護行程:

systemctl restart keepalived

HAProxy 安裝設定

由於 API Server 的負載均衡只需要依賴 IP 跟 Port,所以只需要可以支援 TCP(layer 4)的負載均衡器即可。常用的選項是 HAProxy 以及 Nginx,此實驗環境選用 HAProxy 來作為 API Server 的負載均衡器。

HAProxy 同樣使用系統套件安裝:

apt-get install -y haproxy

底下為 HAProxy 的 /etc/haproxy/haproxy.cfg 設定檔樣板:

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private
frontend spk-frontend
        bind *:16443
        mode tcp
        log global
        option tcplog
        timeout client 3600s
        backlog 4096
        maxconn 50000
        use_backend spk-masters
backend spk-masters
        mode  tcp
        option redispatch
        balance roundrobin
        timeout connect 1s
        timeout queue 5s
        timeout server 3600s
        server spkmaster-1 ${SPK_LAB_NETWORK}.11:6443 check
        server spkmaster-2 ${SPK_LAB_NETWORK}.12:6443 check
        server spkmaster-3 ${SPK_LAB_NETWORK}.13:6443 check

其中,

  1. 此實驗環境的控制平面端點採用 16443 port,而個別 API Server 則是使用 6443 port。
  2. ${SPK_LAB_NETWORK}: 實驗環境網段位址前置字串,預設是 172.42.42
  3. 可以看出3個控制節點的 API Server 端點是寫死的。

設定修改完成後,重啟 HAProxy 守護行程:

systemctl restart haproxy

小結

在網路上有看過利用 Kubelet 以靜態 Pod 形式執行 HAProxy 以及 Keepalived 的參考文件。但此實驗環境使用的 kubeadm 版本在執行 kubeadm init 或者 kubeadm join之前,kubelet 並不會正常執行。由於控制平面端點在kubeadm init 執行前就要正常啟動,這導致無法利用靜態 Pod 形式來部署。另一個替代方式是以 Docker 容器來執行 HAProxy 以及 Keepalived, 也許之後的版本會改採用這種方式。

參考文章

郭信義 (Sam Kuo)

奔騰網路科技技術長,專長分散式系統、Web 應用與雲端服務架構、設計、開發、部署與維運。工作之餘,喜歡關注自由軟體的發展與應用,偶爾寫一下部落格文章。