16.5.4 StatefulSet与持久化应用

StatefulSet用于管理有状态工作负载,提供稳定网络标识、持久化存储绑定与有序部署/扩缩容能力,适合数据库、消息队列、配置中心等需要稳定身份和数据一致性的场景。

原理草图(StatefulSet + Headless Service + PVC):

文章图片

核心特性与行为:
- 稳定标识:Pod名称固定为<sts-name>-<ordinal>,重建后名称不变。
- 稳定网络:结合Headless Service提供固定DNS,如mysql-0.mysql-hl.default.svc.cluster.local
- 稳定存储:volumeClaimTemplates为每个Pod创建独立PVC。
- 有序操作:默认顺序创建/删除,按序滚动更新,保障依赖关系。

关键配置要点(含解释):
- serviceName必须指向Headless Service,确保DNS解析。
- volumeClaimTemplates定义存储规格与访问模式,建议明确storageClassName与容量。
- podManagementPolicy可选OrderedReadyParallel,影响扩缩容时序。
- updateStrategy建议生产使用RollingUpdate并配置partition做灰度更新。
- readinessProbelivenessProbe确保仅健康实例对外服务。

示例:部署MySQL StatefulSet(含PVC与Headless Service)

# 文件: /opt/k8s/mysql-hl.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-hl
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
    name: mysql
---
# 文件: /opt/k8s/mysql-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-hl
  replicas: 2
  podManagementPolicy: OrderedReady
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: busybox:1.36
        command: ["sh","-c","chown -R 999:999 /var/lib/mysql"]
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "Root@123"
        ports:
        - containerPort: 3306
          name: mysql
        readinessProbe:
          exec:
            command: ["sh","-c","mysqladmin ping -uroot -pRoot@123"]
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          tcpSocket:
            port: 3306
          initialDelaySeconds: 30
          periodSeconds: 10
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "standard"
      resources:
        requests:
          storage: 10Gi

部署与验证(命令说明清晰):

# 1) 创建Headless Service
kubectl apply -f /opt/k8s/mysql-hl.yaml

# 2) 创建StatefulSet
kubectl apply -f /opt/k8s/mysql-sts.yaml

# 3) 查看Pod与PVC绑定关系(预期每个Pod对应一个PVC)
kubectl get pods -l app=mysql -o wide
kubectl get pvc -l app=mysql -o wide

# 4) 验证DNS解析(从集群内访问)
kubectl run -it --rm dns-test --image=busybox:1.36 -- sh
# 在容器内执行:
nslookup mysql-0.mysql-hl

扩缩容与有序性验证:

# 将副本扩容到3,观察创建顺序 mysql-2
kubectl scale sts/mysql --replicas=3
kubectl get pods -l app=mysql -w

# 缩容回2,按序删除 mysql-2
kubectl scale sts/mysql --replicas=2

滚动更新与灰度发布示例:

# 将partition设为1,仅更新序号>=1的Pod
kubectl patch sts/mysql -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":1}}}}'

# 更新镜像
kubectl set image sts/mysql mysql=mysql:8.0.36

# 查看更新进度
kubectl rollout status sts/mysql
kubectl get pods -l app=mysql -o wide

排错清单与命令(问题->排查命令->结论):
- PVC Pending:

kubectl get pvc
kubectl describe pvc data-mysql-0
kubectl get sc

结论:检查StorageClass是否存在、Provisioner是否就绪、配额是否充足。

  • Pod无法挂载卷:
kubectl describe pod mysql-0
kubectl get events --sort-by=.metadata.creationTimestamp | tail -n 20

结论:查看CSI节点插件日志、节点是否可达、存储后端权限。

  • 有序更新过慢:
kubectl get pod mysql-0 -o jsonpath='{.status.containerStatuses[0].ready}{"\n"}'
kubectl describe pod mysql-0 | sed -n '/Readiness probe/,+5p'

结论:优化探针、启动耗时或调整podManagementPolicyParallel

持久化应用实践要点(简明可执行):
- 应用内部复制优先:MySQL主从、Redis哨兵/集群与RWO卷组合。
- 避免多副本写入同一RWX存储,降低写入冲突风险。
- 初始化数据使用initContainers,确保目录权限与基础数据一致。
- 配合Pod反亲和与拓扑约束提升可用性。

练习(含目标与验证):
1) 将上例MySQL从2扩容到3并验证mysql-2的PVC独立性。
验证命令:

kubectl get pvc | grep data-mysql
kubectl exec -it mysql-2 -- sh -c "ls -l /var/lib/mysql | head"

2) 模拟Pod删除并验证数据持久性。
步骤:

kubectl delete pod mysql-0
kubectl wait --for=condition=Ready pod/mysql-0 --timeout=120s
kubectl exec -it mysql-0 -- sh -c "ls -l /var/lib/mysql | head"

预期:数据目录仍存在,PVC未变化。

3) 将podManagementPolicy改为Parallel并观察扩容时序。
验证命令:

kubectl patch sts/mysql -p '{"spec":{"podManagementPolicy":"Parallel"}}'
kubectl scale sts/mysql --replicas=4
kubectl get pods -l app=mysql -w