Deployment、StatefulSet与DaemonSet是K8s三大核心工作负载控制器,分别服务于无状态应用、有状态应用与节点级守护进程。文章对比三者的调度行为、更新策略、存储处理方式以及在不同应用场景下的选择决策及配置实践与故障排查与性能调优方案分析。

岗位关联

需要学习Deployment/StatefulSet/DaemonSet,Deployment、StatefulSet与DaemonSet是K8s三大核心工作负载控制器,分别服务于无状态应用、有状态应用与节点级守护进程。作为云原生与微服务架构进阶 > Kubernetes核心与Operator开发的基础概念,Deployment/StatefulSet/DaemonSet为实际应用场景提供理论依据。

目录
  1. 可视化演示
  2. Kubernetes 工作负载控制器概述
  3. Deployment:无状态应用管理
  4. StatefulSet:有状态应用管理
  5. DaemonSet:节点级服务管理
  6. 三种控制器的对比与选型
  7. 实际应用场景与案例分析
  8. 进阶话题与最佳实践
  9. AI 应用场景
  10. Python 演示
  11. 常见问答
  12. 常见误区
  13. 参考来源

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

Kubernetes 工作负载控制器概述

Kubernetes 作为容器编排的事实标准,其核心设计理念之一就是通过控制器模式(Controller Pattern)来管理集群中的工作负载。控制器模式是一种控制循环机制,它持续观测集群的实际状态,并与期望状态进行比较,当两者不一致时自动执行调谐操作(Reconciliation),使实际状态逐步趋近于期望状态。这种声明式的设计哲学使得用户只需描述“我要什么”,而不需要关心“怎么做”,极大地简化了分布式系统的管理复杂度。

在 Kubernetes 中,工作负载控制器是一组负责管理 Pod 生命周期的核心组件。常见的工作负载控制器包括 Deployment、StatefulSet、DaemonSet、Job、CronJob 等。每种控制器都有其特定的适用场景和语义:Deployment 适用于无状态应用的滚动更新和扩缩容;StatefulSet 为有状态应用提供稳定的网络标识和持久化存储;DaemonSet 确保每个节点上运行一个 Pod 副本,适用于节点级别的基础服务。理解这些控制器的设计原理和差异,是高效使用 Kubernetes 的关键。

控制器的工作机制基于 Kubernetes 的 Level-triggered(水平触发)设计。与 Edge-triggered 不同,Level-triggered 意味着控制器不关心事件本身,而是持续检查当前状态是否满足期望。即使某个事件丢失,控制器也能在下一个循环中检测到偏差并修复。这种设计大大提高了系统的可靠性和容错能力。所有工作负载控制器都依赖于 ReplicaSet 或直接管理 Pod 资源,通过 label selector 来匹配和管理其下属的 Pod 集合。

Deployment:无状态应用管理

Deployment 是 Kubernetes 中最常用的工作负载控制器,专为管理无状态应用而设计。它通过管理 ReplicaSet 来间接控制 Pod 的副本数量,提供了声明式的更新能力。用户只需修改 Deployment 的 Pod 模板(如镜像版本、资源配置等),Deployment 控制器就会自动以滚动更新的方式将旧版本的 Pod 替换为新版本,整个过程无需停机。Deployment 还维护了一个修订历史记录(revisionHistoryLimit),支持快速回滚到任意历史版本。

滚动更新(Rolling Update)是 Deployment 的默认更新策略,通过 maxSurge 和 maxUnavailable 两个参数精确控制更新节奏。maxSurge 指定在更新过程中可以超出期望副本数的最大 Pod 数量,maxUnavailable 指定不可用 Pod 的最大数量。例如,当 replicas=10、maxSurge=25%、maxUnavailable=25% 时,更新过程中最多创建 12 个 Pod,同时至少保持 8 个 Pod 可用。这种机制确保了服务在更新期间始终保持足够的可用性,实现了零停机部署。

