69·存储高级

数据持久化

kubernetesstoragepersistencedatabasestateful

数据持久化

学习目标

  1. 理解有状态应用的数据持久化需求
  2. 掌握数据库等有状态服务的存储方案
  3. 了解 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 故障
  - 模拟节点故障
  - 验证数据完整性

常见有状态应用的存储

应用推荐存储访问模式特殊要求
MySQLSSD 块存储RWO高 IOPS
PostgreSQLSSD 块存储RWO高 IOPS
MongoDBSSD 块存储RWO副本集
RedisSSD 块存储RWO持久化配置
ElasticsearchSSD 块存储RWO高 IOPS
MinIO块存储/NFSRWO/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...

继续学习

完成本课后,建议继续学习下一课「存储最佳实践」 以巩固所学知识。