Kubernetes 是容器编排的事实标准,提供了强大的原语来管理你的应用程序。在其核心构建块中,Deployment (部署) 和 StatefulSet (有状态集) 是最常用的两个控制器。虽然它们都管理 Pod,但它们是为根本不同类型的工作负载设计的。

理解何时使用 Deployment 何时使用 StatefulSet 对于在 Kubernetes 上构建健壮、可伸缩和有弹性的应用程序至关重要。让我们深入了解它们的独特特性和用例。


理解 Kubernetes Pod

在比较之前,我们先回顾一下,Deployment 和 StatefulSet 的核心都是 Pod (容器组)。Pod 是 Kubernetes 中最小的可部署单元,它封装了一个或多个容器、存储资源、一个唯一的网络 IP 以及管理容器如何运行的选项。Pod 本质上是短暂的;它们可以被 Kubernetes 创建、销毁和重新调度。


Deployment:无状态应用程序的首选

Deployment (部署) 是 Kubernetes 的主力。它旨在管理无状态应用程序,其中单个 Pod 是可互换的,并且没有与其绑定的唯一标识或持久状态。想想 Web 服务器、REST API 或消息队列消费者——如果一个 Pod 宕机,另一个相同的 Pod 可以取代它,而不会影响应用程序的功能。

Key Characteristics of Deployments:
  • 无状态性: Pod 被认为是相同且可丢弃的。由同一个 Deployment 管理的每个 Pod 都可以与任何其他 Pod 互换。
  • 副本管理: 你可以定义所需数量的相同 Pod 副本。Deployment 控制器确保始终运行此数量的 Pod。
  • 编排式更新: Deployment 提供强大的策略来更新你的应用程序,如滚动更新 (RollingUpdate)(默认)和重新创建 (Recreate)
    • 滚动更新: 逐步用新 Pod 替换旧 Pod,确保零停机。
    • 重新创建: 在创建新 Pod 之前,先销毁所有旧 Pod(会导致停机)。
  • 无稳定标识: 由 Deployment 管理的 Pod 具有任意的主机名,并且在扩缩容或重新调度期间不保证保持其网络标识或顺序。它们的名称通常看起来像 my-app-xxxx-yyyy
  • 共享持久存储(可选): 虽然 Pod 本身是无状态的,但它们可以挂载 PersistentVolume (持久卷,PV)。然而,对于 Deployment,如果多个 Pod 共享一个 PV,它们都会写入同一个卷,这通常适用于只读数据或只追加的日志。它不是为每个副本独特的、专用的存储而设计的。
何时使用 Deployment:
  • Web 服务器 (例如 Nginx, Apache): 服务静态内容或充当反向代理。
    • 实例: 你有一个简单的网站,所有的 HTML、CSS 和 JavaScript 文件都打包在一个 Docker 镜像中。你可以部署 Nginx Deployment,并设置 3 个副本。每个 Nginx Pod 都是完全相同的,它们共享同一个网站文件。即使其中一个 Pod 崩溃或被重启,用户体验也不会受影响,因为另外两个 Pod 会继续提供服务,并且 Kubernetes 会自动启动一个新的 Nginx Pod 来替代故障的 Pod。
  • RESTful API: 不在服务器上维护会话状态的微服务。
    • 实例: 一个处理用户注册的微服务,它接收请求并将数据存储到后端数据库。这个服务本身不存储任何用户会话信息或特定于实例的数据。你可以运行一个 5 副本的 Deployment。任何请求都可以被任何一个 Pod 处理,Pod 之间完全独立。当新版本发布时,通过滚动更新可以平滑过渡,不会造成服务中断。
  • 消息队列消费者: 处理来自队列的消息,任何实例都可以获取任何消息。
    • 实例: 你有一个处理订单的消费者服务,它从 Kafka 队列中读取消息并更新数据库。这些消费者是等价的,每个都可以独立地处理消息。使用 Deployment 可以轻松地根据消息量弹性伸缩消费者数量,每个 Pod 都是一个“工作单元”,无需关心其身份或持久状态。

StatefulSet:管理有状态工作负载

StatefulSet (有状态集) 专门用于管理有状态应用程序。与 Deployment 不同,StatefulSet 确保每个 Pod 都具有稳定、唯一的标识,维护持久存储,并保证有序、优雅的部署和扩缩容。这使得它们非常适合数据库、分布式键值存储以及其他需要稳定网络标识、持久存储和有序操作的应用程序。

