前言
在 Kubernetes 環境中,容器的生命週期是短暫的,當 Pod 被刪除或重新調度時,容器內的資料也會隨之消失。為了解決資料持久化的問題,Kubernetes 提供了持久化儲存機制,包括 Persistent Volume(PV)和 Persistent Volume Claim(PVC)。本文將深入介紹這些儲存概念、建立方式以及動態供應機制。
儲存概念
為什麼需要持久化儲存
容器設計的理念是無狀態的,但實際應用中經常需要儲存資料:
- 資料庫資料:MySQL、PostgreSQL 等資料庫需要持久化儲存
- 應用程式日誌:日誌檔案需要保留以供分析
- 使用者上傳檔案:圖片、文件等使用者資料
- 配置檔案:某些應用需要持久化的配置
Kubernetes 儲存架構
Kubernetes 儲存架構包含以下核心元件:
| 元件 | 說明 |
|---|
| Volume | 最基本的儲存抽象,與 Pod 生命週期綁定 |
| Persistent Volume (PV) | 叢集層級的儲存資源,獨立於 Pod 生命週期 |
| Persistent Volume Claim (PVC) | 使用者對儲存資源的請求 |
| StorageClass | 定義動態供應儲存的規範 |
Volume 與 Persistent Volume 的差異
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| ┌─────────────────────────────────────────────────────────────┐
│ Volume │
│ - 與 Pod 生命週期綁定 │
│ - Pod 刪除時,資料可能遺失 │
│ - 適合暫時性資料儲存 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Persistent Volume │
│ - 獨立於 Pod 生命週期 │
│ - Pod 刪除後,資料仍然保留 │
│ - 適合需要持久化的資料 │
│ - 可在不同 Pod 間共享 │
└─────────────────────────────────────────────────────────────┘
|
PV 建立
PV 基本概念
Persistent Volume(PV)是叢集中的一塊儲存空間,由管理員預先配置或透過 StorageClass 動態供應。PV 是叢集資源,不屬於任何 Namespace。
PV 存取模式
PV 支援以下存取模式:
| 存取模式 | 縮寫 | 說明 |
|---|
| ReadWriteOnce | RWO | 單一節點讀寫掛載 |
| ReadOnlyMany | ROX | 多節點唯讀掛載 |
| ReadWriteMany | RWX | 多節點讀寫掛載 |
| ReadWriteOncePod | RWOP | 單一 Pod 讀寫掛載(Kubernetes 1.22+) |
PV 回收策略
| 策略 | 說明 |
|---|
| Retain | 保留資料,需手動處理 |
| Delete | 刪除 PV 時同時刪除後端儲存 |
| Recycle | 清除資料後重新使用(已棄用) |
建立 hostPath PV
最簡單的 PV 類型,使用節點本地目錄:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/pv-data
type: DirectoryOrCreate
|
1
2
3
4
5
6
7
8
| # 套用 PV 定義
kubectl apply -f pv-hostpath.yaml
# 查看 PV 狀態
kubectl get pv
# 查看 PV 詳細資訊
kubectl describe pv local-pv
|
建立 NFS PV
使用 NFS 共享儲存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.1.100
path: /exports/data
mountOptions:
- hard
- nfsvers=4.1
|
建立雲端儲存 PV
AWS EBS PV
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: v1
kind: PersistentVolume
metadata:
name: aws-ebs-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
awsElasticBlockStore:
volumeID: vol-0123456789abcdef0
fsType: ext4
|
Azure Disk PV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| apiVersion: v1
kind: PersistentVolume
metadata:
name: azure-disk-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
azureDisk:
diskName: myDisk
diskURI: /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Compute/disks/myDisk
kind: Managed
fsType: ext4
|
GCE Persistent Disk PV
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: v1
kind: PersistentVolume
metadata:
name: gce-pd-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
|
PVC 使用
PVC 基本概念
Persistent Volume Claim(PVC)是使用者對儲存資源的請求。PVC 會與符合條件的 PV 進行綁定,綁定後 Pod 就可以使用該儲存。
建立 PVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
# 可選:指定 StorageClass
# storageClassName: standard
# 可選:使用標籤選擇特定 PV
selector:
matchLabels:
type: local
|
1
2
3
4
5
6
7
8
| # 套用 PVC 定義
kubectl apply -f pvc.yaml
# 查看 PVC 狀態
kubectl get pvc
# 查看 PVC 詳細資訊
kubectl describe pvc my-pvc
|
PV 與 PVC 綁定機制
PVC 與 PV 的綁定遵循以下規則:
- 容量匹配:PV 容量必須大於或等於 PVC 請求
- 存取模式匹配:PV 必須支援 PVC 請求的存取模式
- StorageClass 匹配:如果指定 StorageClass,必須相符
- 標籤選擇器:如果 PVC 使用 selector,PV 必須符合條件
在 Pod 中使用 PVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: nginx:1.24
volumeMounts:
- name: data-volume
mountPath: /usr/share/nginx/html
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: my-pvc
|
在 Deployment 中使用 PVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 1
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: nginx:1.24
ports:
- containerPort: 80
volumeMounts:
- name: web-data
mountPath: /usr/share/nginx/html
- name: logs
mountPath: /var/log/nginx
volumes:
- name: web-data
persistentVolumeClaim:
claimName: web-data-pvc
- name: logs
persistentVolumeClaim:
claimName: logs-pvc
|
StatefulSet 與 PVC
StatefulSet 可以為每個 Pod 副本建立獨立的 PVC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 10Gi
|
這會為每個 Pod 建立獨立的 PVC:data-mysql-0、data-mysql-1、data-mysql-2。
StorageClass
StorageClass 概念
StorageClass 提供了一種描述儲存「類別」的方式,讓管理員可以定義不同的儲存配置。它是動態供應的核心元件。
StorageClass 組成
| 欄位 | 說明 |
|---|
| provisioner | 指定使用哪個 Volume Plugin 來供應 PV |
| parameters | 傳遞給 provisioner 的參數 |
| reclaimPolicy | PV 回收策略(Delete 或 Retain) |
| volumeBindingMode | 綁定模式(Immediate 或 WaitForFirstConsumer) |
| allowVolumeExpansion | 是否允許擴展 Volume 容量 |
建立 StorageClass
AWS EBS StorageClass
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-ebs-gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
|
Azure Disk StorageClass
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-managed-premium
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
cachingMode: ReadOnly
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
|
GCE Persistent Disk StorageClass
1
2
3
4
5
6
7
8
9
10
| apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
|
NFS StorageClass(使用 NFS Provisioner)
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: nfs.csi.k8s.io
parameters:
server: nfs-server.example.com
share: /exports/data
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
- hard
- nfsvers=4.1
|
設定預設 StorageClass
1
2
3
4
5
6
7
8
| # 查看現有 StorageClass
kubectl get storageclass
# 設定預設 StorageClass
kubectl patch storageclass <storage-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
# 移除預設設定
kubectl patch storageclass <storage-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
|
Volume 綁定模式
PVC 建立後立即綁定 PV(預設行為):
1
| volumeBindingMode: Immediate
|
適用情境:
WaitForFirstConsumer 模式
等待 Pod 調度後再綁定 PV:
1
| volumeBindingMode: WaitForFirstConsumer
|
適用情境:
- 多可用區域的叢集
- 需要 PV 與 Pod 在同一區域
- 減少跨區域資料存取延遲
動態供應
動態供應概念
動態供應允許系統根據 PVC 的需求自動建立 PV,無需管理員手動預先建立。這大幅簡化了儲存管理的工作。
動態供應流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 使用者 │ │ Kubernetes │ │ Provisioner │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ 建立 PVC │ │
│───────────────────>│ │
│ │ │
│ │ 查詢 StorageClass │
│ │───────────────────>│
│ │ │
│ │ 建立後端儲存 │
│ │<───────────────────│
│ │ │
│ │ 建立 PV │
│ │<───────────────────│
│ │ │
│ │ 綁定 PVC 與 PV │
│ │ │
│ PVC 已綁定 │ │
│<───────────────────│ │
│ │ │
|
使用動態供應
只需在 PVC 中指定 StorageClass:
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: aws-ebs-gp3
resources:
requests:
storage: 20Gi
|
若使用預設 StorageClass,可省略 storageClassName:
1
2
3
4
5
6
7
8
9
10
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: default-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
|
完整動態供應範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| # StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "4000"
throughput: "250"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: database-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-storage
resources:
requests:
storage: 50Gi
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: database
spec:
replicas: 1
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
volumes:
- name: data
persistentVolumeClaim:
claimName: database-pvc
|
Volume 擴展
當 StorageClass 允許擴展時,可以增加 PVC 容量:
1
2
| # 編輯 PVC 增加容量
kubectl edit pvc database-pvc
|
或使用 patch:
1
| kubectl patch pvc database-pvc -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
|
注意事項:
- 只能增加容量,不能減少
- 某些儲存類型可能需要重新啟動 Pod
- 擴展過程可能需要一些時間
最佳實務
1. 選擇適當的存取模式
- 單一 Pod 使用:選擇
ReadWriteOnce - 多 Pod 共享讀取:選擇
ReadOnlyMany - 多 Pod 共享讀寫:選擇
ReadWriteMany(需要支援的儲存類型,如 NFS、EFS)
2. 設定資源配額
1
2
3
4
5
6
7
8
9
| apiVersion: v1
kind: ResourceQuota
metadata:
name: storage-quota
namespace: production
spec:
hard:
requests.storage: 500Gi
persistentvolumeclaims: 20
|
3. 使用 WaitForFirstConsumer
對於多可用區域叢集,建議使用此模式避免跨區域問題:
1
| volumeBindingMode: WaitForFirstConsumer
|
4. 定期備份重要資料
使用 Volume Snapshot 功能進行備份:
1
2
3
4
5
6
7
8
| apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: database-snapshot
spec:
volumeSnapshotClassName: csi-aws-vsc
source:
persistentVolumeClaimName: database-pvc
|
5. 監控儲存使用量
1
2
3
4
5
6
7
8
| # 查看 PV 使用狀況
kubectl get pv
# 查看 PVC 狀態
kubectl get pvc -A
# 查看 Pod 中的儲存使用
kubectl exec -it <pod-name> -- df -h
|
常見問題排解
PVC 處於 Pending 狀態
1
2
3
4
5
6
7
8
| # 查看 PVC 事件
kubectl describe pvc <pvc-name>
# 常見原因:
# 1. 沒有符合條件的 PV
# 2. StorageClass 不存在
# 3. 容量不足
# 4. 存取模式不匹配
|
PV 無法釋放
1
2
3
4
5
6
| # 查看 PV 狀態
kubectl get pv <pv-name>
# 如果狀態為 Released,需要手動處理
# 清除 claimRef 以重新使用(注意:資料可能仍存在)
kubectl patch pv <pv-name> -p '{"spec":{"claimRef": null}}'
|
儲存效能問題
- 選擇適當的儲存類型(SSD vs HDD)
- 調整 IOPS 和吞吐量參數
- 考慮使用本地儲存(Local Persistent Volume)以獲得更好效能
總結
本文介紹了 Kubernetes 持久化儲存的核心概念和實作方式:
- 儲存概念:理解 Volume、PV、PVC 和 StorageClass 的關係
- PV 建立:使用 hostPath、NFS 或雲端儲存建立持久化儲存
- PVC 使用:透過 PVC 請求儲存資源並在 Pod 中掛載使用
- StorageClass:定義儲存類別,支援不同效能和特性的儲存
- 動態供應:自動化儲存供應,簡化管理工作
掌握 Kubernetes 持久化儲存是建構有狀態應用的關鍵技能。建議根據應用需求選擇適當的儲存類型和配置,並遵循最佳實務確保資料的可靠性和可用性。
參考資源