Kubernetes(K8s)学习笔记(第十四期):集群存储与有状态应用(下篇):StatefulSet 有状态应用管理

Kubernetes(K8s)学习笔记(第十四期):集群存储与有状态应用(下篇):StatefulSet 有状态应用管理

本笔记为 Kubernetes 系列第十四期,聚焦有状态应用的管理。涵盖:StatefulSet 核心特性、Headless Service 与固定 DNS、PVC 模板自动创建存储、Nginx/etcd/Redis/MySQL 集群部署实践。所有命令和 YAML 示例均已经过整理和注释。全文约 4700 字 ,包含 16 个 YAML 示例55+ 命令示例12 张对比表格,是 Kubernetes 有状态应用管理的完整指南。
知识衔接:

Kubernetes(K8s)学习笔记(第十三期):集群存储与有状态应用(上篇):动态卷供应------StorageClass + Provisioner

更多kubernetes系列知识:kubernetes从入门到进阶

我的主页:AOwhisky,这里有更多运维系统性知识整理和其他有趣内容,欢迎与我一起探讨学习~
--- Compiled and Authored by Whisky --- July 4 th, 2026

目录

  1. 引子:从"无状态"到"有状态"
  2. 无状态 vs 有状态应用
  3. StatefulSet 核心特性
  4. StatefulSet vs Deployment 对比
  5. 实验一:部署 Nginx 集群
  6. 实验二:部署 etcd 集群
  7. 实验三:部署 Redis 集群(主从 + 哨兵)
  8. 实验四:部署 MySQL 集群(主从同步)
  9. StatefulSet 更新策略
  10. StatefulSet Pod 管理
  11. 总结与知识点一览表

引子:从"无状态"到"有状态"

在前三期(ResourceQuota + LimitRange、HPA、动态卷供应)中,我们完成了集群资源管理和存储供应的基础建设。现在,我们面临一个更重要的问题:如何管理有状态应用?

在 Kubernetes 中,Deployment 是管理无状态应用的利器------所有 Pod 完全相同,可以随意扩缩容、滚动更新、重启重建,无需担心数据丢失或身份变化。但现实世界中,大多数应用都是有状态的:

  • 数据库:每个节点有独立的存储,主从身份不能混淆
  • 分布式中间件:每个节点有固定的网络标识,集群需要按顺序启动
  • 消息队列:节点间需要稳定的通信地址

这些应用需要:

  • 固定的网络标识:Pod 重启后 IP 会变化,但 hostname 必须保持不变
  • 独立的持久化存储:每个 Pod 有自己的数据卷,不能共享
  • 有序的启动和停止:主节点先启动,从节点后启动;缩容时从后往前删除

StatefulSet 正是为这些需求而设计的。

一句话概括本期:StatefulSet 让有状态应用在 Kubernetes 上跑得像无状态应用一样丝滑------固定身份、稳定存储、有序操作,三大特性缺一不可。

一、无状态 vs 有状态应用

1.1 核心区别

维度 无状态应用(Stateless) 有状态应用(Stateful)
核心特征 不保存任何请求/会话数据,每次请求独立 依赖持久化的会话/数据,请求间有依赖关系
数据存储 数据仅在请求期间存在,不落地 数据需持久化,依赖固定存储
实例一致性 所有实例完全相同,可随意扩缩容/替换 实例有身份(如编号),启动/扩缩容有顺序要求
网络标识 共享 IP/域名,无固定网络标识 有固定网络标识(如 Headless Service + DNS)
典型应用 Web 服务、API 网关、静态页面 数据库、Redis、ZooKeeper、Kafka

1.2 为什么无状态应用优先推荐?

无状态应用是云原生的最佳实践:

  • 实例可随意替换,故障自愈能力强
  • 扩缩容无需等待数据迁移,秒级完成
  • 滚动更新简单,无需考虑顺序

最佳实践:尽可能将应用拆分为"无状态业务层 + 有状态数据层",既提升扩展能力,又保障数据安全。

二、StatefulSet 核心特性