Deployment 的扩缩容操作同样简单直观,只需修改 replicas 字段即可。水平自动扩缩容(HPA)可以与 Deployment 配合使用,根据 CPU 利用率、内存使用率或自定义指标自动调整副本数。在云原生微服务架构中,Deployment 是部署 Web 服务、API 网关、微服务组件等无状态工作负载的首选控制器。结合 Pod Disruption Budget(PDB)和就绪探针(Readiness Probe),可以确保应用在扩缩容和更新过程中的服务连续性。

StatefulSet:有状态应用管理

StatefulSet 是 Kubernetes 专为有状态应用设计的工作负载控制器,与 Deployment 不同,它为每个 Pod 提供稳定的、唯一的网络标识和持久化存储。StatefulSet 管理的 Pod 具有固定的名称(如 mysql-0、mysql-1、mysql-2),这些名称在 Pod 重新调度时保持不变。每个 Pod 还会自动创建与其绑定的 PersistentVolumeClaim(PVC),即使 Pod 被删除并重新创建,它仍然会绑定到相同的持久化卷,从而保证数据不丢失。

StatefulSet 的另一个关键特性是有序部署和缩放。Pod 按照序号从 0 到 N-1 的顺序依次创建,每个 Pod 必须在前一个 Pod 就绪(Ready)之后才会启动。在缩放操作中,Pod 按照从 N-1 到 0 的逆序终止。这种有序性对于需要选举主节点或建立集群拓扑的分布式系统(如 ZooKeeper、Kafka、Elasticsearch)至关重要。此外,StatefulSet 还支持 Headless Service(无头服务),通过 DNS 直接解析每个 Pod 的 IP 地址,实现稳定的网络发现。

在实际应用中,StatefulSet 常用于部署关系型数据库(MySQL、PostgreSQL)、NoSQL 数据库(MongoDB、Cassandra)、消息队列(Kafka、RabbitMQ)以及分布式协调服务(ZooKeeper、etcd)等有状态组件。使用 StatefulSet 时需要注意存储类的选择、备份恢复策略、以及集群初始化逻辑。对于需要复杂集群管理的场景,还可以结合 Operator 模式(如 MySQL Operator、Kafka Operator)来自动化更高级的运维操作。

DaemonSet:节点级服务管理

DaemonSet 控制器确保集群中的每个(或选定的)节点上运行一个 Pod 副本。当新节点加入集群时,DaemonSet 会自动在该节点上创建 Pod;当节点被移除时,对应的 Pod 也会被回收。DaemonSet 不通过 replicas 字段控制副本数,其副本数量由集群中符合条件的节点数量决定。这种“每节点一个”的特性使其成为运行节点级别基础服务的理想选择。

DaemonSet 的典型应用场景包括:日志收集代理(如 Fluentd、Filebeat)、节点监控代理(如 Prometheus Node Exporter、Datadog Agent)、网络插件(如 Calico、Flannel、Cilium)、以及服务网格代理(如 Istio sidecar 的注入)。通过 nodeSelector 或 nodeAffinity,可以限制 DaemonSet 只在特定标签的节点上运行。通过 tolerations,可以让 DaemonSet 的 Pod 调度到带有污点的节点上(如 master 节点)。

DaemonSet 的更新策略支持 RollingUpdate 和 OnDelete 两种模式。RollingUpdate 策略下,DaemonSet 会逐个节点更新 Pod,可以通过 maxUnavailable 控制并发更新的数量。在实际运维中,DaemonSet 通常需要较高的权限(如 hostNetwork、hostPID、privileged),因此需要特别关注安全性。结合 Pod Anti-Affinity 可以确保同一节点上不会运行多个 DaemonSet 实例。DaemonSet 的管理通常由集群管理员负责,普通应用开发者较少直接与之交互。

三种控制器的对比与选型

Deployment、StatefulSet 和 DaemonSet 是 Kubernetes 中最核心的三种工作负载控制器,它们在设计目标、Pod 管理方式和适用场景上有显著差异。Deployment 将每个 Pod 视为可互换的、无身份的实体,Pod 名称随机生成,删除后重建的 Pod 没有任何关联。StatefulSet 为每个 Pod 赋予稳定的身份标识(固定名称、固定序号、固定存储),Pod 之间存在有序关系。DaemonSet 则以节点为维度管理 Pod,每个节点最多运行一个副本,副本数量随节点数量自动变化。

