Kubernetes StatefulSet 与 Deployment 对比实战:从 YAML 到有状态应用的部署选型
2026/9/10 9:09:25 网站建设 项目流程

Kubernetes StatefulSet 与 Deployment 对比实战:从 YAML 到有状态应用的部署选型

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

导读

本文以 Kubernetes 中两种最常用的工作负载资源为切入点,系统对比 Deployment 与 StatefulSet 在副本管理、存储、扩缩容与更新策略上的本质差异,并结合本仓库(refine 文档站点)中真实存在的 Helm/Deployment 配置,以及一套完整的 MySQL 有状态应用实战 YAML,帮助你在实际项目中做出正确的资源选型:何时用 Deployment 管理无状态应用,何时用 StatefulSet 承载数据库等有状态应用,以及部署过程中可能遇到哪些典型错误。读完本文你将能够独立编写、应用并验证这两类资源的 YAML 清单。

为什么需要区分 Deployment 与 StatefulSet

Kubernetes 为部署和管理容器化应用提供了强大的能力,而 Deployment 与 StatefulSet 是其中最重要的两个工作负载控制器。二者都在扮演"管理应用副本"的关键角色,却服务于截然不同的需求与场景:

  • Deployment面向无状态(stateless)应用。此类应用的每个实例本质上完全相同、可互相替换,例如 Web 服务器、API 后端。
  • StatefulSet面向有状态(stateful)应用。此类应用要求稳定的、唯一的网络标识与持久化存储,例如数据库、消息队列。

理解这两类资源的差异,是决定"什么时候、用什么方式部署"的核心前提。下面分别深入讲解。

什么是 Deployment

Deployment 用于创建并管理应用副本,它能够无缝地更新应用,并在出问题时快速回滚。应用借助 Deployment 可以获得高可用,并且很容易进行水平扩缩容与负载均衡。

Deployment 的核心特性

  • 按需扩缩容(Demand-based scaling):可根据负载自动或手动增加、减少运行中的实例数量。
  • 滚动发布与回滚(Rollout and Rollback):在不停机的情况下平滑发布新版本,或回退到之前的版本。
  • 自愈(Auto-healing):当某个 Pod 失败或被删除时,Deployment 会自动替换它以维持期望状态(desired state)。
  • 更新策略(Update Strategy):可以选择同时更新所有副本,也可以采用滚动更新逐步替换,以最小化对服务的扰动。

一个 Deployment 配置示例

下面是一个最简单的 Deployment 配置。它定义了一个管理 3 个 nginx 应用副本的 Deployment:

apiVersion: apps/v1 kind: Deployment metadata: name: proxy-deployment spec: replicas: 3 selector: matchLabels: app: proxy template: metadata: labels: app: proxy spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80

逐字段解读这个 YAML:

  • apiVersion:指定所使用的 Kubernetes API 版本(此处为apps/v1)。
  • kind:声明资源类型为Deployment
  • metadata:为 Deployment 提供名称和标签,用于标识该对象。
  • spec.replicas:期望的副本数量(这里是 3)。
  • selector:通过matchLabels选择该 Deployment 管理的 Pod。
  • template:描述 Pod 副本应如何构建,包括容器镜像与端口。

仓库中的真实 Deployment:refine 文档站点的 Helm 模板

本仓库中 documentation/k8s/refine-documentation/templates/deployment.yaml 就是一个生产形态的 Deployment 模板,它展示了上述概念在真实项目中的落地方式:

  • replicasvalues.yaml中的replicaCount注入(默认 1),并且在启用自动扩缩容(autoscaling.enabled)时不再显式声明副本数,而是交给 HPA 接管(见 documentation/k8s/refine-documentation/templates/hpa.yaml)。
  • 容器定义了 HTTP 端口、livenessProbereadinessProbe探针,这是无状态 Deployment 的标准健康检查配置。
  • 镜像地址与拉取策略(image.pullPolicy)通过 documentation/k8s/refine-documentation/values.yaml 配置,默认仓库为ghcr.io/refinedev/refine/refine-documentation
  • 支持nodeSelectoraffinitytolerations等调度约束,均为 Deployment 模板中的可选字段。

