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 模板,它展示了上述概念在真实项目中的落地方式:
replicas由values.yaml中的replicaCount注入(默认 1),并且在启用自动扩缩容(autoscaling.enabled)时不再显式声明副本数,而是交给 HPA 接管(见 documentation/k8s/refine-documentation/templates/hpa.yaml)。- 容器定义了 HTTP 端口、
livenessProbe与readinessProbe探针,这是无状态 Deployment 的标准健康检查配置。 - 镜像地址与拉取策略(
image.pullPolicy)通过 documentation/k8s/refine-documentation/values.yaml 配置,默认仓库为ghcr.io/refinedev/refine/refine-documentation。 - 支持
nodeSelector、affinity、tolerations等调度约束,均为 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):
apiVersion和kind字段指定了资源对应的 API 版本与资源类型,此处kind为Deployment。- 元数据(Metadata):包含 Deployment 的
name和namespace;如果未指定,namespace默认为default。 - 规格(Spec):定义 Deployment 的期望状态:
replicas:期望的 Pod 数量。selector:通过matchLabels指定 Deployment 如何找到它所管理的 Pod。template:定义要创建的 Pod,包含用于标签的metadata部分,以及描述容器配置的spec部分。
- 元数据(Metadata):包含 Deployment 的
- 容器规格(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 世界中这两个重要工作负载的核心差异,帮助你快速选型:
| 对比维度 | Deployment | StatefulSet |
|---|---|---|
| 主要用途(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),仅供参考