在选型时,核心判断标准是应用是否有状态。如果应用是无状态的(如 Web 服务器、API 服务、微服务),任何 Pod 实例都可以互相替代,则选择 Deployment。如果应用需要持久化存储、稳定的网络标识、或有序的部署/缩放(如数据库、消息队列、分布式协调服务),则选择 StatefulSet。如果需要在每个节点上运行一个系统级服务(如日志收集、监控、网络插件),则选择 DaemonSet。错误的选型可能导致数据丢失、服务中断或资源浪费。

值得注意的是,三种控制器可以组合使用。例如,一个完整的微服务系统可能使用 Deployment 运行业务服务,使用 StatefulSet 运行数据库和缓存集群,使用 DaemonSet 运行日志收集和监控代理。此外,还需要考虑 Pod Disruption Budget(PDB)来保障更新和缩放期间的可用性,使用 Topology Spread Constraints 实现跨可用区的均匀分布,以及通过 Resource Quotas 和 Limit Ranges 控制资源使用。合理的架构设计需要综合考虑应用的特性、可用性要求和运维成本。

实际应用场景与案例分析

在微服务架构中,Deployment 是最常见的工作负载控制器。以电商平台为例,订单服务、用户服务、支付服务等无状态微服务通常使用 Deployment 管理。通过配置 HPA(水平 Pod 自动扩缩容),可以根据请求量自动调整副本数:在促销高峰期自动扩容以应对流量洪峰,在低谷期缩容以节约资源。结合 Ingress 和 Service 实现负载均衡和服务发现,通过滚动更新实现零停机发布,利用 revisionHistoryLimit 保留历史版本以便快速回滚。

StatefulSet 在数据密集型应用中发挥关键作用。以 Kafka 集群为例,每个 Broker 需要唯一的 ID(通过 Pod 序号自动分配)、持久化的日志存储(通过 volumeClaimTemplates 自动创建 PVC)、以及稳定的网络地址(通过 Headless Service 实现 Pod 间通信)。类似地,Redis Cluster、Elasticsearch 集群等分布式数据存储系统也依赖 StatefulSet 来管理。在实际部署中,通常还需要配置 Pod Anti-Affinity 将不同副本分散到不同节点,避免单点故障。

DaemonSet 在集群基础设施层面不可或缺。以日志收集为例,Fluentd DaemonSet 在每个节点上运行一个代理,收集该节点上所有容器的日志并转发到集中式日志系统(如 Elasticsearch)。Prometheus Node Exporter 通过 DaemonSet 部署,采集每个节点的 CPU、内存、磁盘、网络等指标。在网络层面,Calico 或 Cilium 等 CNI 插件通过 DaemonSet 在每个节点上运行网络代理,负责 Pod 网络的 routing 和 network policy 执行。这些基础服务虽然对用户不可见,但对集群的正常运行至关重要。

进阶话题与最佳实践

Pod Disruption Budget(PDB)是保障应用高可用的重要机制。通过配置 minAvailable 或 maxUnavailable,可以确保在进行节点维护、集群升级或自动缩放时,始终有足够数量的 Pod 处于可用状态。例如,对于一个 replicas=3 的 Deployment,设置 maxUnavailable=1 可以保证在任何时刻至少有两个 Pod 提供服务。PDB 与 eviction API 配合使用,可以有效防止因并行驱逐导致的服务中断。

Topology Spread Constraints 允许用户控制 Pod 在拓扑域(如可用区、节点、机架)之间的分布。通过设置 maxSkew 和 whenUnsatisfiable 参数,可以实现 Pod 的均匀分布,避免所有副本集中在同一个可用区。例如,设置 topologyKey=zone、maxSkew=1 可以确保各可用区的 Pod 数量差不超过 1。这对于跨可用区高可用部署至关重要。结合 nodeAffinity 和 Pod Anti-Affinity,可以构建更加精细的调度策略。

