路线图

DaemonSet、Job 与 CronJob:节点级与任务型工作负载

星辉 2026-07-02 阅读 4 min 661 字 路线图
DaemonSet、Job 与 CronJob:节点级与任务型工作负载 封面

Deployment 和 StatefulSet 管理的是"长期运行的服务",但 K8s 中还有两类不同的场景:每个节点都必须跑一个实例(采集日志、监控节点),以及跑完就结束的一次性或定时任务。DaemonSet、Job 和 CronJob 分别覆盖这两类场景。


DaemonSet:确保每个节点运行一个 Pod#

概念与场景#

DaemonSet 的语义很简单:集群中有 N 个节点,就运行 N 个 Pod——每个节点恰好一个。新节点加入集群时,DaemonSet 自动在上面创建 Pod;节点移除时,Pod 随之被清理。

典型场景

场景说明
日志采集Fluentd/Filebeat 每个节点采集容器日志,推送到中心存储
监控 AgentPrometheus Node Exporter/Prometheus Agent 采集节点指标
网络插件Calico/Flannel 在每个节点配置容器网络
存储插件CSI Daemon Pod 在每个节点挂载存储卷

这些 Agent 必须在每个节点运行,用 Deployment 无法保证这一点——Deployment 的 Pod 数由 replicas 决定,不一定等于节点数,而且多个 Pod 可能调度到同一节点。

YAML 示例#

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  labels:
    app: fluentd
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      tolerations:                # 关键:容忍 master 节点的 taint,否则不会在 master 上运行
        - key: node-role.kubernetes.io/control-plane
          effect: NoSchedule
      containers:
        - name: fluentd
          image: fluent/fluentd:v1.16
          volumeMounts:
            - name: varlog
              mountPath: /var/log       # 采集节点系统日志
            - name: containers
              mountPath: /var/log/pods                # 采集容器日志(containerd 默认日志目录,Docker 时代是 /var/lib/docker/containers)
              readOnly: true
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: containers
          hostPath:
            path: /var/log/pods

nodeSelector:限定特定节点#

默认情况下,DaemonSet 在所有节点上运行。有时你只想在特定节点上部署,比如只在 GPU 节点上运行监控 Agent:

yaml
spec:
  template:
    spec:
      nodeSelector:
        gpu: "true"    # 只在打了 gpu=true 标签的节点上运行

也可以用 affinity.nodeAffinity 实现更灵活的节点选择(如按标签正则匹配)。DaemonSet 还自动容忍某些 taint(如 node.kubernetes.io/not-ready),确保在节点就绪前就启动关键 Agent。

tolerations 是 DaemonSet YAML 中常见配置。默认情况下,K8s master 节点打了 node-role.kubernetes.io/control-plane:NoSchedule 的 taint,普通 Pod 不会调度到 master。但日志采集等 Agent 需要覆盖所有节点,所以必须加对应的 toleration。


Job:一次性任务#

概念与参数#

Job 管理的是"跑完就结束"的任务。不同于 Deployment 的 Pod 需要一直运行,Job 的 Pod 成功执行完成后(退出码 0),Job 标记为完成。

关键参数

参数含义默认值
completions需要成功完成多少个 Pod 才算 Job 完成1
parallelism同时运行多少个 Pod(并行数)1
backoffLimit失败重试上限,超过后 Job 标记为 Failed6
  • completions=1, parallelism=1:跑 1 个 Pod,最简单的场景
  • completions=5, parallelism=2:总共要 5 个 Pod 成功完成,同时最多跑 2 个
  • 不设 completionsparallelism=3:3 个 Pod 并行从工作队列取任务,任一 Pod 成功后就不再新建 Pod,待所有 Pod 退出后 Job 完成(工作队列模式)。注意:若把 completions 显式设为 1,实际同一时刻只会跑 1 个 Pod(并行度受 completions - 已成功数 限制),无法真正并行

实战:数据迁移任务#

假设你需要把一批数据从旧存储迁移到新存储,任务可以并行执行但总量固定:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: data-migration
spec:
  completions: 10        # 总共 10 个子任务需要完成
  parallelism: 3         # 同时跑 3 个 Pod
  backoffLimit: 4        # 失败重试最多 4 次
  template:
    spec:
      containers:
        - name: migrator
          image: my-migration-tool:v1
          command: ["python", "migrate.py"]
          env:
            - name: BATCH_SIZE
              value: "1000"
      restartPolicy: OnFailure   # Job 必须设置 OnFailure 或 Never,不能用 Always

restartPolicy 说明

  • OnFailure:Pod 内容器失败时,在同一 Pod 内重启容器(不新建 Pod)
  • Never:容器失败时,创建新 Pod 重试(旧 Pod 保留用于排查)

查看 Job 状态

bash
kubectl get jobs
# NAME            COMPLETIONS   DURATION   AGE
# data-migration   3/10         2m         2m

kubectl describe job data-migration
# 查看 Events 了解 Pod 创建和失败情况