2.1 三大核心特性

特性 说明
固定身份标识 每个 Pod 拥有唯一的序号(从 0 开始递增),hostname 固定为「StatefulSet 名称-序号」
有序操作 部署/扩容按 0→N-1 顺序创建;缩容/删除按 N-1→0 逆序删除;更新按 N-1→0 逆序滚动
稳定存储 通过 PVC 模板为每个 Pod 自动创建独立的 PVC,Pod 重建后自动复用原 PVC

2.2 固定网络标识

StatefulSet 必须关联一个 Headless Service(ClusterIP: None),为每个 Pod 提供固定的 DNS 解析:

text 复制代码
<statefulset-name>-<ordinal>.<service-name>.<namespace>.svc.cluster.local

示例 :StatefulSet nginx,Service nginx,Namespace default

  • nginx-0.nginx.default.svc.cluster.local
  • nginx-1.nginx.default.svc.cluster.local
  • nginx-2.nginx.default.svc.cluster.local

Pod 名称的稳定来源

StatefulSet 中 Pod 的名称由两部分组成:<statefulset-name>-<ordinal>。这个名称是稳定且可预测的,不随 Pod 重启或重建而改变。

  • ordinal 从 0 开始递增,永不重复
  • Pod 重建后,ordinal 保持不变
  • 通过 metadata.name 字段,Pod 可以获取自己的名称

环境变量获取 Pod 名称

yaml 复制代码
env:
- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

2.3 有序操作详解

操作 顺序 说明
创建/扩容 0 → 1 → 2 ... 前一个 Pod 必须处于 Running 和 Ready 状态,才会创建下一个
删除/缩容 ... → 2 → 1 → 0 前一个 Pod 彻底删除后,才会删除下一个
滚动更新 ... → 2 → 1 → 0 默认从最后一个序号开始更新(逆序)

为什么需要有序操作?

有状态应用通常有启动依赖关系:

  • 主节点(ordinal=0)必须先启动,初始化集群
  • 从节点(ordinal>0)依赖主节点的网络地址加入集群
  • 如果无序启动,从节点可能无法找到主节点

StatefulSet 的有序操作保证了:

  1. 启动顺序:确保集群先有"主人",再有"随从"
  2. 终止顺序:缩容时先删除"随从",再删除"主人",避免数据丢失
  3. 更新顺序:从最后一个节点开始更新,保持集群最小可用性

2.4 稳定存储

PVC 模板(volumeClaimTemplates)

  • 为每个 Pod 自动创建独立的 PVC
  • PVC 命名格式:<template-name>-<statefulset-name>-<ordinal>
  • Pod 删除后,PVC 和 PV 仍然保留,数据不丢失
  • Pod 重建后自动复用原 PVC

示例 :StatefulSet nginx,PVC 模板 nginx-data

  • nginx-data-nginx-0
  • nginx-data-nginx-1
  • nginx-data-nginx-2

重要 :删除 StatefulSet 时,PVC 不会自动删除。如需清理数据,需要手动删除 PVC。

三、StatefulSet vs Deployment 对比

控制器 StatefulSet Deployment
适用场景 有状态应用(数据库、分布式集群、消息队列) 无状态应用(Web 服务、API 接口、静态服务)
网络 Headless Service(固定 DNS 解析) ClusterIP/LoadBalancer(共享 IP)
存储 必须 PVC + PV(固定存储卷绑定) 可选 PVC(无数据持久化要求)
扩缩容 按实例编号顺序扩缩容(如从 0→1→2) 无顺序,瞬间完成
更新策略 有序更新(如从最后一个实例开始) 滚动更新/重建,无顺序
重启/重建 实例重启后需恢复原有数据/身份 实例重启后无影响

四、实验一:部署 Nginx 集群

需求:部署 3 节点 Nginx 集群,实现数据持久化、固定网络标识、有序部署。

4.1 环境准备

bash 复制代码
root@master30:~# kubectl create ns statefulset
root@master30:~# kubectl config set-context --current --namespace statefulset

