K8s学习笔记(十三) StatefulSet

在 Kubernetes 中,StatefulSet 是专门为 "有状态应用" 设计的工作负载控制器。与管理无状态应用的 Deployment 不同,它能为 Pod 提供 稳定的身份标识专属的持久化存储有序的操作流程,完美适配数据库集群(如 MySQL、MongoDB)、分布式系统(如 Kafka、ZooKeeper)等需要固定身份和独立数据的场景。

1 什么是 "有状态应用"?为什么需要 StatefulSet?

1.1 有状态应用的 3 个核心需求

无状态应用(如 Nginx、API 服务)的 Pod 可以随意替换:名称随机(如nginx-7f9b8f4d9-2xq3p)、IP 动态变化、数据无需持久化或可共享,用 Deployment 管理即可。

有状态应用(如 MySQL 主从、ZooKeeper 集群)有 3 个特殊需求,Deployment 无法满足:

  • 稳定的身份 :每个实例需要固定的名称(如zk-0zk-1)和网络标识,其他服务需通过固定标识访问(例如 "主库必须是mysql-0")。
  • 专属的存储 :每个实例的数据必须独立(如mysql-0的数据不能被mysql-1覆盖),且 Pod 重建后数据必须保留。
  • 有序的操作 :部署时需先启动zk-0,再启动zk-1;扩容时需按顺序新增;更新时需逐个替换,避免集群故障。

1.2 StatefulSet 的核心价值

StatefulSet 正是为解决这些痛点而生,它为每个 Pod 提供:

  • 固定不变的身份(名称、DNS 域名);
  • 专属的持久化存储(每个 Pod 绑定独立的 PVC);
  • 严格的操作顺序(部署 / 扩容 / 更新 / 删除都按序号执行)。

2 StatefulSet 的核心特性(必懂)

2.1 稳定的网络标识(固定名称 + DNS)

  • 固定的 Pod 名称 :StatefulSet 的 Pod 名称格式为 [StatefulSet名称]-[序号](如web-0web-1web-2),序号从 0 开始,唯一且固定(即使 Pod 重建,名称和序号也不变)。

  • 稳定的 DNS 记录:需配合Headless Service(无头服务)使用。每个 Pod 会被分配一个固定的 DNS 域名:

    plaintext 复制代码
    <pod名称>.<无头服务名称>.<命名空间>.svc.cluster.local

    例如:web-0.nginx-service.default.svc.cluster.local。其他 Pod 可通过该域名稳定访问特定实例(无需关心 IP 变化)。

2.2 稳定的持久化存储(专属 PVC)

通过 volumeClaimTemplates(PVC 模板)为每个 Pod 自动创建专属的 PVC ,格式为 [模板名称]-[Pod名称](如data-web-0data-web-1)。

  • 每个 PVC 绑定独立的 PV,确保数据隔离(web-0的数据不会被web-1访问)。
  • 即使 Pod 被删除重建,新 Pod 仍会挂载原 PVC,数据不丢失。

2.3 有序的操作流程(按序号执行)

StatefulSet 的所有操作(部署、扩容、更新、删除)都按 "序号升序 / 降序" 严格执行,确保集群稳定性:

  • 部署 :先创建web-0,成功后再创建web-1,以此类推。
  • 扩容 :新增实例时,按序号递增(如当前有web-0web-1,新增web-2)。
  • 更新 :默认按序号降序逐个更新(先更新web-2,成功后再更新web-1,最后web-0),避免集群中断。
  • 删除 :按序号降序逐个删除(先删web-2,再删web-1,最后web-0)。

3 StatefulSet 的关键组件(依赖项)

StatefulSet 不能独立工作,必须配合两个核心组件:

3.1 Headless Service(无头服务)

作用:为 StatefulSet 的 Pod 提供稳定的 DNS 解析(核心!)。

