本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
如何處理資料密集型 Kubernetes 環境中的資料重複問題
為什麼要重複資料?
為每個團隊建立應用程式副本及其狀態副本很方便。例如,您可能需要一個單獨的資料庫副本來測試一些重要的模式更改,或者開發其他破壞性操作,如批次插入/刪除/更新...
複製資料需要大量時間。這是因為您需要先將所有資料從源塊儲存提供商下載到計算節點,然後再將其傳送回儲存提供商。此過程涉及大量的網路流量和 CPU/RAM 使用。透過將某些昂貴的操作解除安裝到專用硬體來實現硬體加速,始終能大幅提升效能。它能將完成操作所需的時間縮短好幾個數量級。
卷快照來幫忙
Kubernetes 在 1.12 版本中引入了 VolumeSnapshots 作為 alpha 版本,在 1.17 版本中升級為 beta 版本,並在 1.20 版本中釋出了正式版。VolumeSnapshots 使用儲存提供商的專用 API 來複制資料卷。
由於資料已經位於同一儲存裝置(裝置陣列)中,對於具有本地快照的儲存提供商(大多數本地儲存提供商),複製資料通常是元資料操作。您只需將新磁碟指向一個不可變快照,並只儲存增量(或讓它執行全盤複製)。由於此操作在儲存後端內部進行,因此速度快得多,通常不涉及透過網路傳送流量。公共雲端儲存提供商的底層工作方式略有不同。它們將快照儲存到物件儲存,然後在“複製”磁碟時,再從物件儲存複製回塊儲存。從技術上講,在雲提供商端會花費大量的計算和網路資源,但從 Kubernetes 使用者的角度來看,VolumeSnapshots 的工作方式相同,無論是本地快照儲存提供商還是遠端快照儲存提供商,並且此操作不涉及計算和網路資源。
聽起來我們有解決方案了,對嗎?
實際上,VolumeSnap照是名稱空間的,Kubernetes 會保護名稱空間資料不被租戶(名稱空間)之間共享。此 Kubernetes 限制是一個有意識的設計決策,以便在不同名稱空間中執行的 Pod 無法掛載其他應用程式的 PersistentVolumeClaim(PVC)。
一種解決方法是在一個名稱空間中建立多個包含重複資料的卷。但是,您可能會很容易引用錯誤的副本。
因此,想法是透過名稱空間來分離團隊/專案,以避免這種情況,並通常限制對生產名稱空間的訪問。
解決方案?外部建立“黃金快照”
繞過此設計限制的另一種方法是外部建立快照(而不是透過 Kubernetes)。這也被稱為手動預配置快照。接下來,我將把它匯入為多租戶的黃金快照,可用於多個名稱空間。下面的圖示將針對 AWS EBS (Elastic Block Storage) 和 GCE PD (Persistent Disk) 服務。
準備黃金快照的高階計劃
- 識別您要在雲提供商中克隆資料的磁碟(EBS/永續性磁碟)
- 製作磁碟快照(在雲提供商控制檯中)
- 獲取磁碟快照 ID
為每個團隊克隆資料的高階計劃
- 建立名稱空間“sandbox01”
- 將磁碟快照 (ID) 作為 VolumeSnapshotContent 匯入到 Kubernetes
- 在名稱空間“sandbox01”中建立對映到 VolumeSnapshotContent 的 VolumeSnapshot
- 從 VolumeSnapshot 建立 PersistentVolumeClaim
- 安裝帶 PVC 的 Deployment 或 StatefulSet
步驟 1:識別磁碟
首先,您需要識別您的黃金源。在我的例子中,它是“production”名稱空間中 PersistentVolumeClaim “postgres-pv-claim”上的 PostgreSQL 資料庫。
kubectl -n <namespace> get pvc <pvc-name> -o jsonpath='{.spec.volumeName}'
輸出將類似於
pvc-3096b3ba-38b6-4fd1-a42f-ec99176ed0d90
步驟 2:準備您的黃金源
您只需執行一次此操作,或者在每次要重新整理黃金資料時執行。
建立磁碟快照
轉到 AWS EC2 或 GCP Compute Engine 控制檯,搜尋具有與上次輸出匹配的標籤的 EBS 卷(在 AWS 上)或永續性磁碟(在 GCP 上)。在本例中,我看到:pvc-3096b3ba-38b6-4fd1-a42f-ec99176ed0d9
。
點選“建立快照”併為其命名。您可以在控制檯中手動執行此操作,在 AWS CloudShell / Google Cloud Shell 中,或在終端中執行。要在終端中建立快照,您必須安裝並配置 AWS CLI 工具 (aws
) 或 Google 的 CLI (gcloud
)。
這是在 GCP 上建立快照的命令
gcloud compute disks snapshot <cloud-disk-id> --project=<gcp-project-id> --snapshot-names=<set-new-snapshot-name> --zone=<availability-zone> --storage-location=<region>

