Pod 重啟資料就消失
到目前為止你部署的所有東西都是無狀態的:nginx、frontend、API server。Pod 死了再起一個一模一樣的就好。
但資料庫不行。
上一篇整合實作裡的 MySQL 一刪 Pod、資料就全部不見。為什麼?
容器的檔案系統是短暫的。Pod 刪除後,容器內所有寫入的檔案(包括 MySQL 的 /var/lib/mysql 資料)跟著消失。下次 Pod 重啟,是從乾淨的 Image 重新開始。
$ kubectl exec mysql-pod -- mysql -e "INSERT INTO users VALUES('Alice');"
$ kubectl delete pod mysql-pod
$ kubectl exec mysql-pod -- mysql -e "SELECT * FROM users;"
ERROR: Table 'users' doesn't exist # ← 整個資料庫都沒了
要保住資料,需要 K8s 的持久化儲存:PV + PVC。
PV vs PVC:停車位的比喻
K8s 把儲存拆成兩個資源:
- PersistentVolume(PV) — 一塊實際的儲存空間(管理員建)
- PersistentVolumeClaim(PVC) — Pod 對儲存的申請單(開發者寫)
┌── 停車場(K8s 管理員)─────────────────────┐
│ PV-1: 10GB SSD │
│ PV-2: 50GB HDD │
│ PV-3: 5GB SSD │
└──────────────────────────────────────────┘
↑ 配對(Binding)
┌── 開發者 ─────────────────────────────────┐
│ PVC-A: "我要 5GB" → 配到 PV-3 │
│ PVC-B: "我要 10GB" → 配到 PV-1 │
└──────────────────────────────────────────┘
↓
Pod 用 PVC 掛載
為什麼要拆兩個? 職責分離。
- 基礎架構團隊管 PV:「公司有幾台 NAS、幾顆 SSD、各自多大」
- 應用開發團隊寫 PVC:「我的 App 要 10GB ReadWriteOnce」
- 開發者不需要知道底層是 NFS 還是 SSD,反正 K8s 會配給你
對照 Docker
| Docker | Kubernetes |
|---|---|
docker volume create mydata | 建 PV |
docker run -v mydata:/var/lib/mysql | PVC + Pod volumeMounts |
| 自動建 + 掛載合在一起 | 拆成兩步(PV / PVC 分離) |
PV YAML
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 2Gi # 這塊 PV 有 2GB
accessModes:
- ReadWriteOnce # 只允許一個 Node 同時讀寫
persistentVolumeReclaimPolicy: Retain # PVC 刪了資料保留
storageClassName: manual # 給 PVC 配對用的標籤
hostPath:
path: /tmp/k8s-pv-data # 用 Node 本機目錄當儲存
幾個關鍵欄位:
accessModes:誰能讀寫?
| Mode | 縮寫 | 行為 | 適合 |
|---|---|---|---|
ReadWriteOnce | RWO | 一個 Node 可讀寫 | 資料庫(最常用) |
ReadOnlyMany | ROX | 多個 Node 可唯讀 | 靜態檔案 |
ReadWriteMany | RWX | 多個 Node 可讀寫 | 共享檔案(需 NFS) |
persistentVolumeReclaimPolicy:PVC 刪了 PV 怎辦?
| Policy | 行為 | 適合 |
|---|---|---|
Retain | PV 和資料保留,管理員手動處理 | 生產環境(資料不能丟) |
Delete | PV 和資料一起砍 | 開發環境 / 雲端(PVC 刪 EBS 磁碟也刪,省錢) |
kubectl delete pvc 不小心打錯就 game over 了。
hostPath(學習用)
hostPath 用 Node 本機的某個目錄當儲存。只能在學習 / 單機叢集用,因為:
- 多 Node 叢集,Pod 換到別台 Node 就找不到資料
- Node 本身掛掉,資料就跟著沒
- 沒做容錯 / 備份
PVC YAML
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-pvc
spec:
accessModes:
- ReadWriteOnce # ← 跟 PV 對應
resources:
requests:
storage: 1Gi # 我要 1GB(PV 有 2GB,夠)
storageClassName: manual # ← 跟 PV 對應
PVC 不指定 PV 名字,K8s 會看 storageClassName + accessModes + 容量去自動配對。
配對流程
$ kubectl apply -f pv.yaml
$ kubectl apply -f pvc.yaml
$ kubectl get pv,pvc
NAME CAPACITY ACCESS MODES STATUS CLAIM
pv/local-pv 2Gi RWO Bound default/local-pvc
NAME STATUS VOLUME CAPACITY
pvc/local-pvc Bound local-pv 2Gi
STATUS: Bound = 配對成功。注意:
- PVC 申請 1GB,但配到的 PV 是 2GB → 整顆 PV 都歸 PVC 用(一夫一妻制)
- 想要剛好 1GB?多建一個 1GB PV,K8s 自動挑最接近的
Pod 用 PVC
apiVersion: v1
kind: Pod
metadata:
name: mysql-with-pvc
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: rootpw
volumeMounts:
- name: data
mountPath: /var/lib/mysql # MySQL 資料目錄
volumes:
- name: data
persistentVolumeClaim:
claimName: local-pvc # ← 掛 PVC
容器內的 /var/lib/mysql 寫入會落到 PVC 對應的 PV 實體儲存。
驗證資料持久化
# 1. 寫資料
$ kubectl exec mysql-with-pvc -- mysql -prootpw -e "
CREATE DATABASE shop;
USE shop;
CREATE TABLE users(name VARCHAR(20));
INSERT INTO users VALUES('Alice');
"
# 2. 砍 Pod
$ kubectl delete pod mysql-with-pvc
# 3. 重建 Pod(同一個 PVC)
$ kubectl apply -f mysql-pod.yaml
# 4. 看資料
$ kubectl exec mysql-with-pvc -- mysql -prootpw -e "USE shop; SELECT * FROM users;"
+-------+
| name |
+-------+
| Alice | # ← 資料還在!
+-------+
對照沒掛 PVC 的版本:資料消失 vs 資料還在。這就是 PV / PVC 的價值。
排錯
| STATUS | 意思 | 怎麼處理 |
|---|---|---|
Bound | 配對成功 | 正常 |
Pending | PVC 找不到合適 PV | 檢查 storageClassName / 容量是否匹配 |
Lost | PV 不見了 | PV 被手動刪除?hostPath 目錄被砍? |
Pending 最常見,看一下原因:
$ kubectl describe pvc local-pvc
Events:
Warning ProvisioningFailed no available volume plugin matches
通常是:
storageClassName 跟 PV 對不上accessModes 不一致重點整理
- 容器是無狀態的,要保資料必須用 PV
- PV = 實體儲存(管理員建),PVC = 申請單(開發者寫)
- K8s 自動配對(看
storageClassName/accessModes/ 容量) accessModes90% 用ReadWriteOnce- 生產用
Retain,永遠不要用 Delete hostPath只能學習用,正式用 NFS / 雲商磁碟
下一步
PV 是手動建的 — 但工作上資料庫要 5GB、API 服務要 10GB、Cache 要 2GB...每個都管理員手動建,太累了。
下一篇:StorageClass + StatefulSet 自動建 PV。
📅 下一篇:StorageClass + StatefulSet 自動建 PV
手動建 PV 太累了——StorageClass 動態配置 + StatefulSet 跑 MySQL,一條龍做完。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)