工作负载资源:Deployment、StatefulSet、DaemonSet、Job与CronJob

说实话,Kubernetes 里最核心的概念之一,就是工作负载资源。你想想看,PaaS 平台说白了就是帮用户管应用的,而应用在 K8s 里跑,总得有个载体吧?

我个人习惯把工作负载资源分成两类:一类是跑长期服务的,比如 Web 后端、API 网关;另一类是跑短任务的,比如数据清洗、定时备份。今天咱们就把这五种资源掰开揉碎讲清楚。

Deployment:最常用的无状态部署

Deployment 是我用得最多的资源,没有之一。它管理的是无状态应用——说白了,每个 Pod 都是一样的,谁挂了直接换一个,数据不存本地。

核心特点:
  • 支持滚动更新和回滚
  • 支持声明式扩缩容
  • Pod 名字随机,不保证稳定网络标识

我在项目中遇到过这样一个场景:一个微服务需要从 3 个副本扩到 10 个,应对双十一流量。用 Deployment 就一行命令的事:kubectl scale deployment my-app --replicas=10。但要注意,如果应用本身有状态(比如依赖本地磁盘),那 Deployment 就不合适了。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
我的小技巧: 生产环境一定要设置 strategy.rollingUpdate.maxSurgemaxUnavailable,默认值 25% 有时候会引发问题。我曾经遇到过更新时 Pod 全挂了,就是因为没配这两个参数。

StatefulSet:有状态应用的救星

StatefulSet 是专门为有状态应用设计的。比如数据库、消息队列、缓存集群——这些家伙每个实例都有自己的身份,不能随便替换。

为什么需要它?你想想看,MySQL 主从复制,主库和从库能一样吗?当然不能。StatefulSet 给每个 Pod 分配了稳定的网络标识和持久化存储。

特性 Deployment StatefulSet
Pod 名称 随机后缀 有序编号(如 mysql-0, mysql-1)
存储 共享卷或临时卷 每个 Pod 独立 PVC
启停顺序 无序 有序(从 N 到 0 停止,从 0 到 N 启动)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
避坑指南: 我曾经在生产环境遇到过 StatefulSet 缩容导致数据丢失的问题。原因是 PVC 默认不会随 Pod 删除而删除。如果你要缩容,记得先确认数据已经备份或迁移。

DaemonSet:每个节点跑一个

DaemonSet 的用途很明确:确保每个 Node 上都运行一个 Pod 副本。典型的场景包括日志采集(Fluentd)、监控代理(Prometheus Node Exporter)、网络插件(Calico)等。

说白了,DaemonSet 就是「节点级守护进程」。你加一个新节点,它自动在上面跑一个 Pod;你删一个节点,Pod 也跟着消失。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      containers:
      - name: fluentd
        image: fluent/fluentd:v1.14
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
我的经验: 如果你只想在部分节点上运行 DaemonSet,可以用 nodeSelectoraffinity。比如我只想在 GPU 节点上跑推理服务,就可以打上 gpu=true 的标签。

Job:跑一次就结束

Job 是用来执行一次性任务的。比如数据迁移、批量处理、模型训练。任务跑完,Pod 就进入 Completed 状态,不会重启。

嗯,这里要注意:Job 有两种模式——非并行和并行。非并行就是只跑一个 Pod,跑完结束;并行可以指定 completionsparallelism,比如我要处理 100 个文件,同时开 5 个 Worker。

apiVersion: batch/v1
kind: Job
metadata:
  name: data-migration
spec:
  template:
    spec:
      containers:
      - name: migration
        image: myapp/migration-tool:latest
        command: ["python", "migrate.py"]
      restartPolicy: Never
  backoffLimit: 4
避坑指南: 我曾经遇到过一个 Job 跑了 3 天还没结束,结果集群资源被占满了。后来我养成了习惯:生产环境的 Job 一定要设置 activeDeadlineSeconds,超时自动终止。

CronJob:定时跑 Job

CronJob 就是带定时器的 Job。它根据 Cron 表达式来调度 Job 的执行。常见的用途包括:定时备份数据库、清理过期日志、生成日报等。

说白了,CronJob = Cron + Job。你只需要写一个 Cron 表达式,K8s 就会在指定时间帮你创建 Job。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: myapp/backup-tool:latest
            command: ["sh", "-c", "pg_dumpall > /backup/db.sql"]
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
关键配置项:
  • schedule:Cron 表达式,支持 5 位(分 时 日 月 周)
  • startingDeadlineSeconds:如果 Job 没按时启动,最多等多久
  • concurrencyPolicy:是否允许并发执行(Allow/Forbid/Replace)
  • successfulJobsHistoryLimit:保留多少个成功的 Job 记录
我的建议: 生产环境的 CronJob 一定要设置 concurrencyPolicy: Forbid。为什么?因为如果备份任务上次还没跑完,下次又开始了,两个备份同时写同一个文件,后果你懂的。

如何选择?一张图说清楚

最后,我总结一下这五种资源的选择逻辑:

  • 无状态服务(Web、API、微服务)→ Deployment
  • 有状态服务(数据库、缓存、消息队列)→ StatefulSet
  • 节点级守护(日志、监控、网络)→ DaemonSet
  • 一次性任务(迁移、批处理)→ Job
  • 定时任务(备份、清理、报表)→ CronJob

其实,大部分 PaaS 平台的核心就是把这些资源封装成「应用模板」。用户只需要填几个参数,平台自动生成对应的 YAML。你想想看,如果用户每次都要手写这些配置,那还叫什么 PaaS?

好了,这五种工作负载资源就讲到这里。下一章咱们聊聊服务发现和负载均衡——Service 和 Ingress 的那些事。