这说明:对 Web 前端、API 等无状态服务,Deployment 是默认且推荐的工作负载类型,配合 HPA 即可实现按 CPU/内存利用率的自动伸缩。

什么是 StatefulSet

设想一个电商应用场景:你需要维护购物车与会话(session)的状态,就必须有某种方式持久化数据和状态。此时 StatefulSet 登场——它确保每个 Pod 的身份与数据在重启和重新调度之后依然保留。如果把无状态应用比作"健忘"的实例,那么 StatefulSet 则像"记事"的实例:即使被调度到集群中的其他节点,它依然记得自己的数据(状态)。

StatefulSet 的核心特性

  • 稳定、唯一的网络标识(Stable, Unique Network Identifiers):StatefulSet 中每个 Pod 都会获得唯一且稳定的主机名与网络标识,确保网络与存储访问的一致性和可识别性。这一点对数据库这类有状态应用至关重要。
  • 持久化存储管理(Persistent Storage Management):StatefulSet 管理一组 Pod 的部署与扩缩容,同时维护与每个 Pod 关联的持久化存储。即使 Pod 被重新调度到不同节点,它仍然保留自己的存储,从而保证数据持久性。
  • 有序、优雅的部署与扩缩容(Ordered, Graceful Deployment and Scaling):StatefulSet 中的 Pod 严格按照序号(ordinal index)依次创建、更新和删除。这保证了受控的扩缩容与滚动更新,维护了对有状态应用至关重要的顺序与完整性。

一个 StatefulSet 配置示例

apiVersion: apps/v1 kind: StatefulSet metadata: name: scheduling-statefulset spec: serviceName: "scheduling-service" replicas: 3 selector: matchLabels: app: scheduling-app template: metadata: labels: app: scheduling-app spec: containers: - name: scheduling-container image: scheduling-image volumeClaimTemplates: - metadata: name: scheduling-storage spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 2Gi

在这个示例中,我们定义了一个名为scheduling-statefulset、拥有 3 个副本的 StatefulSet。请注意:

  • serviceName是维持每个 Pod 网络身份的关键字段,它必须指向一个 Headless Service(clusterIP: None的 Service),StatefulSet 借助它生成形如<pod-name>.<service-name>.<namespace>.svc.cluster.local的稳定 DNS 名称。
  • volumeClaimTemplates是 StatefulSet 区别于 Deployment 的重要机制:它为集合中的每个 Pod自动创建属于自己的 PersistentVolumeClaim(PVC),从而实现"一个 Pod 一份独立存储"。

实战一:部署一个有状态应用(MySQL)

前置条件:你已经成功创建了一个 Kubernetes 集群。

创建 YAML 文件

下面通过 YAML 创建一个 StatefulSet,运行一个典型的有状态应用——MySQL 数据库。我们将使用 MySQL 的公共镜像:

apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql-statefulset spec: serviceName: "mysql" replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD value: <YOUR_PASSWORD> ports: - containerPort: 3306 volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-persistent-storage spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi

专家提示:请密切关注该 YAML 正常运行所需的环境变量。以 MySQL 为例,必须在 YAML 中提供 MySQL 的 root 主密码,即上面 YAML 中<YOUR_PASSWORD>所引用的值。此外两个关键设计值得说明:

  • volumeMounts将名为mysql-persistent-storage的卷挂载到容器内/var/lib/mysql——这是 MySQL 的数据目录,数据落盘后即可跨重启保留。
  • volumeClaimTemplates会为 Pod 动态创建 PVC,accessModes: ["ReadWriteOnce"]表示该卷仅支持单节点读写(单副本数据库的标准选择),storage: 1Gi声明了容量请求。

