Kubernetes 中的 StatefulSet

Kubernetes 中的 StatefulSet

文章目录

  • [Kubernetes 中的 StatefulSet](#Kubernetes 中的 StatefulSet)
    • [1. 为什么需要 StatefulSet?](#1. 为什么需要 StatefulSet?)
    • [2. StatefulSet 的核心特性](#2. StatefulSet 的核心特性)
      • [2.1 稳定的网络标识](#2.1 稳定的网络标识)
      • [2.2 稳定的持久化存储](#2.2 稳定的持久化存储)
      • [2.3 有序的部署和伸缩](#2.3 有序的部署和伸缩)
      • [2.4 扩缩容行为](#2.4 扩缩容行为)
    • [3. StatefulSet 的组成部分](#3. StatefulSet 的组成部分)
    • [4. StatefulSet 的更新策略](#4. StatefulSet 的更新策略)
    • [5. 删除和级联删除](#5. 删除和级联删除)
    • [6. StatefulSet 的局限性](#6. StatefulSet 的局限性)
    • [7. 典型使用场景](#7. 典型使用场景)
    • [8. 与 Deployment 的对比](#8. 与 Deployment 的对比)
    • [9. 最佳实践](#9. 最佳实践)
    • [10. 简单示例:部署一个有状态 Nginx](#10. 简单示例:部署一个有状态 Nginx)
    • 总结

StatefulSet 是 Kubernetes 中用于管理有状态应用的工作负载 API 对象。与 Deployment 专注于无状态应用不同,StatefulSet 为每个 Pod 提供持久标识符和稳定的存储,从而确保 Pod 在重新调度后仍能保留其状态和身份。

1. 为什么需要 StatefulSet?

在容器化环境中,无状态应用(如 Web 前端)可以随意扩缩容,每个 Pod 对等且可替换。但有状态应用(如数据库、消息队列、分布式协调服务)需要满足以下要求:

  • 稳定的网络标识:每个实例需要固定的主机名或 DNS 名称,即使重启或迁移也不会改变。
  • 持久化存储:每个实例绑定独立的持久卷,重启后数据不丢失。
  • 有序的部署、伸缩和删除:实例之间通常有启动顺序要求(如主从关系),需要按顺序执行操作。
  • 稳定的成员关系:集群中的成员身份需要可预测,便于发现和连接。

Deployment 无法满足这些要求,因为它的 Pod 名称随机、共享存储卷、无顺序控制。StatefulSet 正是为解决这些问题而设计的。

2. StatefulSet 的核心特性

2.1 稳定的网络标识

  • 每个 Pod 拥有唯一的、稳定的网络标识,格式为:$(statefulset名称)-$(序号)
  • 例如,StatefulSet 名为 web,副本数为 3,则 Pod 名称依次为 web-0web-1web-2
  • 配合 Headless Service (ClusterIP 为 None 的 Service),可以为每个 Pod 提供稳定的 DNS 记录:pod-name.service-name.namespace.svc.cluster.local
  • 即使 Pod 被重新调度到其他节点,其名称和主机名保持不变,保证了网络身份的稳定性。

2.2 稳定的持久化存储

  • 每个 Pod 可以关联一个或多个 PersistentVolumeClaim(PVC),PVC 的名称也包含 Pod 的序号:pvc-name-statefulset名称-序号
  • 当 Pod 被重新调度时,Kubernetes 会自动将原有的 PVC 重新挂载到新的 Pod 上,确保数据持久化。
  • 这要求 StorageClass 支持动态制备或预先创建好 PV,PVC 与 Pod 一一绑定。

2.3 有序的部署和伸缩

  • 顺序创建:StatefulSet 按照序号从 0 到 N-1 的顺序逐个创建 Pod,只有前一个 Pod 处于 Running 和 Ready 状态后,才会创建下一个 Pod。
  • 顺序删除:当缩容或删除 StatefulSet 时,会按序号从 N-1 到 0 的顺序逐个删除 Pod。
  • 顺序更新 :更新时默认采用 RollingUpdate 策略,也会按序号倒序逐个更新 Pod(从最后一个开始),以保证可用性。
  • 支持并行操作 :可以通过 podManagementPolicy 设置为 Parallel 来改变默认的有序行为,但通常会牺牲一定的顺序保证。

2.4 扩缩容行为

  • 扩容时,新增 Pod 的序号继续递增(例如从 3 增加到 4,新增 Pod 名称为 web-3)。
  • 缩容时,只会删除序号最大的 Pod,且会等待其完全终止后再删除下一个。
  • 如果某个 Pod 故障,StatefulSet 会重新创建一个同名 Pod,并尝试挂载原来的 PVC。

3. StatefulSet 的组成部分

一个典型的 StatefulSet 清单包含以下关键字段:

yaml 复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"          # 必须指定一个 Headless Service
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:          # 为每个 Pod 动态创建 PVC
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 1Gi
  • serviceName:必须指定一个 Headless Service 的名称,用于为 Pod 提供稳定的网络标识。该 Service 通常定义如下:

    yaml 复制代码
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      clusterIP: None
      selector:
        app: nginx
      ports:
      - port: 80
        name: web
  • volumeClaimTemplates :定义 PVC 模板,StatefulSet 会为每个 Pod 创建一个独立的 PVC。PVC 的命名规则为:<volumeClaimTemplate.name>-<statefulset.name>-<ordinal>

4. StatefulSet 的更新策略

StatefulSet 支持两种更新策略:

  • RollingUpdate (默认):按序号从大到小的顺序逐个更新 Pod。可以设置 partition 参数来控制更新的范围(例如只更新序号大于等于 partition 的 Pod),实现金丝雀发布或灰度发布。
  • OnDelete:手动删除 Pod 后才会触发重新创建(使用新模板),适用于需要精细控制更新时机的场景。

更新时,还可以配置 spec.updateStrategy.rollingUpdate.maxUnavailable(可选,但 StatefulSet 默认保证至少有一个 Pod 可用)。

5. 删除和级联删除

  • 删除 StatefulSet 时,默认不会删除其管理的 Pod 和 PVC(级联删除策略为 orphan)。这意味着即使 StatefulSet 被删除,Pod 和 PVC 仍然存在,需要手动清理。
  • 如果需要同时删除 Pod,可以使用 kubectl delete statefulset web --cascade=orphan 改为 backgroundforeground 级联删除策略(Kubernetes 1.7+ 支持)。但 PVC 始终需要手动删除。

6. StatefulSet 的局限性

  • 升级复杂性:有状态应用的升级通常比无状态复杂,可能需要考虑数据格式兼容性、主从切换等。
  • 存储管理:PVC 的生命周期需要额外关注,缩容或删除 StatefulSet 不会自动删除 PVC,可能导致存储资源泄露。
  • 扩缩容速度:由于顺序创建/删除,扩缩容速度可能较慢,尤其是副本数较多时。
  • 不保证 Pod 的唯一性:虽然名称唯一,但 StatefulSet 不会阻止两个同名 Pod 同时运行(例如在节点故障恢复时可能短暂出现),需要应用自身处理这种冲突(如使用分布式锁)。

7. 典型使用场景

  • 分布式数据库:如 Cassandra、MongoDB 副本集、Elasticsearch,需要每个节点有稳定的身份和数据存储。
  • 消息队列:如 Kafka、RabbitMQ,每个 broker 需要持久化存储和固定的网络标识。
  • 有状态中间件:如 ZooKeeper、etcd,依赖稳定的成员关系进行集群协调。
  • 需要独立存储的 Legacy 应用:迁移到 Kubernetes 时,每个实例需要独立的存储卷。

8. 与 Deployment 的对比

特性 Deployment StatefulSet
Pod 名称 随机后缀,如 pod-xxx 固定有序名称,如 pod-0
存储 共享存储卷(或使用单个 PVC) 每个 Pod 独立的 PVC
网络标识 无保证,Pod 重建后 IP 变化 稳定的 DNS 名称(通过 Headless Service)
启动/停止顺序 无序,可并行 有序(默认)
扩缩容 快速,可并行 顺序执行
更新策略 滚动更新,支持暂停、最大不可用等 滚动更新(默认倒序)或 OnDelete
适用场景 无状态应用 有状态应用

9. 最佳实践

  • 始终为 StatefulSet 创建 Headless Service,以便其他服务通过 DNS 发现 Pod。
  • 谨慎使用 Parallel 策略,只有在应用本身不依赖启动顺序时才使用。
  • 监控 PVC 的使用情况,避免存储空间耗尽。
  • 对于需要备份的数据,结合 VolumeSnapshot 或备份工具定期备份。
  • 更新镜像或配置时 ,考虑数据兼容性,必要时使用 partition 进行分批更新。

10. 简单示例:部署一个有状态 Nginx

假设我们要部署一个 Nginx 集群,每个实例有自己的独立存储(存储不同内容)和稳定域名。

  1. 创建 Headless Service:

    bash 复制代码
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      clusterIP: None
      selector:
        app: nginx
      ports:
      - port: 80
    EOF
  2. 创建 StatefulSet:

    bash 复制代码
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      serviceName: nginx
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:1.21
            ports:
            - containerPort: 80
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 1Gi
    EOF
  3. 验证:

    bash 复制代码
    kubectl get pods -l app=nginx
    kubectl get pvc

    可以看到 Pod 名称为 web-0web-1web-2,每个 Pod 对应一个 PVC www-web-0www-web-1www-web-2

  4. 测试网络标识:

    在集群内另一个 Pod 中,可以解析 web-0.nginx.default.svc.cluster.local 访问到特定 Pod。

总结

StatefulSet 是 Kubernetes 对有状态应用的标准管理方式,通过稳定的网络标识、独立的持久化存储和有序的操作,弥补了 Deployment 在管理有状态应用时的不足。虽然它引入了一些复杂性,但对于运行数据库、消息队列等核心有状态服务来说,是不可或缺的组件。理解 StatefulSet 的工作原理和最佳实践,能够帮助我们在 Kubernetes 中更可靠地运行生产级有状态应用。

相关推荐
阿乐艾官2 小时前
【K8s思维导图及单节点容器启动流程】
java·容器·kubernetes
礼拜天没时间.2 小时前
企业级Docker镜像仓库Harbor部署实战
linux·运维·docker·云原生·容器·sre
小邓睡不饱耶2 小时前
Hadoop 3.x 企业级实战指南:从纠删码到云原生容器化
大数据·hadoop·云原生
kUhzIPVBnE2 小时前
二极管箝位型三电平逆变器与NPC三电平逆变器的主要难点及MATLAB/Simulink仿真模型研究
云原生
阿寻寻3 小时前
【云原生技术】Pod 列表新增时间字段:取值口径与获取方式
docker·云原生·kubernetes
DeeplyMind4 小时前
第27章 常见问题与解决方案
运维·docker·容器
nix.gnehc4 小时前
在K8s集群中部署Traefik并验证Python HTTP服务
python·http·kubernetes
nix.gnehc4 小时前
深入浅出 K8s 内外部通信:全场景方案解析与生产实践
云原生·容器·kubernetes
DeeplyMind5 小时前
第26章 Docker监控与日志
docker·容器·eureka