4.2 创建 Headless Service

yaml 复制代码
root@master30:~# cat > nginx-service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: statefulset
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    name: nginx-port
EOF

root@master30:~# kubectl apply -f nginx-service.yaml
root@master30:~# kubectl get svc
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   None         <none>        80/TCP    5s

4.3 创建 StatefulSet

yaml 复制代码
root@master30:~# cat > nginx-statefulset.yaml <<'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
  namespace: statefulset
spec:
  serviceName: nginx
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: nginx-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "nfs-storage"   # 使用上一期部署的 NFS Provisioner
      resources:
        requests:
          storage: 10Gi
EOF

root@master30:~# kubectl apply -f nginx-statefulset.yaml

4.4 观察 Pod 创建顺序

bash 复制代码
root@master30:~# kubectl get pods -w
NAME       READY   STATUS    RESTARTS   AGE
nginx-0    1/1     Running   0          10s
nginx-1    1/1     Running   0          30s
nginx-2    1/1     Running   0          50s

关键观察 :Pod 按 nginx-0nginx-1nginx-2 顺序创建,每个 Pod 间隔约 20 秒。

4.5 验证 PVC

bash 复制代码
root@master30:~# kubectl get pvc
NAME                 STATUS   VOLUME                                     CAPACITY
nginx-data-nginx-0   Bound    pvc-8eb06bf1-a5de-4e94-bf83-514941f73c9f   10Gi
nginx-data-nginx-1   Bound    pvc-e853eccd-8060-4326-8c45-0b88c34a3d97   10Gi
nginx-data-nginx-2   Bound    pvc-27d944f7-1e62-4352-9023-3874e6867ef9   10Gi

4.6 验证固定 DNS

bash 复制代码
root@master30:~# kubectl run test --image=nginx -it -- bash
root@test:/# curl nginx-0.nginx
nginx-data-nginx-0
root@test:/# curl nginx-1.nginx
nginx-data-nginx-1
root@test:/# curl nginx-2.nginx
nginx-data-nginx-2

4.7 验证数据持久化

bash 复制代码
# 删除 nginx-2
root@master30:~# kubectl delete pod nginx-2

# 等待重新创建
root@master30:~# kubectl get pods
NAME       READY   STATUS    RESTARTS   AGE
nginx-0    1/1     Running   0          10m
nginx-1    1/1     Running   0          10m
nginx-2    1/1     Running   0          8s

# 验证数据仍然存在
root@master30:~# kubectl exec test -- curl nginx-2.nginx
nginx-data-nginx-2

4.8 实验小结

  • StatefulSet 保证 Pod 按序号从 0 到 N-1 顺序创建
  • 每个 Pod 获得独立的 PVC,数据持久化
  • Pod 重建后 DNS 名称不变,数据不丢失
  • Headless Service 为每个 Pod 提供固定 DNS 解析

4.9 清理环境

bash 复制代码
root@master30:~# kubectl delete sts nginx
root@master30:~# kubectl delete svc nginx
root@master30:~# kubectl delete pvc nginx-data-nginx-{0..2}

五、实验二:部署 etcd 集群

需求:部署 3 节点 etcd 集群,实现数据持久化、固定网络标识、集群自动组网。

5.1 准备 NFS 存储

bash 复制代码
root@master30:~# mkdir -m 777 -p /shares/etcd/data-{0..2}
root@master30:~# systemctl restart nfs-server.service

5.2 创建 Headless Service

yaml 复制代码
root@master30:~# cat > etcd-service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: etcd
  namespace: statefulset
spec:
  clusterIP: None
  selector:
    app: etcd
  ports:
  - port: 2379
    name: client
  - port: 2380
    name: peer
EOF

root@master30:~# kubectl apply -f etcd-service.yaml

5.3 创建 PV(静态方式)

yaml 复制代码
root@master30:~# cat > etcd-pv.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: etcd-pv-0
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: etcd-storage
  nfs:
    server: 10.1.8.30
    path: /shares/etcd/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: etcd-pv-1
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: etcd-storage
  nfs:
    server: 10.1.8.30
    path: /shares/etcd/data-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: etcd-pv-2
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: etcd-storage
  nfs:
    server: 10.1.8.30
    path: /shares/etcd/data-2