应用 StatefulSet YAML

执行kubectl apply命令将清单提交到集群:

kubectl apply -f mysql.yaml

验证 StatefulSet 是否正常运行

kubectl get statefulsets检查 StatefulSet 是否已创建:

kubectl get statefulsets

还可以查看与该 StatefulSet 关联的 Pod。使用按标签过滤的命令kubectl get pods -l <label-key>=<label-value>,例如本文中的标签是app=mysql

kubectl get pods -l app=mysql

从输出中可以看到该 StatefulSet 下正在运行的 Pod。

扩缩容与更新镜像

  • 扩缩容 StatefulSet 使用kubectl scale命令,格式为kubectl scale statefulsets [statefulset-name] --replicas=[new-replica-count]
  • 更新 StatefulSet 中使用的容器镜像(即发布应用新版本)使用kubectl set image命令,格式为kubectl set image statefulset [statefulset-name] [container-name]=[new-image-name]

使用 StatefulSet 时可能遇到的典型错误

StatefulSet 对状态敏感,而 Deployment 是无状态的,因此创建 StatefulSet 时更容易遇到错误。以下是一些只在创建 StatefulSet 时才会出现的问题:

  • PVC 冲突(Persistent Volume Claim Conflicts):当指定的 PVC 不可用,或已被另一个 StatefulSet / Pod 占用时报错。
  • Headless Service 缺失(Headless Service Requirement):由于缺少 StatefulSet 网络身份所需的 Headless Service 而报错。
  • Pod 命名问题(Pod Naming Issue):StatefulSet 的 Pod 名称格式(<statefulset-name>-<ordinal>)与 Kubernetes 命名规范冲突时报错。
  • 更新策略配置错误(Update Strategy Misconfiguration):更新策略配置不当导致的错误——与默认支持滚动更新的 Deployment 不同,StatefulSet 对更新顺序与策略有更严格的约束。
  • 有状态 Pod 调度失败(Stateful Pod Scheduling Failure):由于持久化存储的亲和性(affinity)约束导致 Pod 无法调度,这在无状态 Deployment 中并不常见。

实战二:部署一个无状态应用(hello-app)

前置条件:你已经成功创建了一个 Kubernetes 集群。

创建 YAML 文件

apiVersion: apps/v1 kind: Deployment metadata: name: stateless-app1 namespace: default spec: replicas: 3 selector: matchLabels: app: stateless-app1 template: metadata: labels: app: stateless-app1 spec: containers: - name: hello-app-1 image: gcr.io/google-samples/hello-app:2.0 resources: requests: cpu: "500m" memory: "2Gi" limits: cpu: "500m" memory: "2Gi"

逐段解读这份 YAML:

  • API 版本与类型(API Version and Kind)apiVersionkind字段指定了资源对应的 API 版本与资源类型,此处kindDeployment
    • 元数据(Metadata):包含 Deployment 的namenamespace;如果未指定,namespace默认为default
    • 规格(Spec):定义 Deployment 的期望状态:
      • replicas:期望的 Pod 数量。
      • selector:通过matchLabels指定 Deployment 如何找到它所管理的 Pod。
      • template:定义要创建的 Pod,包含用于标签的metadata部分,以及描述容器配置的spec部分。
  • 容器规格(Container Specs):包含容器name、所用image,以及resources块——它定义了容器的 CPU 与内存requests(请求量)和limits(上限)。

应用无状态 YAML

执行:

kubectl apply -f stateless-app1.yaml

验证 Deployment 是否正常运行

使用kubectl rollout status检查 Deployment 是否发布成功:

kubectl rollout status deployment/stateless-app1

从输出可以看到 deployment 成功滚动发布。若想确认该 Deployment 当前正在运行的 Pod 数量,使用kubectl get pods -l app=[deployment-label]即可——上述示例会显示 3 个 Pod(副本)在运行。