与普通 Service 的区别:Headless Service 没有 ClusterIP,它会为每个关联的 Pod 创建 DNS A 记录(pod名称.服务名称),让其他 Pod 能通过域名访问特定实例。

示例(创建 Headless Service):

yaml 复制代码
# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service  # 服务名称,用于DNS解析
spec:
  selector:
    app: nginx  # 匹配StatefulSet的Pod标签
  ports:
  - port: 80
    targetPort: 80
  clusterIP: None  # 关键:设为None,即无头服务

3.2 VolumeClaimTemplate(PVC 模板)

作用:为每个 Pod 自动创建专属的 PVC(无需手动创建),确保数据独立存储。

模板中定义 PVC 的需求(容量、访问模式、存储类),StatefulSet 会为每个 Pod 生成对应的 PVC。

示例(PVC 模板片段):

yaml 复制代码
volumeClaimTemplates:
- metadata:
    name: data  # 模板名称,生成的PVC格式为data-<pod名称>
  spec:
    accessModes: [ "ReadWriteOnce" ]
    resources:
      requests:
        storage: 1Gi  # 每个Pod分配1Gi存储
    storageClassName: "standard"  # 关联的存储类(需提前创建)

4 实操案例:部署一个 StatefulSet(Nginx 示例)

以 Nginx 为例(虽然 Nginx 是无状态的,但可用于演示 StatefulSet 的特性),完整步骤如下:

步骤 1:创建 Headless Service

yaml 复制代码
# 保存为headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
  clusterIP: None  # 无头服务
bash 复制代码
kubectl apply -f headless-service.yaml

步骤 2:创建 StatefulSet

yaml 复制代码
# 保存为statefulset-nginx.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web  # StatefulSet名称,用于生成Pod名称
spec:
  serviceName: "nginx-service"  # 关联的Headless Service名称(必须正确)
  replicas: 3  # 3个实例
  selector:
    matchLabels:
      app: nginx  # 匹配Pod的标签
  template:
    metadata:
      labels:
        app: nginx  # Pod标签,需与Service的selector一致
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: data  # 挂载名为data的卷(与PVC模板名称一致)
          mountPath: /usr/share/nginx/html  # 挂载到容器内的目录
  # PVC模板:为每个Pod自动创建PVC
  volumeClaimTemplates:
  - metadata:
      name: data  # 卷名称,与volumeMounts.name一致
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi  # 每个Pod请求1Gi存储
      storageClassName: "standard"  # 需提前创建名为standard的StorageClass(或用现有)
bash 复制代码
kubectl apply -f statefulset-nginx.yaml

步骤 3:验证 StatefulSet 的特性

1. 查看 Pod(固定名称 + 有序部署)
bash 复制代码
kubectl get pods -l app=nginx

输出(名称为web-0web-1web-2,按序号创建):

plaintext 复制代码
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          2m
web-1   1/1     Running   0          1m50s  # web-0成功后才创建
web-2   1/1     Running   0          1m40s  # web-1成功后才创建
2. 查看自动创建的 PVC(专属存储)
bash 复制代码
kubectl get pvc

输出(每个 Pod 对应一个 PVC,名称为data-web-0data-web-1data-web-2):

plaintext 复制代码
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-web-0     Bound    pvc-xxx-xxx                               1Gi        RWO            standard       2m
data-web-1     Bound    pvc-yyy-yyy                               1Gi        RWO            standard       1m50s
data-web-2     Bound    pvc-zzz-zzz                               1Gi        RWO            standard       1m40s
3. 验证稳定的 DNS 解析(通过 Headless Service)

在集群内任意 Pod 中,通过 DNS 域名访问web-0

bash 复制代码
# 启动一个测试Pod
kubectl run -it --rm test --image=busybox:1.35 -- sh

# 在测试Pod中执行(解析web-0的IP)
nslookup web-0.nginx-service

输出(能稳定解析到web-0的 IP,即使web-0重建,域名也不变):