EOF

root@master30:~# kubectl apply -f etcd-pv.yaml

提示:生产环境建议使用动态卷供应(StorageClass),而非手动创建 PV。

5.4 创建 StatefulSet

yaml 复制代码
root@master30:~# cat > etcd-statefulset.yaml <<'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
  namespace: statefulset
spec:
  serviceName: etcd
  replicas: 3
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      containers:
      - name: etcd
        image: registry.aliyuncs.com/google_containers/etcd:3.5.10-0
        command:
        - /usr/local/bin/etcd
        ports:
        - containerPort: 2379
          name: client
        - containerPort: 2380
          name: peer
        env:
        - name: ETCD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: ETCD_DATA_DIR
          value: /var/lib/etcd
        - name: ETCD_INITIAL_ADVERTISE_PEER_URLS
          value: "http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2380"
        - name: ETCD_LISTEN_PEER_URLS
          value: "http://0.0.0.0:2380"
        - name: ETCD_LISTEN_CLIENT_URLS
          value: "http://0.0.0.0:2379"
        - name: ETCD_ADVERTISE_CLIENT_URLS
          value: "http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2379"
        - name: ETCD_INITIAL_CLUSTER
          value: "etcd-0=http://etcd-0.etcd.statefulset.svc.cluster.local:2380,etcd-1=http://etcd-1.etcd.statefulset.svc.cluster.local:2380,etcd-2=http://etcd-2.etcd.statefulset.svc.cluster.local:2380"
        - name: ETCD_INITIAL_CLUSTER_TOKEN
          value: "etcd-token"
        - name: ETCD_INITIAL_CLUSTER_STATE
          value: "new"
        volumeMounts:
        - name: etcd-data
          mountPath: /var/lib/etcd
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
  volumeClaimTemplates:
  - metadata:
      name: etcd-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "etcd-storage"
      resources:
        requests:
          storage: 10Gi
EOF

root@master30:~# kubectl apply -f etcd-statefulset.yaml

5.5 验证 etcd 集群状态

bash 复制代码
root@master30:~# kubectl exec etcd-0 -- etcdctl member list
8747632212a8465, started, etcd-2, http://etcd-2.etcd.statefulset.svc.cluster.local:2380, http://etcd-2.etcd.statefulset.svc.cluster.local:2379, false
881e7c876792f042, started, etcd-0, http://etcd-0.etcd.statefulset.svc.cluster.local:2380, http://etcd-0.etcd.statefulset.svc.cluster.local:2379, false
b6d311da39fc1138, started, etcd-1, http://etcd-1.etcd.statefulset.svc.cluster.local:2380, http://etcd-1.etcd.statefulset.svc.cluster.local:2379, false

5.6 验证数据持久化

bash 复制代码
# 写入数据
root@master30:~# kubectl exec etcd-0 -- etcdctl put /users/user1/name laowang

# 删除 etcd-2
root@master30:~# kubectl delete pod etcd-2

# 等待重建后验证
root@master30:~# kubectl exec etcd-2 -- etcdctl get /users/user1/name
/users/user1/name
laowang

5.7 实验小结

  • etcd 集群通过 StatefulSet 实现了固定网络标识
  • 每个 etcd 节点通过 ${ETCD_NAME} 环境变量获取自己的名称
  • ETCD_INITIAL_CLUSTER 使用固定的 DNS 名称,实现集群自动组网
  • 删除 Pod 后数据不丢失(PVC 保留)

5.8 清理环境

bash 复制代码
root@master30:~# kubectl delete sts etcd
root@master30:~# kubectl delete svc etcd
root@master30:~# kubectl delete pvc etcd-data-etcd-{0..2}
root@master30:~# kubectl delete pv etcd-pv-{0..2}

六、实验三:部署 Redis 集群(主从 + 哨兵)

