数据持久化
学习目标
- 理解有状态应用的数据持久化需求
- 掌握数据库等有状态服务的存储方案
- 了解 StatefulSet 与持久存储的结合使用
核心概念
无状态 vs 有状态
无状态应用(Stateless):
┌─────────────────────────────────────┐
│ Deployment: nginx │
│ Pod 1 ←→ 任意存储 │
│ Pod 2 ←→ 任意存储 │
│ Pod 3 ←→ 任意存储 │
│ │
│ 特点:Pod 可随时替换 │
└─────────────────────────────────────┘
有状态应用(Stateful):
┌─────────────────────────────────────┐
│ StatefulSet: mysql │
│ Pod 0 ←→ PVC-0(固定数据) │
│ Pod 1 ←→ PVC-1(固定数据) │
│ Pod 2 ←→ PVC-2(固定数据) │
│ │
│ 特点:Pod 与存储绑定,不可随意替换 │
└─────────────────────────────────────┘
数据库持久化方案
# MySQL with PVC
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
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: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
volumeClaimTemplates 的作用:
StatefulSet 创建时:
Pod mysql-0 → 自动创建 PVC mysql-data-mysql-0
Pod mysql-1 → 自动创建 PVC mysql-data-mysql-1
Pod mysql-2 → 自动创建 PVC mysql-data-mysql-2
每个 Pod 有独立的 PVC,数据不共享
Pod 重建后会绑定同一个 PVC
StatefulSet 存储特点
特点 1:稳定的存储标识
Pod 名称固定:mysql-0, mysql-1, mysql-2
PVC 名称固定:mysql-data-mysql-0, mysql-data-mysql-1
特点 2:有序部署
mysql-0 创建并就绪 → mysql-1 创建 → mysql-2 创建
特点 3:有序删除
mysql-2 删除 → mysql-1 删除 → mysql-0 删除
特点 4:PVC 保留
Pod 删除时 PVC 不删除(默认)
重新创建 Pod 时绑定原有 PVC
多副本数据库架构
主从复制架构:
┌─────────────────────────────────────────┐
│ StatefulSet │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ mysql-0 │ │ mysql-1 │ │ mysql-2 │
│ │ (主) │ │ (从) │ │ (从) │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ ▼ ▼ ▼
│ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ PVC-0 │ │ PVC-1 │ │ PVC-2 │
│ │ (主数据)│ │ (从数据)│ │ (从数据)│
│ └──────────┘ └──────────┘ └──────────┘
│ │
│ 写入 → 主节点 → 复制到从节点 │
│ 读取 → 从节点(可选) │
└─────────────────────────────────────────┘
持久化关键配置
# 1. 使用 volumeClaimTemplates(推荐)
spec:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
# 2. 使用 NFS 共享存储(适合共享数据)
spec:
volumes:
- name: shared-data
nfs:
server: nfs-server
path: /exports/shared
# 3. 使用 emptyDir(临时缓存)
spec:
volumes:
- name: cache
emptyDir:
sizeLimit: 1Gi
持久化检查清单
部署有状态应用前检查:
□ 选择合适的存储类型
- 数据库:块存储(SSD)
- 文件共享:NFS/EFS
- 缓存:emptyDir
□ 配置正确的访问模式
- 单 Pod:ReadWriteOnce
- 多 Pod 共享:ReadWriteMany
□ 设置合理的存储容量
- 考虑数据增长
- 预留扩展空间
□ 配置备份策略
- VolumeSnapshot
- 定时快照
- 异地备份
□ 测试恢复流程
- 模拟 Pod 故障
- 模拟节点故障
- 验证数据完整性
常见有状态应用的存储
| 应用 | 推荐存储 | 访问模式 | 特殊要求 |
|---|---|---|---|
| MySQL | SSD 块存储 | RWO | 高 IOPS |
| PostgreSQL | SSD 块存储 | RWO | 高 IOPS |
| MongoDB | SSD 块存储 | RWO | 副本集 |
| Redis | SSD 块存储 | RWO | 持久化配置 |
| Elasticsearch | SSD 块存储 | RWO | 高 IOPS |
| MinIO | 块存储/NFS | RWO/RWX | 对象存储 |
数据迁移方案
方案 1:快照恢复
源 PVC → VolumeSnapshot → 新 PVC
方案 2:rsync 迁移
源 Pod → rsync → 目标 Pod
方案 3:应用层复制
主库 → 复制 → 新从库
方案 4:导出导入
mysqldump → SQL 文件 → mysql import
实践练习
练习 1:部署带持久存储的 MySQL
# mysql-persistent.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # base64 encoded "password123"
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
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: password
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
练习 2:验证数据持久性
# 部署 MySQL
kubectl apply -f mysql-persistent.yaml
# 等待就绪
kubectl wait --for=condition=ready pod -l app=mysql --timeout=120s
# 创建测试数据
kubectl exec mysql-0 -- mysql -uroot -ppassword123 -e "CREATE DATABASE testdb; USE testdb; CREATE TABLE t1(id INT); INSERT INTO t1 VALUES(1);"
# 删除 Pod
kubectl delete pod mysql-0
# 等待重建
kubectl wait --for=condition=ready pod -l app=mysql --timeout=120s
# 验证数据存在
kubectl exec mysql-0 -- mysql -uroot -ppassword123 -e "SELECT * FROM testdb.t1;"
练习 3:查看 PVC 绑定
# 查看 StatefulSet 创建的 PVC
kubectl get pvc
# 查看 PVC 与 Pod 的对应关系
kubectl get pods,pvc -l app=mysql
# 删除 StatefulSet(PVC 保留)
kubectl delete statefulset mysql
# 查看 PVC 仍然存在
kubectl get pvc
# 重新创建 StatefulSet,数据仍在
kubectl apply -f mysql-persistent.yaml
常见误解
1. StatefulSet 不是必须的
单副本数据库可以用 Deployment + PVC
StatefulSet 用于需要稳定标识和有序部署的场景
简单场景不需要 StatefulSet
2. PVC 删除会导致数据丢失
默认回收策略是 Retain(PV 保留)
但 PVC 删除后 PV 变为 Released 状态
需要手动清理或重新绑定
生产环境建议使用 Retain 策略
小结
| 要点 | 说明 |
|---|---|
| 有状态应用 | 需要持久存储,数据不可丢失 |
| StatefulSet | 稳定标识 + 有序部署 + 持久存储 |
| volumeClaimTemplates | 为每个 Pod 自动创建独立 PVC |
| 存储选择 | 数据库用 SSD,文件共享用 NFS |
| 备份策略 | VolumeSnapshot + 定时快照 |
练习编辑器
bash
Loading...