在 GitOps 实践中,工作负载控制器的配置通常以 YAML 文件形式存储在 Git 仓库中,通过 ArgoCD 或 Flux 等工具自动同步到集群。最佳实践包括:使用 Kustomize 或 Helm 管理多环境配置差异;将镜像版本固定为具体的 tag 或 digest,避免使用 latest;配置合理的资源请求和限制(requests/limits);设置健康检查(liveness/readiness/startup probe);启用 Pod Security Standards 限制容器的权限。此外,定期审查和优化控制器的配置,如调整 revisionHistoryLimit、配置合理的终止宽限期(terminationGracePeriodSeconds),也是生产环境运维的重要环节。

AI 应用场景

使用 Deployment 部署 ML 模型推理服务

解决的问题:在 AI 推理场景中,模型推理服务通常是无状态的——给定相同的输入,任何实例都能返回相同的结果。使用 Deployment 管理推理服务 Pod,可以通过 HPA 根据 GPU 利用率或请求队列长度自动扩缩容。结合 Rolling Update 策略实现模型版本的零停机切换,当新模型上线时,逐步将流量从旧模型迁移到新模型,一旦发现问题可以立即回滚。通过 Ingress 配置金丝雀发布,将少量请求路由到新版本进行验证。

具体案例:在 AI 推理场景中,模型推理服务通常是无状态的——给定相同的输入,任何实例都能返回相同的结果。使用 Deployment 管理推理服务 Pod,可以通过 HPA 根据 GPU 利用率或请求队列长度自动扩缩容。结合 Rolling Update 策略实现模型版本的零停机切换,当新模型上线时,逐步将流量从旧模型迁移到新模型,一旦发现问题可以立即回滚。通过 Ingress 配置金丝雀发布,将少量请求路由到新版本进行验证。

使用 StatefulSet 管理分布式训练任务的有状态存储

解决的问题:在大规模 AI 训练场景中,多个训练 Worker 需要协同工作,每个 Worker 需要持久化存储来保存检查点(Checkpoint)和训练日志。使用 StatefulSet 管理训练 Worker Pod,每个 Worker 拥有固定的身份标识和独立的 PVC,确保训练中断后可以从最近的检查点恢复。Headless Service 为每个 Worker 提供稳定的 DNS 名称,方便 Worker 之间进行梯度同步和参数交换。有序的启动顺序确保 Parameter Server 先于 Worker 启动。

具体案例:在大规模 AI 训练场景中,多个训练 Worker 需要协同工作,每个 Worker 需要持久化存储来保存检查点(Checkpoint)和训练日志。使用 StatefulSet 管理训练 Worker Pod,每个 Worker 拥有固定的身份标识和独立的 PVC,确保训练中断后可以从最近的检查点恢复。Headless Service 为每个 Worker 提供稳定的 DNS 名称,方便 Worker 之间进行梯度同步和参数交换。有序的启动顺序确保 Parameter Server 先于 Worker 启动。

使用 DaemonSet 实现节点级 GPU 监控与资源管理

解决的问题:在 GPU 集群中,需要对每个节点的 GPU 使用情况进行实时监控和资源管理。通过 DaemonSet 在每个 GPU 节点上部署监控代理,采集 GPU 利用率、显存使用、温度、功耗等指标,并上报到 Prometheus 监控系统。同时部署 GPU 资源管理插件(如 NVIDIA Device Plugin),确保 Kubernetes 能够正确识别和分配 GPU 资源。DaemonSet 的“每节点一个”特性确保每个 GPU 节点都被覆盖,新加入的 GPU 节点会自动被纳入监控范围。

具体案例:在 GPU 集群中,需要对每个节点的 GPU 使用情况进行实时监控和资源管理。通过 DaemonSet 在每个 GPU 节点上部署监控代理,采集 GPU 利用率、显存使用、温度、功耗等指标,并上报到 Prometheus 监控系统。同时部署 GPU 资源管理插件(如 NVIDIA Device Plugin),确保 Kubernetes 能够正确识别和分配 GPU 资源。DaemonSet 的“每节点一个”特性确保每个 GPU 节点都被覆盖,新加入的 GPU 节点会自动被纳入监控范围。

Python 演示