需求:部署 3 节点 Redis 集群(1 主 2 从),实现固定网络标识、数据持久化、哨兵高可用。

6.1 准备 NFS 存储

bash 复制代码
root@master30:~# mkdir -m 777 -p /shares/redis/data-{0..2}
root@master30:~# systemctl restart nfs-server.service

6.2 创建 Headless Service

yaml 复制代码
root@master30:~# cat > redis-service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: statefulset
spec:
  clusterIP: None
  selector:
    app: redis
  ports:
  - port: 6379
    name: redis
  - port: 26379
    name: sentinel
EOF

root@master30:~# kubectl apply -f redis-service.yaml

6.3 创建 PV

yaml 复制代码
root@master30:~# cat > redis-pv.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: redis-pv-0
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: redis-storage
  nfs:
    server: 10.1.8.30
    path: /shares/redis/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: redis-pv-1
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: redis-storage
  nfs:
    server: 10.1.8.30
    path: /shares/redis/data-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: redis-pv-2
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: redis-storage
  nfs:
    server: 10.1.8.30
    path: /shares/redis/data-2
EOF

root@master30:~# kubectl apply -f redis-pv.yaml

6.4 创建 StatefulSet

yaml 复制代码
root@master30:~# cat > redis-statefulset.yaml <<'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: statefulset
spec:
  serviceName: redis
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7.0-alpine
        ports:
        - containerPort: 6379
          name: redis
        - containerPort: 26379
          name: sentinel
        command: ["/bin/sh", "-c"]
        args:
        - |
          NODE_ID=$(hostname | awk -F'-' '{print $2}')
          cat > /data/redis.conf << EOF
          bind 0.0.0.0
          protected-mode no
          port 6379
          dir /data
          appendonly yes
          requirepass "redis123"
          masterauth "redis123"
          EOF
          if [ "$NODE_ID" != "0" ]; then
            echo "replicaof redis-0.redis.statefulset.svc.cluster.local 6379" >> /data/redis.conf
          fi
          redis-server /data/redis.conf &
          until nslookup redis-0.redis.statefulset.svc.cluster.local; do
            echo "Waiting for DNS resolution..."
            sleep 2
          done
          cat > /data/sentinel.conf << EOF
          bind 0.0.0.0
          protected-mode no
          port 26379
          sentinel resolve-hostnames yes
          sentinel announce-hostnames yes
          sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2
          sentinel auth-pass mymaster redis123
          sentinel down-after-milliseconds mymaster 5000
          sentinel failover-timeout mymaster 10000
          EOF
          redis-sentinel /data/sentinel.conf
        volumeMounts:
        - name: redis-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "redis-storage"
      resources:
        requests:
          storage: 10Gi
EOF

root@master30:~# kubectl apply -f redis-statefulset.yaml

6.5 验证主从状态

bash 复制代码
root@master30:~# kubectl exec redis-0 -- redis-cli -a redis123 info replication
Warning: Using a password with '-a' option may not be safe.
# Replication
role:master
connected_slaves:2
slave0:ip=10.224.113.181,port=6379,state=online,lag=1
slave1:ip=10.224.19.24,port=6379,state=online,lag=1

6.6 验证哨兵状态

bash 复制代码
root@master30:~# kubectl exec redis-0 -- redis-cli -p 26379 sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "redis-0.redis.statefulset.svc.cluster.local"
 5) "port"
 6) "6379"

6.7 实验小结

  • 使用 hostname | awk -F'-' '{print $2}' 获取 Pod 序号,决定主从角色
  • redis-0 固定为主节点,其他节点为从节点
  • 哨兵配置 sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2 监控主节点
  • sentinel resolve-hostnames yes 启用 hostname 解析,确保哨兵能通过 DNS 名称找到主节点

6.8 清理环境

bash 复制代码
root@master30:~# kubectl delete sts redis
root@master30:~# kubectl delete svc redis
root@master30:~# kubectl delete pvc redis-data-redis-{0..2}
root@master30:~# kubectl delete pv redis-pv-{0..2}