GCP 快照建立
GCP 透過 PVC 名稱識別磁碟,因此是直接對映。在 AWS 中,您需要首先透過 CSIVolumeName AWS 標籤(值為 PVC 名稱)查詢卷(volume-id),該標籤將用於快照建立。

在 AWS 上識別磁碟 ID
標記完成卷(volume-id)vol-00c7ecd873c6fb3ec
,然後可以在 AWS 控制檯中建立 EBS 快照,或者使用aws cli
。
aws ec2 create-snapshot --volume-id '<volume-id>' --description '<set-new-snapshot-name>' --tag-specifications 'ResourceType=snapshot'
步驟 3:獲取您的磁碟快照 ID
在 AWS 中,上述命令將輸出類似以下內容:
"SnapshotId": "snap-09ed24a70bc19bbe4"
如果您使用的是 GCP 雲,您可以透過查詢給定快照的名稱,從 gcloud 命令獲取快照 ID
gcloud compute snapshots --project=<gcp-project-id> describe <new-snapshot-name> | grep id:
您應該會得到類似的輸出
id: 6645363163809389170
步驟 4:為每個團隊建立開發環境
現在我有了我的黃金快照,它是不可變資料。每個團隊都將獲得此資料的副本,團隊成員可以根據需要修改它,前提是每個團隊都將建立一個新的 EBS/永續性磁碟。
下面我將為每個名稱空間定義一個清單。為了節省時間,您可以使用諸如 sed
或 yq
等工具替換名稱空間名稱(例如將“sandbox01”更改為“sandbox42”),或者使用 Kubernetes 感知模板工具(如 Kustomize),或者在 CI/CD 管道中使用變數替換。
這是一個清單示例
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
name: postgresql-orders-db-sandbox01
namespace: sandbox01
spec:
deletionPolicy: Retain
driver: pd.csi.storage.gke.io
source:
snapshotHandle: 'gcp/projects/staging-eu-castai-vt5hy2/global/snapshots/6645363163809389170'
volumeSnapshotRef:
kind: VolumeSnapshot
name: postgresql-orders-db-snap
namespace: sandbox01
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgresql-orders-db-snap
namespace: sandbox01
spec:
source:
volumeSnapshotContentName: postgresql-orders-db-sandbox01
在 Kubernetes 中,VolumeSnapshotContent (VSC) 物件不是名稱空間的。但是,我需要為每個不同的名稱空間使用單獨的 VSC,因此每個 VSC 的 metadata.name
也必須不同。為了簡化這一點,我將目標名稱空間作為名稱的一部分。
現在是時候用安裝在您的 K8s 叢集中的 CSI(容器儲存介面)驅動替換驅動程式欄位了。主要的雲提供商都有支援 VolumeSnapshots 的塊儲存 CSI 驅動,但通常 CSI 驅動預設不安裝,請諮詢您的 Kubernetes 提供商。
上面的清單定義了一個在 GCP 上執行的 VSC。在 AWS 上,驅動程式和 SnashotHandle 值可能如下所示:
driver: ebs.csi.aws.com
source:
snapshotHandle: "snap-07ff83d328c981c98"
此時,我需要使用**保留**策略,這樣 CSI 驅動就不會嘗試刪除我手動建立的 EBS 磁碟快照。
對於 GCP,您必須手動構建此字串——新增完整的專案 ID 和快照 ID。對於 AWS,它只是一個普通的快照 ID。
VSC 還需要指定哪個 VolumeSnapshot (VS) 將使用它,因此 VSC 和 VS 相互引用。
現在我可以從上面的 VS 建立 PersistentVolumeClaim。首先設定這個很重要
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pv-claim
namespace: sandbox01
spec:
dataSource:
kind: VolumeSnapshot
name: postgresql-orders-db-snap
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 21Gi
如果預設 StorageClass 具有 WaitForFirstConsumer 策略,那麼實際的雲磁碟將僅在某個 Pod 繫結該 PVC 時才從 Golden Snapshot 建立。
現在我將該 PVC 分配給我的 Pod(在我的案例中是 PostgreSQL),就像我使用任何其他 PVC 一樣。
kubectl -n <namespace> get volumesnapshotContent,volumesnapshot,pvc,pod
VS 和 VSC 都應為 READYTOUSE true,PVC 已繫結,並且 Pod(來自 Deployment 或 StatefulSet)正在執行。
為了繼續使用我的黃金快照中的資料,我只需要為下一個名稱空間重複此操作即可!無需在複製過程中浪費時間和計算資源。