python138 行
from kubernetes import client, config def create_deployment():    config.load_kube_config()    apps_v1 = client.AppsV1Api()    container = client.V1Container(        name='ml-serving',        image='tensorflow/serving:2.14.0',        ports=[client.V1ContainerPort(container_port=8501)],        resources=client.V1ResourceRequirements(            requests={'cpu': '500m', 'memory': '512Mi'},            limits={'cpu': '2000m', 'memory': '2Gi'}        )    )    template = client.V1PodTemplateSpec(        metadata=client.V1ObjectMeta(labels={'app': 'ml-serving'}),        spec=client.V1PodSpec(containers=[container])    )    spec = client.V1DeploymentSpec(        replicas=3,        selector=client.V1LabelSelector(            match_labels={'app': 'ml-serving'}        ),        template=template,        strategy=client.V1DeploymentStrategy(            type='RollingUpdate',            rolling_update=client.V1RollingUpdateDeployment(                max_surge='25%',                max_unavailable='25%'            )        )    )    deployment = client.V1Deployment(        api_version='apps/v1',        kind='Deployment',        metadata=client.V1ObjectMeta(name='ml-serving'),        spec=spec    )    apps_v1.create_namespaced_deployment(        namespace='default', body=deployment    )    return deployment def create_statefulset():    config.load_kube_config()    apps_v1 = client.AppsV1Api()    container = client.V1Container(        name='kafka-broker',        image='confluentinc/cp-kafka:7.5.0',        ports=[client.V1ContainerPort(container_port=9092)],        volume_mounts=[client.V1VolumeMount(            name='kafka-data', mount_path='/var/lib/kafka/data'        )]    )    template = client.V1PodTemplateSpec(        metadata=client.V1ObjectMeta(labels={'app': 'kafka'}),        spec=client.V1PodSpec(containers=[container])    )    spec = client.V1StatefulSetSpec(        replicas=3,        service_name='kafka-headless',        selector=client.V1LabelSelector(            match_labels={'app': 'kafka'}        ),        template=template,        volume_claim_templates=[client.V1PersistentVolumeClaim(            metadata=client.V1ObjectMeta(name='kafka-data'),            spec=client.V1PersistentVolumeClaimSpec(                access_modes=['ReadWriteOnce'],                resources=client.V1ResourceRequirements(                    requests={'storage': '50Gi'}                )            )        )]    )    statefulset = client.V1StatefulSet(        api_version='apps/v1',        kind='StatefulSet',        metadata=client.V1ObjectMeta(name='kafka'),        spec=spec    )    apps_v1.create_namespaced_stateful_set(        namespace='default', body=statefulset    )    return statefulset def create_daemonset():    config.load_kube_config()    apps_v1 = client.AppsV1Api()    container = client.V1Container(        name='gpu-monitor',        image='nvidia/dcgm-exporter:3.3.0',        ports=[client.V1ContainerPort(container_port=9400)],        resources=client.V1ResourceRequirements(            limits={'nvidia.com/gpu': '0'}        ),        volume_mounts=[client.V1VolumeMount(            name='pod-gpu-resources',            mount_path='/var/lib/kubelet/pod-resources'        )]    )    template = client.V1PodTemplateSpec(        metadata=client.V1ObjectMeta(            labels={'app': 'gpu-monitor'}        ),        spec=client.V1PodSpec(            containers=[container],            node_selector={'gpu': 'true'},            volumes=[client.V1Volume(                name='pod-gpu-resources',                host_path=client.V1HostPathVolumeSource(                    path='/var/lib/kubelet/pod-resources'                )            )]        )    )    spec = client.V1DaemonSetSpec(        selector=client.V1LabelSelector(            match_labels={'app': 'gpu-monitor'}        ),        template=template    )    daemonset = client.V1DaemonSet(        api_version='apps/v1',        kind='DaemonSet',        metadata=client.V1ObjectMeta(name='gpu-monitor'),        spec=spec    )    apps_v1.create_namespaced_daemon_set(        namespace='monitoring', body=daemonset    )    return daemonset if __name__ == '__main__':    create_deployment()    create_statefulset()    create_daemonset()    print('All controllers created successfully.')
  1. from kubernetes import client, config导入 Kubernetes Python 客户端库,client 提供 API 对象,config 提供配置加载功能
  2. def create_deployment():定义创建 Deployment 的函数,Deployment 用于管理无状态应用的 Pod 副本
  3. name='ml-serving',设置容器名称为 ml-serving,该名称在 Pod 内必须唯一
  4. image='tensorflow/serving:2.14.0',指定使用 TensorFlow Serving 镜像,版本固定为 2.14.0 以确保可复现性
  5. requests={'cpu': '500m', 'memory': '512Mi'},设置资源请求,调度器根据 requests 值决定将 Pod 放置到哪个节点
  6. replicas=3,设置期望副本数为 3,Deployment 控制器会确保始终有 3 个 Pod 在运行
  7. max_surge='25%',滚动更新时允许超出期望副本数 25%,即最多额外创建 1 个 Pod
  8. max_unavailable='25%'滚动更新时允许最多 25% 的 Pod 不可用,确保更新过程中服务持续可用
  9. def create_statefulset():定义创建 StatefulSet 的函数,StatefulSet 为有状态应用提供稳定的网络标识和存储
  10. name='kafka-broker',设置 Kafka Broker 容器名称,每个 StatefulSet Pod 中的容器名称保持一致
  11. service_name='kafka-headless',指定关联的 Headless Service 名称,用于为每个 Pod 创建稳定的 DNS 记录
  12. volume_claim_templates=[client.V1PersistentVolumeClaim(定义 volumeClaimTemplates,StatefulSet 会为每个 Pod 自动创建独立的 PVC
  13. requests={'storage': '50Gi'}每个 PVC 请求 50Gi 的存储空间,用于持久化 Kafka 的消息日志数据
  14. def create_daemonset():定义创建 DaemonSet 的函数,DaemonSet 确保每个符合条件的节点上运行一个 Pod
  15. node_selector={'gpu': 'true'},通过 nodeSelector 限制 DaemonSet 只在带有 gpu=true 标签的节点上运行

常见问答

Deployment 和 StatefulSet 的核心区别是什么?
Deployment 管理的 Pod 是无状态的,每个 Pod 可互换且没有固定的身份标识;StatefulSet 管理的 Pod 是有状态的,每个 Pod 拥有稳定的网络标识(固定名称和 DNS)、有序的部署/缩放顺序、以及独立的持久化存储(通过 volumeClaimTemplates)。简单来说,Deployment 关注副本数量,StatefulSet 关注每个 Pod 的身份和状态。
DaemonSet 的 Pod 数量是如何确定的?
DaemonSet 的 Pod 数量由集群中符合调度条件的节点数量决定。当新节点加入集群且满足 nodeSelector/nodeAffinity 条件时,DaemonSet 会自动在该节点上创建 Pod;当节点被删除或不再满足条件时,对应的 Pod 会被回收。用户不能直接指定 DaemonSet 的副本数,它没有 replicas 字段。
Deployment 的滚动更新策略中 maxSurge 和 maxUnavailable 分别控制什么?
maxSurge 控制滚动更新过程中可以超出期望副本数的最大 Pod 数量(可以是绝对值或百分比),决定了更新的速度上限。maxUnavailable 控制更新过程中允许不可用的最大 Pod 数量,决定了服务的可用性下限。两者共同约束了更新过程中实际运行的 Pod 数量范围,实现平滑的零停机更新。
为什么数据库通常使用 StatefulSet 而不是 Deployment 来部署?
数据库是有状态应用,需要三个关键特性:1) 稳定的网络标识——每个数据库实例需要固定的主机名用于集群成员发现和主从选举;2) 持久化存储——每个实例需要绑定独立的持久卷,确保数据在 Pod 重启后不丢失;3) 有序的部署和终止——数据库集群通常需要按顺序启动以正确建立复制拓扑。StatefulSet 恰好提供了这三个特性,而 Deployment 无法保证。
如何让 DaemonSet 的 Pod 调度到带有污点(Taint)的节点上?
需要为 DaemonSet 的 Pod 模板添加对应的容忍度(Toleration)。例如,master 节点通常带有 node-role.kubernetes.io/master:NoSchedule 污点,要让 DaemonSet 的 Pod 也能调度到 master 节点,需要在 Pod 的 spec.tolerations 中添加匹配的容忍规则。DaemonSet 控制器本身不会自动添加容忍度,需要用户显式配置。