Deployment 与 StatefulSet 对比一览表

下表汇总了 Kubernetes 世界中这两个重要工作负载的核心差异,帮助你快速选型:

对比维度DeploymentStatefulSet
主要用途(Primary Use-Case)管理无状态应用。管理有状态应用。
适用场景(When to Use)- 需要扩缩容无状态应用时
- 每个实例可互相替换的应用
- 需要稳定、唯一网络标识的场景
- 需要持久化存储的应用
副本管理(Replica Management)每个 Pod 名称包含一个随机哈希值。每个 Pod 被分配一个唯一的序号(ordinal index),只要 Pod 存在该序号就保持不变。
存储(Storage)通常使用临时(ephemeral)存储;可以使用持久卷,但不由 Deployment 生命周期统一管理。支持稳定存储:通过 Persistent Volume 与每个 Pod 的序号关联。
扩缩容行为(Scaling Behavior)扩缩容副本时不考虑顺序与状态。按可预测的顺序扩缩容;Pod 按序号依次创建和删除。
更新策略(Update Strategy)支持 Rolling Update(滚动更新)与 Recreate 两种策略。主要使用滚动更新,且按 Pod 序号的逆序进行。
故障转移行为(Failover Behavior)节点故障时,Pod 被重新调度,不关心原始状态。重新调度后仍保持相同的网络身份,这对某些数据密集应用至关重要。
Pod 名称保持(Pod Name Preservation)Pod 被替换时名称会改变。Pod 被重新调度时名称保持不变(粘性身份,sticky identity)。
使用案例(Use Case Examples)- Web 服务器
- 无状态后端服务
- 数据库(如 MySQL、PostgreSQL)
- 任何对数据一致性与顺序敏感的系统
前置条件(Pre-Requisites)了解 Kubernetes 对象的基础知识。理解持久化存储,以及用于稳定网络标识的 Headless Service。

从对比到选型:结合仓库实际案例

回到本仓库本身,可以观察到一个有趣的对照:

  • refine 文档站点这类内容型 Web 应用完全无状态,其 Helm Chart 使用 Deployment + Service + HPA 的组合(见 documentation/k8s/refine-documentation/templates/deployment.yaml、documentation/k8s/refine-documentation/templates/service.yaml 与 documentation/k8s/refine-documentation/templates/hpa.yaml),任意副本被替换都不会影响数据与身份——这正是 Deployment 的典型画像。
  • 而一旦某个业务模块需要落盘数据(例如电商的购物车、会话,或 PostgreSQL、MySQL 这类数据库),就必须切换到 StatefulSet,借助volumeClaimTemplates与 Headless Service 获得逐 Pod 的持久卷与稳定网络身份。

仓库中 documentation/blog/2024-01-22-k8s-postgres.md(在 K8s 中部署 PostgreSQL)、documentation/blog/2023-12-25-kubectl-scale.md(kubectl scale 详解)与 documentation/blog/2023-11-20-kubeclt-delete-pod.md(删除 Pod 的行为差异)可作为深入阅读材料,帮助你把本文的选型原则应用到更多真实场景。

总结

Kubernetes 的 Deployment 与 StatefulSet 服务于应用管理的不同需求。Deployment 适合无状态应用,提供了自动扩缩容、便捷更新和高可用等能力,在需要快速伸缩、频繁发布变更的场景中表现出色;StatefulSet 则为有状态应用而生,确保稳定的网络标识与持久化存储,是数据库等对数据持久性和顺序敏感的系统的必备选择。

选择 Deployment 还是 StatefulSet,最终取决于你的应用是否需要状态:如果数据可随时重建、实例可互相替换,选择 Deployment;如果应用依赖持久数据、要求稳定的身份与启动顺序,则应选择 StatefulSet。希望本文的 YAML 示例、对比表与常见错误清单,能帮助你在下一次 Kubernetes 部署中做出清晰、正确的决策。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询