本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

如何處理資料密集型 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) 服務。

準備黃金快照的高階計劃

  1. 識別您要在雲提供商中克隆資料的磁碟(EBS/永續性磁碟)
  2. 製作磁碟快照(在雲提供商控制檯中)
  3. 獲取磁碟快照 ID

為每個團隊克隆資料的高階計劃

  1. 建立名稱空間“sandbox01”
  2. 將磁碟快照 (ID) 作為 VolumeSnapshotContent 匯入到 Kubernetes
  3. 在名稱空間“sandbox01”中建立對映到 VolumeSnapshotContent 的 VolumeSnapshot
  4. 從 VolumeSnapshot 建立 PersistentVolumeClaim
  5. 安裝帶 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>
Screenshot of a terminal showing volume snapshot creation on GCP

GCP 快照建立

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

Screenshot of AWS web console, showing EBS volume identification

在 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/永續性磁碟。

下面我將為每個名稱空間定義一個清單。為了節省時間,您可以使用諸如 sedyq 等工具替換名稱空間名稱(例如將“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)正在執行。

為了繼續使用我的黃金快照中的資料,我只需要為下一個名稱空間重複此操作即可!無需在複製過程中浪費時間和計算資源。