七、实验四:部署 MySQL 集群(主从同步)

需求:部署 2 节点 MySQL 集群(1 主 1 从),实现数据持久化、固定网络标识、主从自动同步。

7.1 准备 NFS 存储

bash 复制代码
root@master30:~# mkdir -m 777 -p /shares/mysql/data-{0,1}
root@master30:~# systemctl restart nfs-server.service

7.2 创建 Headless Service

yaml 复制代码
root@master30:~# cat > mysql-service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: statefulset
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
    name: mysql-port
EOF

root@master30:~# kubectl apply -f mysql-service.yaml

7.3 创建 PV

yaml 复制代码
root@master30:~# cat > mysql-pv.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-0
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mysql-storage
  nfs:
    server: 10.1.8.30
    path: /shares/mysql/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-1
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mysql-storage
  nfs:
    server: 10.1.8.30
    path: /shares/mysql/data-1
EOF

root@master30:~# kubectl apply -f mysql-pv.yaml

7.4 创建 StatefulSet

yaml 复制代码
root@master30:~# cat > mysql-statefulset.yaml <<'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: statefulset
spec:
  serviceName: mysql
  replicas: 2
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "mysql-storage"
      resources:
        requests:
          storage: 10Gi
EOF

root@master30:~# kubectl apply -f mysql-statefulset.yaml

7.5 验证 Pod 域名解析

bash 复制代码
root@master30:~# kubectl run test-pod --image=busybox -- sleep 3600
root@master30:~# kubectl exec test-pod -- ping -c2 mysql-1.mysql
PING mysql-1.mysql (10.224.113.148): 56 data bytes
64 bytes from 10.224.113.148: seq=0 ttl=62 time=1.586 ms

7.6 验证数据持久化

bash 复制代码
# 创建数据库
root@master30:~# kubectl exec -it mysql-1 -- mysql -u root -p123456 -e 'create database wordpress;'

# 删除 mysql-1
root@master30:~# kubectl delete pod mysql-1

# 等待重建后验证数据
root@master30:~# kubectl exec -it mysql-1 -- mysql -u root -p123456 -e 'show databases;'
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| wordpress          |
+--------------------+

7.7 实验小结

  • MySQL 主从同步需要额外配置,本实验主要演示 StatefulSet 的数据持久化能力
  • 删除 Pod 后数据不丢失,验证了 PVC 的持久化效果
  • 固定 DNS 名称使主从配置更为稳定

7.8 清理环境

bash 复制代码
root@master30:~# kubectl delete sts mysql
root@master30:~# kubectl delete svc mysql
root@master30:~# kubectl delete pvc mysql-data-mysql-{0..1}
root@master30:~# kubectl delete pv mysql-pv-{0..1}

八、StatefulSet 更新策略

8.1 更新策略类型

策略 行为 适用场景
RollingUpdate(默认) 从最后一个序号开始逆序滚动更新 大多数场景
OnDelete 只有手动删除 Pod 时才更新 需要完全控制更新节奏

8.2 配置更新策略

yaml 复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2   # 只更新序号 >= 2 的 Pod

partition 参数

  • 用于金丝雀发布或灰度更新
  • 序号 >= partition 的 Pod 会更新
  • 序号 < partition 的 Pod 保持不变

九、StatefulSet Pod 管理

9.1 Pod 替换

bash 复制代码
# 删除指定 Pod(会触发重建,且数据保留)
kubectl delete pod <pod-name>

9.2 获取 Pod 状态

bash 复制代码
kubectl get sts
kubectl get pods -l app=<label>
kubectl describe sts <name>

9.3 扩缩容

bash 复制代码
# 扩容
kubectl scale sts <name> --replicas=5

# 缩容(从最大序号开始删除)
kubectl scale sts <name> --replicas=2

十、总结与知识点一览表

10.1 StatefulSet 核心特性总结