清理完成的 Job

bash
kubectl delete job data-migration

Job 完成后 Pod 默认保留,方便排查日志。但大量 Job 会堆积已完成 Pod,建议设置 ttlSecondsAfterFinished 自动清理(如 ttlSecondsAfterFinished: 3600,完成 1 小时后自动删除)。


CronJob:定时任务#

概念与参数#

CronJob 在 Job 之上加了时间调度,按 crontab 格式的 schedule 定期创建 Job。

schedule 格式与 Linux crontab 一致:

text
# ┌───────── 分钟 (0-59)
# │ ┌───────── 小时 (0-23)
# │ │ ┌───────── 日 (1-31)
# │ │ │ ┌───────── 月 (1-12)
# │ │ │ │ ┌───────── 星期 (0-6, 0=周日)
# │ │ │ │ │
# * * * * *

例如:

  • 0 2 * * *——每天凌晨 2 点
  • */30 * * * *——每 30 分钟
  • 0 0 1 * *——每月 1 号零点

并发策略(concurrencyPolicy)#

如果上一次 Job 还没跑完,下一次调度时间又到了,怎么处理?

行为
Allow允许新旧 Job 同时运行(默认)
Forbid如果上一次 Job 还在运行,跳过本次调度
Replace如果上一次 Job 还在运行,终止旧 Job,创建新 Job

选择建议:备份类任务用 Forbid(避免同时写两个备份);清理类任务用 Replace(旧任务可能卡住,强制替换更合理)。

实战:定时数据库备份#

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-backup
spec:
  schedule: "0 2 * * *"          # 每天凌晨 2 点执行
  concurrencyPolicy: Forbid      # 上一次没跑完就跳过
  successfulJobsHistoryLimit: 3  # 只保留最近 3 次成功的 Job
  failedJobsHistoryLimit: 1      # 只保留最近 1 次失败的 Job
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          containers:
            - name: backup
              image: mysql:8.0
              command:
                - bash
                - "-c"
                - "mysqldump -h mysql-0.mysql-headless -u root -p${MYSQL_ROOT_PASSWORD} --all-databases > /backup/all.sql"
              env:
                - name: MYSQL_ROOT_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-secret
                      key: root-password
              volumeMounts:
                - name: backup-volume
                  mountPath: /backup
          volumes:
            - name: backup-volume
              persistentVolumeClaim:
                claimName: backup-pvc     # 预先创建的 PVC,用于持久化备份文件
          restartPolicy: OnFailure

要点解读

  1. concurrencyPolicy: Forbid——备份任务不能并发,否则两个 mysqldump 同时锁表
  2. successfulJobsHistoryLimit: 3——只保留最近 3 次成功记录,避免 Job 堆积
  3. failedJobsHistoryLimit: 1——保留 1 次失败记录用于排查
  4. secretKeyRef——数据库密码从 Secret 读取,不硬编码在 YAML 中
  5. mysql-0.mysql-headless——引用 StatefulSet 的 Headless Service DNS 名,定向连接 master

CronJob 不适合秒级/毫秒级精度的定时场景——控制器约每 10 秒轮询一次调度,任务实际触发通常有几秒延迟。另外,控制平面重启或错过调度窗口(超过 startingDeadlineSeconds,或累计错过 100 次)时可能漏调——如果你的定时任务不允许漏调,需要在应用层面做补偿机制。


实战要点#

  1. DaemonSet 的 tolerations——要覆盖 master 节点,必须加 node-role.kubernetes.io/control-plane: NoSchedule 的 toleration;否则 master 上的 taint 会阻止调度
  2. Job 的 restartPolicy——Job 模板中 restartPolicy 只能是 OnFailureNever,不能是 Always(Always 是 Deployment 等长期运行工作负载的默认值)
  3. Job 清理策略——生产环境务必设置 ttlSecondsAfterFinished 或手动清理已完成 Job,否则 Pod 会无限堆积
  4. CronJob 并发策略——备份等不允许并发的任务务必设 Forbid,避免重复执行导致数据冲突
  5. DaemonSet 更新——DaemonSet 支持滚动更新,且 updateStrategy 默认就是 RollingUpdate(改 Pod 模板后自动逐节点更新,可用 maxUnavailable/maxSurge 控制节奏);另一种 OnDelete 策略才需要手动删除 Pod 才触发更新

小结#

DaemonSet、Job 和 CronJob 是 K8s 中三类面向特殊场景的工作负载控制器。DaemonSet 保证每个节点恰好一个 Pod,适用于节点级 Agent(日志、监控、网络插件)。Job 管理一次性任务,通过 completions/parallelism/backoffLimit 控制完成数、并行度和失败重试。CronJob 在 Job 上叠加定时调度,concurrencyPolicy 控制并发行为。三者覆盖了"每个节点都要有"和"跑完就走"两类 Deployment 无法满足的场景。掌握它们,你的 K8s 工作负载知识图谱就完整了。