常见误区

误区Deployment 可以直接管理有状态数据库应用,只要挂载了持久化存储就行。

正解虽然 Deployment 可以挂载 PVC,但它无法为每个 Pod 提供独立的、稳定的存储绑定。Deployment 创建的 Pod 是无身份的,当 Pod 被重新调度时,新 Pod 可能绑定到不同的 PVC,导致数据混乱。有状态数据库应使用 StatefulSet,它通过 volumeClaimTemplates 为每个 Pod 创建独立的 PVC,并确保 Pod 始终绑定到同一个存储卷。

为何常见:许多初学者认为只要挂载了存储就是有状态的,忽略了 Pod 身份与存储绑定的关系。Deployment 挂载共享 PVC 时多个 Pod 可能同时写入导致数据损坏,而 StatefulSet 的每个 Pod 拥有独立的 PVC 才能正确管理有状态数据。

误区DaemonSet 的 Pod 数量可以通过 replicas 字段来控制。

正解DaemonSet 没有 replicas 字段。DaemonSet 的设计目标是在每个符合条件的节点上运行恰好一个 Pod 副本,其 Pod 数量完全由集群中满足调度条件(nodeSelector、nodeAffinity、tolerations)的节点数量决定。如果需要控制副本数量,应该使用 Deployment 而不是 DaemonSet。