特性 说明 关键配置
固定身份 每个 Pod 有唯一序号和固定 hostname Headless Service + serviceName
有序操作 创建/更新按顺序,缩容逆序 由 StatefulSet 控制器自动保证
稳定存储 每个 Pod 独立 PVC,删除后保留 volumeClaimTemplates
固定 DNS Pod 可通过 <name>.<service>.<ns>.svc.cluster.local 访问 Headless Service

10.2 StatefulSet vs Deployment 对比

控制器 StatefulSet Deployment
适用场景 有状态应用 无状态应用
网络 Headless Service(固定 DNS) ClusterIP/LoadBalancer(共享 IP)
存储 必须 PVC + PV 可选 PVC
扩缩容 有序(0→1→2) 无序
更新策略 有序更新(逆序) 滚动更新

10.3 各实验对比

实验 应用 Pod 数量 主要特点
实验一 Nginx 3 演示基础 StatefulSet 特性
实验二 etcd 3 集群自动组网,固定 DNS
实验三 Redis 3 主从 + 哨兵高可用
实验四 MySQL 2 数据持久化验证

10.4 常用命令速查

操作 命令
创建 StatefulSet kubectl apply -f sts.yaml
查看 StatefulSet kubectl get sts
查看 StatefulSet 详情 kubectl describe sts <name>
查看 Pod 顺序 kubectl get pods -w
查看 PVC kubectl get pvc
扩缩容 StatefulSet kubectl scale sts <name> --replicas=<n>
删除 StatefulSet kubectl delete sts <name>

10.5 常见错误排查

错误 原因 解决方法
Pod 一直 Pending PVC 无法绑定或节点资源不足 检查 PV 状态和节点资源
创建顺序异常 Headless Service 未正确配置 确认 clusterIP: None
数据丢失 PVC 被误删 使用 Retain 回收策略
DNS 解析失败 Service 名称不匹配 确认 serviceName 与 Service 名称一致
主从同步失败 网络不通或配置错误 检查 Pod 间网络连通性

10.6 生产环境最佳实践

实践 说明
使用动态卷供应 避免手动创建 PV,使用 StorageClass 自动管理
设置资源限制 为有状态应用配置合理的 CPU/内存请求和限制
合理设置副本数 生产环境至少 3 节点(满足 etcd/Redis 多数派要求)
定期备份数据 有状态应用的数据是关键资产,需定期备份
使用 Retain 回收策略 防止误删 PVC 导致数据丢失
配置 PodDisruptionBudget 保护有状态应用不被同时驱逐

笔记完结说明:至此,Kubernetes 系列笔记(第一期至第十四期)已全部完成,覆盖从基础架构、集群部署、容器管理、网络策略、调度管理、资源管理、存储管理到有状态应用的全链路内容。建议读者按照"先理解架构 → 动手部署集群 → 掌握日常运维 → 进阶存储与有状态应用"的路径循序渐进地学习。如有后续进阶内容(如服务网格 Istio、Kubernetes 安全策略、GitOps 持续部署等),可根据需要继续扩展。感谢学习!
--- Compiled and Authored by Whisky --- July 4 th, 2026

相关推荐
城管不管3 小时前
MySQL 慢查询完整排查
java·服务器·jvm·数据库·mysql·spring·面试
树码小子3 小时前
MySQL 的事务隔离级别(重点面试题)
数据库·mysql
01_ice3 小时前
MySQL表的约束
数据库·sql·mysql
北漂燕郊杨哥4 小时前
Win11 环境下 phpStudy 安装 Redis 最新版,并为 PHP 8.5.9(NTS)配置 redis 扩展
数据库·redis·php
bitbrowser12 小时前
giffgaff突然停用?先分清闲置停用和长期境外断开
笔记
九硕智慧建筑一体化厂家13 小时前
数字化管控落地,直流照明构筑安全照明体系
运维·人工智能·笔记·安全·智慧城市
AI云海13 小时前
天幕红尘笔记
笔记
heimeiyingwang14 小时前
【架构实战】Kubernetes存储实战:从EmptyDir到Ceph CSI的持久化存储选型指南
ceph·架构·kubernetes
J_bean15 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean15 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定