Kubernetes 的多叢集管理已成為現代化架構的重要課題。傳統的手動管理方式已不敷使用,Operator 模式應運而生,透過自定義資源和控制器實作自動化管理。Prometheus Operator 和 Elasticsearch Operator 即為典型案例,它們簡化了佈署和管理流程。GitOps 方法論進一步提升了管理效率,以 Git 倉函式庫作為單一事實來源,透過 Flux 等工具實作自動同步。此外,整合外部服務也是不可或缺的一環,無選擇器服務、CNAME 記錄、內部負載平衡器以及 NodePort 等技術方案提供了多樣化的整合途徑,讓 Kubernetes 叢集能與外部系統無縫協作。
管理多叢集的創新方法
使用 Operator 模式提升叢集管理能力
在管理多個 Kubernetes 叢集時,傳統的手動組態和管理方式已經變得不再高效。為瞭解決這一問題,可以採用 Operator 模式來自動化關鍵的操作任務。Operator 模式由 CoreOS 團隊在 2016 年引入,根據自定義資源定義(CRD)和自定義控制器(Custom Controllers)兩個核心概念,能夠顯著提升叢集管理的能力。
自定義資源定義與控制器
- 自定義資源定義(CRD):允許擴充套件 Kubernetes API,根據自定義的 API 定義新的資源型別。
- 自定義控制器:根據 Kubernetes 的資源和控制器概念,能夠監控 Kubernetes API 物件的事件,並根據這些事件執行自定義邏輯。
Prometheus Operator 的架構與優勢
Prometheus Operator 是 Operator 模式的一個典型應使用案例項,它透過擴充套件 Kubernetes API,引入了 Prometheus、ServiceMonitor、PrometheusRule 和 AlertManager 等新的資源型別,使得使用者能夠透過少量的物件來指定 Prometheus 佈署的所有細節。
圖表說明:Prometheus Operator 架構
此圖示展示了 Prometheus Operator 的架構,清晰地呈現了其內部各元件之間的邏輯關係。
圖表翻譯: 此圖示說明瞭 Prometheus Operator 如何擴充套件 Kubernetes API,以及如何透過新的資源型別簡化 Prometheus 的佈署和管理。
Elasticsearch Operator 的自動化能力
另一個例子是 Elasticsearch Operator,它能夠自動執行多項任務,包括:
- 組態主節點、客戶端節點和資料節點的副本數量
- 組態可用區以實作高用性佈署
- 調整叢集規模
- 為 Elasticsearch 叢集建立快照備份
程式碼範例:Elasticsearch Operator 組態
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: example-es
spec:
version: 8.4.0
nodeSets:
- name: default
count: 3
config:
node.roles: ["master", "data"]
內容解密:
apiVersion和kind指定了 Elasticsearch 資源的 API 版本和型別。metadata.name定義了 Elasticsearch 叢集的名稱。spec.version指定了 Elasticsearch 的版本。nodeSets定義了節點組的組態,包括節點數量和角色。
GitOps 方法管理叢集
GitOps 是由 Weaveworks 團隊推廣的一種方法論,它將軟體開發生命週期的概念應用於維運實踐。在 GitOps 中,Git 倉函式庫成為了真理的唯一來源,叢集狀態與 Git 倉函式庫中的組態保持同步。
圖表說明:GitOps 工作流程
此圖示展示了 GitOps 的工作流程,包括從 Git 倉函式庫到叢集的自動同步過程。
圖表翻譯: 此圖示詳細解釋了 GitOps 如何透過自動化工具(如 Flux)實作 Git 倉函式庫與叢集狀態的同步。
使用 Flux 實作 GitOps
Flux 是實作 GitOps 的一個流行工具。以下是使用 Flux 同步 Git 倉函式庫到 Kubernetes 叢集的步驟:
- 克隆 Flux 的 GitHub 倉函式庫。
- 修改 Deployment 清單以指向您的 GitHub 倉函式庫。
- 將 Flux 佈署到您的 Kubernetes 叢集。
- 使用
fluxctl命令列工具檢索 SSH 公鑰,並在您的 GitHub 倉函式庫中組態佈署金鑰。
程式碼範例:修改 Flux Deployment 清單
apiVersion: apps/v1
kind: Deployment
metadata:
name: flux
spec:
selector:
matchLabels:
name: flux
template:
metadata:
labels:
name: flux
spec:
containers:
- name: flux
args:
- --[email protected]:your_repo/kbp
內容解密:
args部分指定了 Flux 需要同步的 Git 倉函式庫 URL。- 需要將
--git-url後的 URL 修改為您的實際 GitHub 倉函式庫地址。
管理多 Kubernetes 叢集的最佳實踐
在前面的章節中,我們已經瞭解如何使用 Flux 將 GitHub 儲存函式庫的狀態與 Kubernetes 叢集同步。這使得管理叢集中的多個操作工具變得更加容易,因為多個叢集可以與單一儲存函式庫同步,避免了雪花叢集(snowflake clusters)的情況。
多叢集管理工具
當使用多個叢集時,使用 kubectl 可能會立即變得混亂,因為您需要設定不同的上下文(contexts)來管理不同的叢集。在這種情況下,您會想要安裝的兩個工具是 kubectx 和 kubens,它們允許您輕鬆地在多個上下文和名稱空間之間切換。
流行的多叢集管理工具
以下是一些在 Kubernetes 生態系統中流行的多叢集管理工具:
Rancher:Rancher 在一個集中管理的 UI 中管理多個 Kubernetes 叢集。它監控、管理、備份和還原跨本地、雲端和託管 Kubernetes 環境的叢集。它還具有控制跨多個叢集佈署的應用程式的工具,並提供操作工具。
Open Cluster Management (OCM):OCM 是一個社群驅動的專案,專注於 Kubernetes 應用的多叢集和多雲場景。它提供叢集註冊、工作負載分發和動態放置策略及工作負載。
Gardener:Gardener 採用不同的方法來進行多叢集管理,它利用 Kubernetes 原語(primitives)為您的終端使用者提供 Kubernetes 即服務。它支援所有主要雲供應商,並由 SAP 的人員開發。此解決方案針對正在建立 Kubernetes 即服務產品的使用者。
Kubernetes Federation:Kubernetes 在 1.3 版本中首次引入了 Federation v1,後來已被棄用,取而代之的是 Federation v2。Federation v1 的目標是幫助將應用程式分發到多個叢集。Federation v1 是根據 Kubernetes API 構建的,並嚴重依賴 Kubernetes 註解(annotations),這在其設計中造成了一些問題。設計與核心 Kubernetes API 緊密耦合,使得 Federation v1 相當單一。隨著 Kubernetes CRDs 的引入,人們開始思考如何以不同的方式設計 Federation。
管理多個叢集的最佳實踐
在管理多個 Kubernetes 叢集時,請考慮以下最佳實踐:
• 限制叢集的爆炸半徑,以確保級聯故障不會對您的應用程式產生更大的影響。
• 如果您有 PCI、HIPPA 或 HiTrust 等法規遵從性問題,請考慮使用多叢集來簡化將這些工作負載與一般工作負載混合的複雜性。
• 如果硬性多租戶是業務需求,則應將工作負載佈署到專用叢集。
• 如果您的應用程式需要多個區域,請使用全域負載平衡器(Global Load Balancer)來管理叢集之間的流量。
• 您可以將 HPC 等專門工作負載分解到各自的叢集中,以確保滿足工作負載的特殊需求。
• 如果您正在跨多個區域資料中心佈署工作負載,請首先確保有資料複製策略。多個跨區域的叢集可以很容易實作,但跨區域複製資料可能會很複雜,因此請確保有健全的策略來處理非同步和同步工作負載。
• 使用 Kubernetes 運算子(operators),如 prometheus-operator 或 Elasticsearch operator,來處理自動化操作任務。
• 在設計您的多叢集策略時,也要考慮如何實作服務發現和叢集之間的網路連線。像 HashiCorp 的 Consul 或 Istio 這樣的服務網格工具可以幫助實作跨叢集的網路連線。
• 確保您的持續交付(CD)策略能夠處理區域之間或多個叢集之間的多次佈署。
• 研究使用 GitOps 方法來管理多個叢集的操作元件,以確保您的叢集艦隊中的所有叢集之間的一致性。GitOps 方法並不適用於每個人的環境,但您至少應該研究它,以簡化多叢集環境的操作負擔。
與 Kubernetes 整合外部服務
在許多章節中,我們已經討論瞭如何在 Kubernetes 中建立、佈署和管理服務。然而,現實中的系統並非存在於真空之中,我們建立的大多數服務都需要與 Kubernetes 叢集外部的系統和服務互動。這種互動的必要性源於多種情況,例如:
- 我們正在建置新的服務,而這些服務需要被遺留基礎架構存取,而這些基礎架構執行在虛擬或實體機器上。
- 我們建置的服務需要存取預先存在的資料函式庫或其他執行在本地資料中心的實體基礎架構上的服務。
- 我們可能有多個 Kubernetes 叢集,需要將其中的服務相互連線。
因此,將服務暴露、分享以及建置跨越 Kubernetes 叢集邊界的能力,是建置真實世界應用程式的重要組成部分。
將服務匯入 Kubernetes
將 Kubernetes 與外部服務連線的最常見模式,是使用一個 Kubernetes 服務去消費存在於 Kubernetes 叢集外部的服務。通常,這是因為 Kubernetes 被用於新的應用程式開發,或者作為對遺留資源(如本地資料函式庫)的介面。在許多現有的應用程式中,部分應用程式比其他部分更容易遷移。例如,一個具有關鍵任務資料的資料函式庫可能由於資料治理、合規性或業務連續性的原因而必須保留在本地。同時,在 Kubernetes 中為這些遺留資料函式庫建置新的介面具有顯著的好處。如果每次遷移到 Kubernetes 都需要對整個應用程式進行整體遷移,那麼許多應用程式將永遠被鎖定在它們的遺留實作中。本章將展示如何將雲原生新應用程式的開發與現有的服務(如可能執行在傳統虛擬機器、裸機伺服器甚至大型主機上的資料函式庫)整合起來。
使外部服務可從 Kubernetes 存取的第一個挑戰是正確組態網路
使網路運作正常的細節取決於資料函式庫的位置和 Kubernetes 叢集的位置。因此,這些細節超出了本文的範圍。但是通常,根據雲的 Kubernetes 提供者允許將叢集佈署到使用者提供的虛擬網路(VNET)中,而這些虛擬網路可以與本地網路對等連線。
在 Kubernetes 叢集中的 Pod 與本地資源之間建立網路連線後
下一個挑戰是使外部服務看起來和感覺上像一個 Kubernetes 服務。在 Kubernetes 中,服務發現是透過網域名稱系統(DNS)查詢實作的。因此,為了使我們的外部資料函式庫感覺上是 Kubernetes 的原生部分,我們需要在相同的 DNS 中使資料函式庫可被發現。我們將在下面詳細介紹如何做到這一點。
無選擇器服務(Selector-Less Services)用於穩定的 IP 位址
實作此目的第一種方法是使用無選擇器的 Kubernetes 服務。當您建立一個沒有選擇器的 Kubernetes 服務時,由於沒有 Pod 的標籤與不存在的服務選擇器相匹配,因此不會執行負載平衡。相反,您可以對這個無選擇器服務進行程式設計,使其具有指向您想要新增至 Kubernetes 叢集的外部資源的特定 IP 位址(們)。這樣,當 Kubernetes Pod 對 your-database 進行查詢時,內建的 Kubernetes DNS 伺服器將把它翻譯成您的外部服務的服務 IP 位址。下面是一個針對外部資料函式庫的無選擇器服務範例:
apiVersion: v1
kind: Service
metadata:
name: my-external-database
spec:
ports:
- protocol: TCP
port: 3306
targetPort: 3306
內容解密:
此 YAML 程式碼定義了一個名為 my-external-database 的 Kubernetes 服務。該服務監聽 TCP 通訊協定的 3306 連線埠,並將流量轉發到目標連線埠 3306。由於這是一個無選擇器的服務,因此需要手動建立一個 Endpoints 物件來指定實際提供此服務的 IP 位址。
當該服務存在時,您需要更新其 Endpoints 以包含在 24.1.2.3 上提供服務的資料函式庫 IP 位址:
apiVersion: v1
kind: Endpoints
metadata:
# 重點!這個名稱必須與 Service 相符。
name: my-external-database
subsets:
- addresses:
- ip: 24.1.2.3
ports:
- port: 3306
內容解密:
此 YAML 程式碼定義了一個 Endpoints 物件,與之前建立的 my-external-database 服務相關聯。它指定了提供該服務的實際 IP 位址 24.1.2.3 和連線埠號碼 3306。這樣,當 Pod 存取 my-external-database 時,Kubernetes 就會將請求轉發到 24.1.2.3:3306。
圖 13-1 描述了這是如何將一個服務整合到 Kubernetes 中的。如您所見,Pod 在叢集 DNS 伺服器中查詢該服務,就像查詢其他任何 Kubernetes 服務一樣。但是,它不是被給予 Kubernetes 叢集中另一個 Pod 的 IP 位址,而是被給予一個對應於 Kubernetes 叢集外部資源的 IP 位址。這樣,開發人員甚至可能不知道該服務是在叢集外部實作的。
根據 CNAME 的服務用於穩定的 DNS 名稱
前面的範例假設您要與其整合的外部資源具有穩定的 IP 位址。雖然這通常適用於實體本地資源,但根據網路拓撲的不同,這可能並不總是正確。在雲端環境中,虛擬機器(VM)IP 位址更具動態性,因此這種情況更不可能發生。另外,該服務可能有多個副本位於單一 DNS 負載平衡器後面。在這些情況下,您嘗試橋接到叢集中的外部服務沒有穩定的 IP 位址,但它確實有穩定的 DNS 名稱。
對於這些情況,您可以定義一個根據 CNAME 的 Kubernetes 服務。如果您不熟悉 DNS 記錄,CNAME(Canonical Name,規範名稱)記錄表示特定的 DNS 位址應該被翻譯成不同的規範 DNS 名稱。例如,一個指向 foo.com 且包含 bar.com 的 CNAME 記錄表示任何對 foo.com 的 DNS 請求都應該被轉發到 bar.com。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 根據 CNAME 的服務用於穩定的 DNS 名稱
rectangle "DNS Lookup" as node1
rectangle "CNAME Record" as node2
rectangle "Resolved IP" as node3
node1 --> node2
node2 --> node3
@enduml
圖表翻譯: 此圖示展示了Kubernetes Pod如何透過Kubernetes DNS進行DNS查詢,最終解析到外部服務的IP位址。首先,Pod向Kubernetes DNS發起對某個網域名稱的查詢。Kubernetes DNS根據CNAME記錄,將查詢轉發到外部服務的DNS。最後,外部服務的DNS將網域名稱解析為實際的IP位址,從而完成整個查詢過程。
在Kubernetes中整合外部服務
在現代化的雲原生架構中,將外部服務與Kubernetes叢集進行整合是一項常見的需求。這種整合可以透過多種方式實作,包括使用穩定的DNS名稱、穩定的IP地址,或是透過更複雜的控制器基礎方法。
使用CNAME記錄對映外部服務
在Kubernetes中,一種簡單的方法是使用CNAME記錄將外部服務對映到叢集內可被發現的名稱。這可以透過建立一個ExternalName型別的Service來實作。例如,如果你有一個外部資料函式庫,其DNS名稱為database.myco.com,你可以建立一個名為myco-database的CNAME Service。
kind: Service
apiVersion: v1
metadata:
name: myco-database
spec:
type: ExternalName
externalName: database.myco.com
內容解密:
kind: Service表示這是一個Service資源。type: ExternalName指定這是一個CNAME Service,用於將外部服務對映到叢集內部名稱。externalName: database.myco.com指定了外部服務的DNS名稱。
這種方法使得任何在叢集內查詢myco-database的Pod都能被遞迴解析到database.myco.com。然而,這要求外部資源的DNS名稱也能被Kubernetes DNS伺服器解析。
組態DNS伺服器以與替代DNS解析器通訊
如果外部服務的DNS位於公司內部的DNS伺服器上,你可能需要組態Kubernetes叢集的DNS伺服器以與該DNS解析器進行通訊。這可以透過更新Kubernetes ConfigMap中的DNS伺服器組態來實作。
積極的控制器基礎方法
在某些情況下,前述方法不可行,因為外部服務沒有穩定的DNS名稱或IP地址。這時,可以透過建立一個無選擇器的Service,並手動填充其Endpoints資源來實作整合。
- 建立一個無選擇器的Service。
- 建立一個Endpoints資源,並手動填充外部服務的IP地址。
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresses:
- ip: <外部服務的IP地址>
ports:
- name: http
port: 80
內容解密:
- 第一個YAML定義了一個無選擇器的Service。
- 第二個YAML定義了對應的Endpoints資源,包含了外部服務的IP地址。
- 這樣,Kubernetes就可以將流量路由到外部服務。
從Kubernetes匯出服務
除了將外部服務匯入Kubernetes叢集外,有時還需要將叢集內的服務匯出到外部環境中。這可以透過使用內部負載平衡器或NodePort型別的Service來實作。
使用內部負載平衡器匯出服務
許多雲提供商支援內部負載平衡器,可以將虛擬IP地址對映到一組Pod,並且該虛擬IP地址只在虛擬網路內可路由。
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
內容解密:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"是Azure特定的註解,用於啟用內部負載平衡器。- 這使得服務可以透過內部負載平衡器的IP地址被外部存取。
在NodePort上匯出服務
在本地佈署環境中,可以使用NodePort型別的Service來匯出服務。NodePort會在每個節點上開啟一個埠,將流量轉發到Service。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
內容解密:
type: NodePort指定了Service的型別為NodePort。nodePort: 30080指定了節點上開放的埠,用於將流量轉發到Service。
總之,將外部服務與Kubernetes整合,以及將Kubernetes服務匯出到外部環境,是現代雲原生架構中的重要課題。透過使用CNAME記錄、無選擇器的Service、內部負載平衡器或NodePort,可以實作這些整合需求。