Kubernetes 提供了容器協調的強大功能,但對於應用程式開發流程的支援相對不足。因此,在 Kubernetes 上建構應用程式平台,以提升開發者生產力和簡化 Kubernetes 的使用,變得至關重要。平台設計應支援容器映像檔匯出,讓開發者能靈活調整應用程式封裝方式,並整合 Kubernetes 的服務發現機制,確保平台與其他系統的互通性。此外,平台應妥善管理應用程式狀態,並有效利用 Kubernetes 的儲存管理功能,例如 PersistentVolume 和 PersistentVolumeClaim,來提供持久化儲存。對於複雜的有狀態應用程式,Operator 模式提供更進階的管理能力。最後,StorageClass 的動態儲存組態機制簡化了儲存資源的管理,並提升了平台的彈性。
在 Kubernetes 上建構更高層級的應用程式平台的最佳實踐
結合 GitHub Actions 和 GitOps 可以讓開發者實作快速佈署,同時遵循雲原生生態系統和諸如基礎設施即程式碼(IaC)的理念。
建構平台時的設計考量
無數的平台被建立以提高開發者的生產力。透過觀察這些平台成功和失敗的經驗,可以總結出一套通用的模式和考量。遵循這些設計準則有助於確保所建構的平台是成功的,而不是最終需要放棄的「遺留」死衚衕。
支援匯出到容器映像檔
在建構平台時,許多設計透過讓使用者簡單地提供程式碼(如函式即服務(FaaS)中的函式)或原生套件(如 Java 中的 JAR 檔案)而非完整的容器映像檔來提供簡便性。這種方法很有吸引力,因為它讓使用者保持在其熟悉的工具和開發體驗範圍內。平台負責為他們處理應用程式的容器化。
然而,這種方法的問題在於,當開發者遇到所提供的程式設計環境的限制時。例如,他們可能需要特定版本的語言執行環境來解決 bug,或者需要封裝額外的資源或可執行檔,而這些並不是自動容器化應用程式結構的一部分。
無論原因為何,遇到這種情況對開發者來說是個糟糕的時刻,因為他們突然需要學習更多關於如何封裝應用程式的知識,而他們原本只是想稍微擴充套件它來修復 bug 或交付新功能。
然而,事情不一定非要如此。如果支援將平台的程式設計環境匯出到通用容器中,使用該平台的開發者就不需要從頭開始學習所有關於容器的知識。相反,他們擁有一個代表當前應用程式的完整工作容器映像檔(即包含其函式和節點執行環境的容器映像檔)。根據這個起點,他們可以進行微小的調整,以使容器映像檔適應他們的需求。這種逐步降級和增量學習大大平滑了從較高層級平台到較低層級基礎設施的路徑。它還提高了平台的通用性,因為使用它並不會為開發者引入陡峭的學習曲線。
程式碼範例:匯出容器映像檔
# 使用多階段構建來建立乾淨的容器映像檔
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 建立最終的容器映像檔
FROM node:14
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "server.js"]
內容解密:
- 多階段構建:第一階段使用
node:14作為基礎映像檔來安裝依賴並構建應用程式。 - 最終容器映像檔:第二階段同樣使用
node:14,但只複製第一階段構建好的結果,避免將不必要的構建工具包含在最終映像檔中。 - CMD 命令:指定容器啟動時執行的命令。
支援現有的服務和服務發現機制
另一個常見的平台故事是,它們會進化和與其他系統相互連線。許多開發者可能在你的平台上非常滿意和高效,但任何真實世界的應用程式都會跨越你所構建的平台和較低層級的 Kubernetes 應用程式,以及其他平台。與遺留資料函式庫或為 Kubernetes 構建的開源應用程式的連線將始終是足夠大的應用程式的一部分。
由於這種互連需求,Kubernetes 核心原語對於服務和服務發現的使用和暴露對於你所構建的任何平台都至關重要。不要為了改善平台體驗而重新發明輪子,因為這樣做會建立一個無法與更廣泛的世界互動的封閉園區。
如果將平台中定義的應用程式暴露為 Kubernetes 服務,那麼叢集內的任何應用程式都能夠使用你的應用程式,無論它們是否在你的更高層級平台上執行。同樣,如果使用 Kubernetes DNS 伺服器進行服務發現,那麼就能夠從更高層級的應用程式平台連線到叢集中執行的其他應用程式,即使它們未在你的更高層級平台中定義。可能會想建立更好或更易於使用的東西,但不同平台之間的互連是任何足夠年齡和複雜度的應用程式的共同設計模式。你將會後悔建立一個封閉園區的決定。
建構應用程式平台的最佳實踐
儘管 Kubernetes 為操作軟體提供了強大的工具,但它在幫助開發者構建應用程式方面做得相對較少。通常需要在 Kubernetes 上構建平台,以使開發者更具生產力或使 Kubernetes 更易於使用。在構建這類別平台時,牢記以下最佳實踐將會有所裨益:
- 使用准入控制器來限制和修改對叢集的 API 呼叫。准入控制器可以驗證(並拒絕無效的)Kubernetes 資源。變異准入控制器可以自動修改 API 資源以新增新的 sidecar 或其他使用者可能甚至不需要了解的變更。
- 使用
kubectl外掛來擴充套件 Kubernetes 使用者經驗,透過向現有的命令列工具新增新的工具。在極少數情況下,專門構建的工具可能更合適。 - 在 Kubernetes 上構建平台時,仔細考慮平台的使用者及其需求將如何演變。讓事情變得簡單易用顯然是一個好目標,但如果這也導致使用者被困住,如果沒有在你的平台之外重寫一切就無法成功,那麼最終將會是一種令人沮喪(且不成功)的體驗。
管理狀態與有狀態應用程式的最佳實踐
在容器協調的早期階段,主要針對的是無狀態應用程式,這些應用程式會利用外部系統來儲存必要的狀態。隨著時間的推移,根據容器的有狀態工作負載需求日益增長,而 Kubernetes 也逐漸演進以滿足這些需求,不僅允許將儲存卷掛載到 Pod 中,還允許 Kubernetes 直接管理這些卷。
卷與卷掛載
許多應用程式在容器化過程中,需要將特定的目錄掛載到容器中,以便讀寫相關資訊。雖然 ConfigMaps 或 secrets 可以將資料注入卷中,但通常是唯讀的。本文重點討論為容器提供可寫入且能在容器或 Pod 故障後仍然存在的卷。
為什麼需要卷掛載?
一個實際的例子是,某些舊版應用程式會將特定的應用資訊記錄到本地檔案系統。解決方案之一是更新應用程式碼,將日誌輸出到 stdout 或 stderr,或者使用 sidecar 容器透過共用的 Pod 卷將日誌資料串流到外部來源。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-webserver
spec:
replicas: 3
selector:
matchLabels:
app: nginx-webserver
template:
metadata:
labels:
app: nginx-webserver
spec:
containers:
- name: nginx-webserver
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: hostvol
mountPath: /usr/share/nginx/html
volumes:
- name: hostvol
hostPath:
path: /home/webcontent
卷掛載的最佳實踐
- 盡量限制將卷用於需要多個容器共用資料的 Pod,例如介面卡或大使模式。使用 emptyDir 來實作這些共用模式。
- 當需要節點代理或服務存取資料時,使用 hostPath。
- 識別出將關鍵應用日誌和事件寫入本地磁碟的服務,如果可能的話,將其改為輸出到 stdout 或 stderr,並讓真正的 Kubernetes 感知日誌聚合系統來串流日誌,而不是依賴卷掛載。
Kubernetes 儲存管理
Kubernetes 使用 PersistentVolume 和 PersistentVolumeClaim 這兩個不同的 API 來管理 Pod 的儲存。
PersistentVolume
PersistentVolume 可以被視為支援掛載到 Pod 的卷的磁碟。它具有宣告策略,定義了卷的生命週期範圍,使其獨立於使用該卷的 Pod 的生命週期。
PersistentVolume 與 PersistentVolumeClaim 的關係
Kubernetes 可以使用動態或靜態定義的卷。要允許動態建立卷,必須在 Kubernetes 中定義 StorageClass。不同型別和類別的 PersistentVolume 可以在叢集中建立,只有當 PersistentVolumeClaim 與 PersistentVolume 匹配時,才會將其分配給 Pod。
卷的最佳實踐
- 使用動態卷供應來簡化儲存管理。
- 使用合適的 StorageClass 來滿足不同的效能和容量需求。
管理複雜的有狀態應用程式
對於像 MongoDB、MySQL 或 Kafka 這樣的複雜資料管理系統,需要更多的協調來確保叢整合員之間的通訊、成員識別和成員出現或消失的順序。
Operator 模式
Operator 模式是一種新的模式,不僅可以利用 Kubernetes 原語,還可以新增業務或應用邏輯作為自定義控制器,以簡化複雜資料管理系統的維運。
Kubernetes 儲存管理深度解析
Kubernetes 為現代容器化應用提供了強大的儲存管理機制,從 PersistentVolume(PV)到 StorageClass,建構出彈性且高效的儲存解決方案。本文將探討 Kubernetes 儲存的核心概念、實作方式及最佳實踐。
PersistentVolume 與 PersistentVolumeClaim 的協同運作
在 Kubernetes 中,PersistentVolume(PV)代表實際的儲存資源,而 PersistentVolumeClaim(PVC)則是對儲存資源的需求定義。兩者的結合實作了儲存資源的動態分配與管理。
PersistentVolume 的詳細組態
以下是一個典型的 NFS 型 PersistentVolume 組態範例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
labels:
tier: "silver"
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
內容解密:
- storageClassName: 指定所屬的 StorageClass 名稱,用於分類別管理不同的儲存型別。
- accessModes: 定義存取模式,
ReadWriteMany表示可被多個節點同時讀寫。 - mountOptions: 設定掛載選項,如 NFS 版本和連線特性。
- persistentVolumeReclaimPolicy: 定義資源回收策略,
Recycle表示自動清理資料後可重複使用。
PersistentVolumeClaim 的精確匹配
PVC 是 Pod 對儲存資源的需求描述,以下範例展示瞭如何建立一個符合前述 PV 的 PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
selector:
matchLabels:
tier: "silver"
內容解密:
- storageClassName: 與 PV 的 StorageClass 名稱一致,確保使用正確的儲存型別。
- selector.matchLabels: 使用標籤選擇器精確匹配符合要求的 PV。
- resources.requests.storage: 請求的儲存容量大小,必須與 PV 相符。
在 Deployment 中使用 PVC
成功建立 PVC 後,可以在 Deployment 組態中參照它,為應用提供持久化儲存:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-webserver
spec:
replicas: 3
selector:
matchLabels:
app: nginx-webserver
template:
metadata:
labels:
app: nginx-webserver
spec:
containers:
- name: nginx-webserver
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: hostvol
mountPath: /usr/share/nginx/html
volumes:
- name: hostvol
persistentVolumeClaim:
claimName: my-pvc
內容解密:
- volumeMounts: 將 PVC 掛載到容器內的指定路徑。
- persistentVolumeClaim.claimName: 參照之前建立的 PVC 名稱。
- replicas: 設定多個副本,分享相同的 PVC,實作高用性。
StorageClass:動態儲存組態的核心
StorageClass 提供了一種動態建立 PV 的機制,無需管理員手動預先組態:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: nfs
provisioner: cluster.local/nfs-client-provisioner
parameters:
archiveOnDelete: "true"
圖表說明:Kubernetes 儲存元件關係圖
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 圖表說明:Kubernetes 儲存元件關係圖
rectangle "使用" as node1
rectangle "請求" as node2
rectangle "動態建立" as node3
rectangle "提供儲存" as node4
node1 --> node2
node2 --> node3
node3 --> node4
@enduml
圖表翻譯: 此圖示呈現了 Kubernetes 中 Pod、PVC、StorageClass 和 PV 之間的關聯流程。Pod 使用 PVC 請求儲存資源,PVC 根據 StorageClass 的定義動態建立合適的 PV,最終由 PV 提供實際的儲存服務給 Pod 使用。
Kubernetes 儲存最佳實踐
- 啟用預設 StorageClass:簡化應用佈署流程,提高靈活性。
- 考量區域和連線性:在叢集架構設計時,充分考慮運算和資料層之間的網路拓撲,最佳化效能。
- 使用 CSI 或 FlexVolume:採用容器儲存介面(CSI)或 FlexVolume,讓儲存供應商能夠獨立開發和更新儲存外掛程式。