为何常见:初学者容易将所有工作负载控制器与 Deployment 的 API 结构混淆,认为所有控制器都有 replicas 字段。实际上 DaemonSet 的核心语义是“每节点一个”,副本数由节点数决定,这是它与 Deployment/StatefulSet 的本质区别之一。

误区StatefulSet 的 Pod 删除后,新创建的 Pod 会获得新的名称和存储。

正解StatefulSet 的 Pod 被删除后,重新创建的 Pod 会保持完全相同的名称(如 mysql-0)和绑定到相同的 PVC。这正是 StatefulSet 的核心价值——提供稳定的身份标识。Pod 名称由 StatefulSet 名称加序号组成,序号在 Pod 生命周期内不会改变。即使节点故障导致 Pod 被重新调度到新节点,新 Pod 仍然使用相同的名称和存储。

为何常见:人们容易用 Deployment 的思维理解 StatefulSet,在 Deployment 中每个 Pod 确实是临时的、可互换的。但 StatefulSet 的设计哲学完全不同,它保证每个 Pod 的身份是持久的,这是运行分布式有状态应用的基础。

误区DaemonSet 只适合运行系统级服务,普通应用不应该使用 DaemonSet。

正解虽然 DaemonSet 最常用于系统级服务(日志收集、监控、网络插件),但它也可以用于特定的应用场景,如需要在每个节点上运行一个实例的业务服务。例如,某些缓存代理、数据预处理服务等场景可能希望每个节点有一个本地实例以减少网络延迟。选择 DaemonSet 的关键标准不是“是否系统级”,而是“是否需要每节点一个副本”。

为何常见:教程和文档中 DaemonSet 的示例大多是系统组件,导致人们形成了 DaemonSet 只能用于系统服务的刻板印象。实际上 DaemonSet 是一种通用的“每节点一个”调度模式,任何符合该语义的工作负载都可以使用。

参考来源

  • official_documentationKubernetes 工作负载控制器官方文档Kubernetes 官方文档中关于工作负载控制器的全面介绍,涵盖 Deployment、StatefulSet、DaemonSet 等核心控制器的设计原理和使用方法。
  • official_documentationDeployment 控制器官方指南Kubernetes 官方文档中关于 Deployment 的详细指南,包括滚动更新策略、回滚操作、扩缩容配置以及最佳实践。
  • official_documentationStatefulSet 控制器官方指南Kubernetes 官方文档中关于 StatefulSet 的详细说明,涵盖稳定网络标识、有序部署/缩放、持久化存储以及 volumeClaimTemplates 的使用。
  • official_documentationDaemonSet 控制器官方指南Kubernetes 官方文档中关于 DaemonSet 的详细说明,包括节点级服务管理、更新策略、节点选择以及与集群生命周期的关系。