在 Kubernetes 環境中佈署 Redis 叢集時,StatefulSet 能夠提供必要的穩定性和持久化儲存。透過 StatefulSet,每個 Redis Pod 都擁有唯一的網路識別,方便管理和擴充套件。設定 PersistentVolumeClaim 可以確保資料持久化,即使 Pod 重新啟動,資料也不會遺失。擴充套件 Redis 叢集時,只需調整 StatefulSet 的副本數量,Kubernetes 會自動建立新的 Pod,並使用 ConfigMap 和 Secret 管理組態和密碼等敏感資訊。為了讓應用程式存取 Redis,需要建立 Service,其中 headless Service 適用於寫入操作,而 ClusterIP Service 則適用於讀取操作。此外,Ingress 可以用於路由外部流量到 Redis 服務。Helm 作為 Kubernetes 的套件管理器,可以簡化佈署流程,並透過引數化組態實作不同環境的快速佈署。values.yaml 檔案可以根據不同環境的需求設定不同的引數值,例如副本數量、資源限制等。對於開發者工作流程,建立專用的開發叢集可以提高開發效率。開發者可以快速迭代和除錯程式碼,並在提交程式碼前進行測試。使用名稱空間可以隔離不同開發者的資源,避免互相干擾。
使用 StatefulSet 佈署 Redis 資料函式庫
本章節將介紹如何使用 Kubernetes 的 StatefulSet 資源佈署 Redis 資料函式庫,並確保其高用性。
建立 Redis StatefulSet
首先,我們需要建立一個 Redis StatefulSet。以下是一個範例 YAML 檔案:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: "redis"
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5-alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
這個 YAML 檔案定義了一個名為 redis 的 StatefulSet,具有一個副本,使用 redis:5-alpine 映象,並將資料儲存在 PersistentVolume 中。
內容解密:
serviceName指定了與此 StatefulSet 相關聯的 Service 名稱。replicas指定了 StatefulSet 中的副本數量。volumeClaimTemplates定義了用於儲存資料的 PersistentVolumeClaim 範本。
擴充套件 Redis 叢集
要擴充套件 Redis 叢集,我們需要增加副本數量並確保新的副本能夠連線到主節點。以下是更新後的 YAML 檔案:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: "redis"
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5-alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
- name: script
mountPath: /script/launch.sh
subPath: launch.sh
- name: passwd-volume
mountPath: /etc/redis-passwd
command:
- sh
- -c
- /script/launch.sh
volumes:
- name: script
configMap:
name: redis-config
defaultMode: 0777
- name: passwd-volume
secret:
secretName: redis-passwd
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
內容解密:
replicas已更新為 3,以增加副本數量。- 新增了一個名為
launch.sh的指令碼,用於啟動 Redis 節點並連線到主節點。 - 使用 ConfigMap 將
launch.sh指令碼掛載到容器中。
建立 Redis Service
要使 Redis 資料函式庫可供前端存取,我們需要建立兩個不同的 Kubernetes Service。以下是範例 YAML 檔案:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis
namespace: default
spec:
ports:
- port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
sessionAffinity: None
type: ClusterIP
內容解密:
- 此 Service 用於讀取 Redis 資料。
selector指定了與此 Service 相關聯的 Pod。
建立 Redis Write Service
要使 Redis 資料函式庫可供寫入,我們需要建立一個 headless Service。以下是範例 YAML 檔案:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis-write
name: redis-write
spec:
clusterIP: None
ports:
- port: 6379
selector:
app: redis
內容解密:
- 此 Service 用於寫入 Redis 資料。
clusterIP設定為None,使其成為 headless Service。
使用 Ingress 路由流量到靜態檔案伺服器
最後,我們需要建立一個 Ingress 資源來路由流量到靜態檔案伺服器。以下是範例 YAML 檔案:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
spec:
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: fileserver
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: fileserver
port:
number: 80
圖表翻譯:
此圖表顯示了 Ingress 資源的組態,將 /api 路徑路由到 fileserver Service 的 8080 埠,將 / 路徑路由到 fileserver Service 的 80 埠。
使用 Helm 引數化您的應用程式
到目前為止,我們所討論的一切都集中在將單一例項佈署到單一叢集。然而,在現實中,幾乎每個服務和服務團隊都需要佈署到多個環境(即使它們分享一個叢集)。即使您是一名開發人員,開發單一應用程式,您也可能希望至少有一個開發版本和一個生產版本,以便在不破壞生產使用者的情況下進行迭代和開發。
佈署選項
許多團隊最初的失敗模式是簡單地將檔案從一個叢集複製到另一個叢集。與其擁有單一的 frontend/ 目錄,不如擁有一對 frontend-production/ 和 frontend-development/ 目錄。雖然這是一個可行的選擇,但也很危險,因為您現在負責確保這些檔案彼此保持同步。如果它們原本應該完全相同,這可能很容易,但開發和生產之間的一些差異是可以預期的,因為您將開發新功能。關鍵是這種差異必須是有意的且易於管理。
另一個實作此目的選擇是使用分支和版本控制,將生產和開發分支從中央儲存函式庫中分出,並且分支之間的差異清晰可見。這對於某些團隊來說可能是一個可行的選擇,但當您希望同時將軟體佈署到不同的環境(例如,佈署到多個不同雲區域的 CI/CD 系統)時,在分支之間切換的機制就變得具有挑戰性。
因此,大多數人最終都會使用範本系統。範本系統將範本(形成應用程式組態的集中式骨幹)與引數(將範本專門化為特定的環境組態)結合起來。這樣,您可以擁有一個通用的分享組態,並根據需要進行有意(且易於理解)的自定義。Kubernetes 有多種範本系統,但迄今為止最受歡迎的是 Helm。
Helm 簡介
在 Helm 中,應用程式被封裝在稱為 chart 的檔案中集合中(在容器和 Kubernetes 的世界中,航海笑話比比皆是)。chart 以 chart.yaml 檔案開頭,該檔案定義了 chart 本身的後設資料:
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for our frontend journal server.
name: frontend
version: 0.1.0
此檔案放置在 chart 目錄的根目錄中(例如,frontend/)。在此目錄中,有一個 templates 目錄,範本就放在這裡。範本基本上是來自前面的示例的 YAML 檔案,其中檔案中的某些值被引數參照所取代。例如,假設您要引數化前端的副本數量。以前,Deployment 是這樣的:
spec:
replicas: 2
在範本檔案(frontend-deployment.tmpl)中,它看起來像這樣:
spec:
replicas: {{ .replicaCount }}
內容解密:
此段落展示瞭如何使用 Helm 範本系統來引數化 Kubernetes 資源定義檔案。其中 {{ .replicaCount }} 是一種範本語法,用於參照在 values.yaml 檔案中定義的變數 replicaCount。這樣,當佈署 chart 時,可以透過替換 replicaCount 的值來動態地設定 Deployment 的副本數量。
這意味著當您佈署 chart 時,您將用適當的引數替換 replicas 的值。引數本身在 values.yaml 檔案中定義。每個環境都會有一個 values 檔案,用於佈署應用程式。這個簡單 chart 的 values 檔案如下所示:
replicaCount: 2
內容解密:
這裡定義了一個 values.yaml 檔案,用於存放範本中使用的變數值。在這個例子中,replicaCount 被設定為 2,這將用於替換範本中的 {{ .replicaCount }}。
將所有這些放在一起,您可以使用 helm 工具佈署此 chart,如下所示:
helm install path/to/chart --values path/to/environment/values.yaml
這會引數化您的應用程式並將其佈署到 Kubernetes。隨著時間的推移,這些引數化將擴充套件到涵蓋應用程式的多種環境。
佈署服務的最佳實踐
Kubernetes 是一個強大的系統,可能看起來很複雜。但是,如果您使用以下最佳實踐,則設定基本應用程式以獲得成功可以很簡單:
- 大多數服務應作為 Deployment 資源佈署。Deployments 建立相同的副本以實作冗餘和擴充套件。
- Deployments 可以使用 Service 公開,Service 實際上是一個負載平衡器。
- Service 可以在叢集內(預設)或外部公開。如果要公開 HTTP 應用程式,可以使用 Ingress 控制器新增諸如請求路由和 SSL 之類別的功能。
- 最終,您需要引數化您的應用程式,以使其組態在不同環境中更具可重用性。像 Helm 這樣的封裝工具是這種引數化的最佳選擇。
開發者工作流程
Kubernetes 的設計初衷是為了可靠地執行軟體。它簡化了應用程式的佈署與管理,提供了應用導向的 API、自癒特性以及諸如 Deployments 等有用的工具,以實作零停機的軟體滾動更新。雖然這些工具非常有用,但它們並沒有讓開發者在 Kubernetes 上開發應用程式變得更容易。這就是開發者工作流程發揮作用的地方。儘管許多叢集都是設計來執行生產環境應用程式,因此很少被開發者工作流程存取,但啟用開發者工作流程以針對 Kubernetes 進行開發至關重要,這通常意味著需要一個專門用於開發的叢集或至少部分叢集。建立這樣一個叢集以促進 Kubernetes 應用程式的開發對於確保 Kubernetes 的成功至關重要。如果沒有為您的叢集構建任何程式碼,那麼叢集本身就沒有完成太多工作。
目標
在描述構建開發叢集的最佳實踐之前,值得闡述我們對這些叢集的目標。顯然,最終目標是使開發者能夠快速、輕鬆地在 Kubernetes 上構建應用程式,但這在實踐中究竟意味著什麼,又如何在開發叢集的實際功能中體現出來?
為了回答這個問題,讓我們首先確定開發者與叢集互動的階段。
第一階段:入門
第一階段是入門,這是新開發者加入團隊的時候。此階段包括為使用者提供叢集登入以及讓他們熟悉第一個佈署。此階段的目標是在最短的時間內讓開發者上手。您應該為此過程設定一個關鍵績效指標(KPI)目標。一個合理的目標是讓使用者在不到半小時的時間內從零開始執行目前的應用程式。每當有新成員加入團隊時,都要測試您是否達到了這個目標。
第二階段:開發
第二階段是開發,這是開發者的日常活動。此階段的目標是確保快速迭代和除錯。開發者需要快速、重複地將程式碼推播到叢集。他們還需要能夠輕鬆測試和除錯程式碼。此階段的 KPI 更具挑戰性,但您可以透過測量將 pull request(PR)或變更上傳並在叢集中執行所需的時間,或者透過調查使用者的生產力感受來估計。您還可以透過團隊的整體生產力來衡量。
第三階段:測試
第三階段是測試,這與開發階段交織在一起,用於在提交和合併之前驗證程式碼。此階段的目標有兩個。首先,開發者應該能夠在提交 PR 之前執行其環境的所有測試。其次,所有測試都應該在程式碼合併到儲存函式庫之前自動執行。除了這些目標之外,您還應該為測試執行時間設定 KPI。隨著專案變得越來越複雜,越來越多的測試需要更長的時間。當這種情況發生時,識別出一組較小的煙霧測試可能會很有價值,開發者可以在提交 PR 之前使用這些測試進行初步驗證。您還應該對測試的不穩定性設定非常嚴格的 KPI。不穩定的測試是指偶爾(或不那麼偶爾)失敗的測試。在任何合理活躍的專案中,每千次執行失敗超過一次將導致開發者摩擦。您需要確保叢集環境不會導致測試不穩定。
建立開發叢集
當人們開始考慮在 Kubernetes 上開發時,首先要做出的選擇之一是建立單個大型開發叢集還是為每個開發者建立一個叢集。請注意,這種選擇只有在動態叢集建立很容易的環境中才有意義,例如公有雲。在實體環境中,可能只有一個大型叢集是唯一可行的選擇。
如果您有選擇權,您應該考慮每個選項的優缺點。如果您選擇為每個使用者建立一個叢集,這種方法的一個重大缺點是它將更昂貴、效率更低,並且您將有很多不同的開發叢集需要管理。額外的成本來自於每個叢集很可能被嚴重低估利用率的事實。此外,隨著開發者建立不同的叢集,跟蹤和垃圾收集不再使用的資源變得更加困難。根據使用者的叢集方法的一個優點是簡單性:每個開發者都可以自行管理自己的叢集,並且由於隔離,不同的開發者很難相互幹擾。
另一方面,單個開發叢集將顯著更高效;您可能可以在一個分享叢集上維持相同數量的開發者,成本降低到三分之一(或更少)。此外,您更容易為叢集安裝分享服務,例如監控和日誌記錄,這使得生成一個對開發者友好的叢集變得更加容易。分享開發叢集的一個缺點是使用者管理和潛在的開發者之間的幹擾。由於向 Kubernetes 叢集新增新使用者和名稱空間的過程目前尚未簡化,您需要啟動一個流程來使新開發者上線。
儘管兩種方法都是可行的,但一般來說,我們的建議是為所有開發者建立一個大型分享叢集。儘管在開發者之間的幹擾方面存在挑戰,但這些挑戰是可以管理的,並且最終,成本效率和能夠輕鬆地向叢集中新增組織範圍內的服務的能力超過了幹擾的風險。但是,您需要投資於使開發者上線、資源管理和垃圾收集的流程。
為多個開發者設定分享叢集
在設定大型分享叢集時,主要目標是確保多個使用者可以同時使用該叢集而不會相互幹擾。區分不同開發者的明顯方法是使用 Kubernetes 名稱空間。名稱空間可以用作服務佈署的作用域,以確保一個使用者的前端服務不會干擾另一個使用者的前端服務。名稱空間也是 RBAC 的作用域,確保一個開發者無法意外刪除另一個開發者的工作。因此,在分享叢集中,使用名稱空間是有意義的。
apiVersion: v1
kind: Namespace
metadata:
name: developer-namespace
內容解密:
此 YAML 程式碼片段定義了一個新的 Kubernetes 名稱空間資源。apiVersion 和 kind 欄位指定了資源的型別和版本。metadata 部分包含了名稱空間的名稱,這裡被設定為 developer-namespace。使用名稱空間可以幫助組織和隔離叢集中的資源,從而避免不同專案或團隊之間的資源名稱衝突。
Kubernetes 名稱空間隔離示意圖
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Kubernetes StatefulSet 佈署 Redis 高可用性叢集
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml
圖表翻譯: 此圖表展示了一個 Kubernetes 叢集中如何使用名稱空間進行資源隔離。Kubernetes 叢集包含多個名稱空間,每個名稱空間內佈署了不同的服務。這種結構有效地避免了不同名稱空間之間的資源衝突,提高了資源管理的效率和安全性。
總之,建立一個適合多個開發者的分享 Kubernetes 叢集需要仔細規劃和正確組態,以確保不同使用者之間的隔離和資源的有效管理。透過使用名稱空間、適當的 RBAC 設定以及高效的資源管理策略,可以實作一個既高效又安全的開發環境。