plaintext 复制代码
Name:      web-0.nginx-service
Address 1: 10.244.1.5 web-0.nginx-service.default.svc.cluster.local
4. 验证 Pod 重建后身份和存储不变

删除web-0,观察重建后的 Pod:

bash 复制代码
# 删除web-0
kubectl delete pod web-0

# 查看重建的Pod
kubectl get pods -l app=nginx

输出(新 Pod 仍叫web-0,且挂载原 PVCdata-web-0,数据不丢失):

plaintext 复制代码
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          30s  # 名称不变
web-1   1/1     Running   0          3m
web-2   1/1     Running   0          2m50s

5 StatefulSet vs Deployment(核心区别)

特性 StatefulSet Deployment
应用类型 有状态应用(数据库、分布式系统) 无状态应用(Web 服务、API)
Pod 名称 固定格式(名称-序号,如web-0 随机名称(如nginx-7f9b8f4d9-2xq3p
网络标识 稳定 DNS 域名(依赖 Headless Service) 依赖普通 Service(通过 ClusterIP 访问)
存储 专属 PVC(通过 volumeClaimTemplates) 共享 Volume 或无持久化
操作顺序 按序号有序部署 / 扩容 / 更新 / 删除 并行操作(无顺序)
身份稳定性 重建后名称、存储、网络标识不变 重建后名称、IP 全变

6 常见问题与注意事项

  1. StatefulSet 的 Pod 一直 Pending
    • 原因:volumeClaimTemplates中请求的 PVC 无法绑定 PV(如 StorageClass 不存在、PV 容量不足)。
    • 排查:kubectl describe pvc data-web-0(查看具体 PVC 的绑定失败原因)。
  2. DNS 解析失败
    • 原因:Headless Service 的selector与 StatefulSet 的 Pod 标签不匹配,或 CoreDNS 未正常运行。
    • 排查:kubectl describe service nginx-service(确认 selector 正确);检查 CoreDNS 状态(kubectl get pods -n kube-system | grep coredns)。
  3. 更新 StatefulSet 时集群不可用
    • 解决:调整更新策略(spec.updateStrategy.rollingUpdate.partition),控制更新范围;或使用OnDelete策略(手动删除旧 Pod 才更新)。

7 总结

StatefulSet 是管理有状态应用的 "利器",核心记住 3 点:

  • 提供固定身份(名称、DNS),适合需要标识的集群应用;
  • 提供专属存储(自动创建 PVC),适合数据独立的实例;
  • 提供有序操作,适合对启动 / 更新顺序敏感的集群。

典型适用场景:

  • 数据库集群(MySQL 主从、PostgreSQL);
  • 分布式协调服务(ZooKeeper、etcd);
  • 消息队列(Kafka、RabbitMQ 集群)。
相关推荐
朱包林2 小时前
Prometheus监控K8S集群-ExternalName-endpoints-ElasticStack采集K8S集群日志实战
运维·云原生·容器·kubernetes·prometheus
zhangrelay3 小时前
蓝桥云课中支持的ROS1版本有哪些?-2025在线尝试ROS1全家福最方便的打开模式-
linux·笔记·学习·ubuntu
罗不俷3 小时前
【Kubernetes】(二十)Gateway
容器·kubernetes·gateway
zhangxuyu11183 小时前
Spring boot 学习记录
java·spring boot·学习
云闲不收3 小时前
GoFrame框架学习笔记
笔记·学习
MClink3 小时前
架构学习之旅-架构的由来
java·学习·架构
_dindong3 小时前
动规:01背包
数据结构·笔记·学习·算法·leetcode·动态规划·力扣
LGL6030A4 小时前
数据结构学习(1)——指针、结构体、链表(C语言)
数据结构·学习
蒙奇D索大5 小时前
【数据结构】考研算法精讲:分块查找的深度剖析 | 从“块内无序、块间有序”思想到ASL性能最优解
数据结构·笔记·学习·考研·改行学it