Key Characteristics of StatefulSets:
  • 稳定、唯一的网络标识符: StatefulSet 中的每个 Pod 都根据其序号索引 (web-0, web-1, web-2 等) 拥有持久的主机名。即使 Pod 被重新调度,此标识也保持一致。
  • 稳定、持久存储: StatefulSet 通常与 PersistentVolumeClaims (持久卷声明,PVC)PersistentVolumes (持久卷,PV) 一起使用,其中每个 Pod 获得自己专用的、稳定的存储卷。如果 Pod 宕机并被重新调度,它将重新挂载其原始的持久卷。
  • 有序、优雅的部署和扩缩容:
    • Deployment: Pod 以任意顺序创建/删除。
    • StatefulSet: Pod 以序号索引顺序创建 (web-0,然后 web-1,然后 web-2)。缩容也以相反的序号顺序进行(先 web-2,然后 web-1)。这对于依赖于法定人数或领导者选举的分布式系统至关重要。
  • 有序终止: 当 Pod 被缩容或删除时,它会从最高序号开始优雅地终止。这允许应用程序在关闭之前执行清理或握手。
  • 无头服务集成: StatefulSet 几乎总是使用无头服务 (Headless Service) (ClusterIP: None) 为其 Pod 提供稳定的网络标识。这允许其他服务通过其唯一主机名发现单个 Pod。
何时使用 StatefulSet:
  • 数据库 (例如 MySQL, PostgreSQL, MongoDB, Cassandra): 每个数据库实例都需要自己的持久存储,并且通常需要一个稳定的网络标识进行复制或集群。
    • 实例: 你想在 Kubernetes 上部署一个 MySQL 主从复制集群。你需要创建一个 StatefulSet,例如 3 个副本。mysql-0 可以是主节点,mysql-1mysql-2 是从节点。每个 Pod (mysql-0, mysql-1, mysql-2) 都会获得一个唯一的、稳定的主机名和自己的独立持久卷来存储数据库文件。当 mysql-0 崩溃重启后,它仍然会以 mysql-0 的身份回来,并挂载回之前的数据卷,保持其作为主节点的角色或在集群中恢复其状态。扩缩容时,也会按照严格的序号顺序进行,以确保集群状态的正确同步。
  • 分布式键值存储 (例如 ZooKeeper, etcd): 这些系统依赖于有序启动、稳定标识和持久数据存储。
    • 实例: 部署一个 ZooKeeper 集群。ZooKeeper 节点需要通过它们稳定的网络标识来相互发现并形成仲裁。它们也需要持久存储来保存元数据和事务日志。使用 StatefulSet 可以确保 ZooKeeper 节点按照特定的顺序启动,并且每个节点都有其固定的网络名和数据卷,这对于 ZooKeeper 集群的正确运行至关重要。
  • 存储状态的消息队列 (例如带有持久日志的 Kafka): 其中消息日志需要是持久的并与特定 Broker 相关联。
    • 实例: Kafka Broker 需要将消息日志持久化到磁盘上,并且每个 Broker 都有一个唯一的 ID。使用 StatefulSet 部署 Kafka 可以为每个 Broker 提供独立的持久卷,确保消息日志不会丢失。同时,Pod 的稳定网络标识有助于生产者和消费者正确地连接到特定的 Broker。

如何选择 Deployment 和 StatefulSet

选择 Deployment 还是 StatefulSet 归结为一个基本问题:你的应用程序的状态是否需要在 Pod 重启或重新调度后仍然持久存在,并且每个实例是否需要一个稳定、唯一的标识?

特性/要求 Deployment StatefulSet
工作负载类型 无状态 有状态
Pod 标识 可互换,无稳定标识 稳定、唯一 (pod-name-0, pod-name-1)
存储 共享 PVs (通常只读/追加) 每个 Pod 独占、持久的存储 (通过 PVCs)
扩缩容顺序 任意 有序 (扩容 0 -> N-1,缩容 N-1 -> 0)
更新 滚动更新,重新创建 有序滚动更新,有序终止
用例 Web 服务器、API、无状态 worker 数据库、分布式系统、消息队列

通用经验法则:

  • 从 Deployment 开始。 如果你的应用程序可以设计成无状态的,或者会话状态在外部处理(例如,通过数据库或缓存层),那么 Deployment 更简单、更灵活,并提供更快的扩缩容/更新。
  • 仅在必要时使用 StatefulSet。 如果你的应用程序本质上需要稳定的网络标识符、操作的保证顺序或每个副本的专用持久存储,那么 StatefulSet 是正确且必要的选择。它增加了复杂性,但提供了有状态应用程序所需的保证。

Kubernetes 提供了强大的工具来管理无状态和有状态应用程序。通过理解 Deployment 和 StatefulSet 之间的核心差异,并结合具体的应用场景进行分析,你可以做出明智的架构决策,从而实现更健壮、更高效和更易于维护的 Kubernetes 部署。在可能的情况下始终追求无状态性,但在你的应用程序性质要求真正的有状态管理时,请自信地拥抱 StatefulSet。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