4. ScaledJob 详解:CRD 定义、与 ScaledObject 的区别、Job 触发模式与失败重试策略

在 KEDA 生态中,ScaledJob 是专门为 批处理任务(Batch Job) 设计的事件驱动伸缩原语。与 ScaledObject 管理 Deployment/StatefulSet 的 Pod 副本数不同,ScaledJob 根据事件源水位动态创建 Kubernetes Job 对象,每个 Job 执行一次即完成。本章将深入 ScaledJob 的 CRD 结构、与 ScaledObject 的核心差异、触发模式以及生产级失败重试策略。

核心概念: ScaledJob 不是“扩缩容已有 Pod”,而是“按需创建一次性 Job”。当事件源(如 RabbitMQ 队列深度、AWS SQS 消息数)超过阈值时,KEDA 会创建新的 Job 实例来处理积压任务。

4.1 ScaledJob CRD 定义

ScaledJob 的 CRD 定义在 keda.sh/v1alpha1 API 版本下,其核心结构包含 jobTargetRef(Job 模板)、triggers(事件源)、scalingStrategy(伸缩策略)和 failedJobsHistoryLimit(失败历史限制)。以下是一个完整的 YAML 示例:

apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  name: sqs-job-scaler
  namespace: default
spec:
  # Job 模板:定义每次创建的 Job 规格
  jobTargetRef:
    template:
      spec:
        containers:
        - name: worker
          image: myregistry/worker:1.0
          command: ["python", "process_messages.py"]
          env:
          - name: QUEUE_URL
            value: "https://sqs.us-east-1.amazonaws.com/123456789/myqueue"
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
        restartPolicy: Never
    backoffLimit: 4  # Job 内部重试次数
    ttlSecondsAfterFinished: 300  # Job 完成后 5 分钟自动清理

  # 事件源触发器
  triggers:
  - type: aws-sqs-queue
    metadata:
      queueURL: "https://sqs.us-east-1.amazonaws.com/123456789/myqueue"
      queueLength: "5"          # 目标队列长度
      awsRegion: "us-east-1"
      activationQueueLength: "0" # 激活阈值

  # 伸缩策略
  scalingStrategy:
    strategy: "default"         # 可选:default, accurate, eager
    maxScale: 10                # 最大并发 Job 数
    pollingInterval: 10         # 轮询间隔(秒)
    cooldownPeriod: 30          # 冷却期(秒)

  # 失败历史限制
  failedJobsHistoryLimit: 5
  successfulJobsHistoryLimit: 3
关键字段说明:
  • jobTargetRef:与原生 Kubernetes Job 的 spec 结构一致,但不需要 spec.parallelism 和 spec.completions,因为 KEDA 会动态控制并发 Job 数量。
  • scalingStrategy.strategy:决定 KEDA 如何计算需要创建的 Job 数量。默认策略基于事件源水位与目标值的比值。
  • failedJobsHistoryLimit:保留最近 N 个失败 Job 的记录,用于审计和调试。

4.2 ScaledJob 与 ScaledObject 的核心区别

虽然 ScaledJob 和 ScaledObject 都使用相同的触发器机制,但它们的伸缩对象、生命周期和适用场景有本质区别。下表从 6 个维度进行对比:

对比维度 ScaledObject ScaledJob
伸缩目标 Deployment / StatefulSet / Custom Resource Kubernetes Job(一次性 Pod)
Pod 生命周期 长期运行,持续处理请求 执行完成后自动退出
伸缩方向 水平扩缩容(增加/减少副本数) 垂直创建(增加 Job 实例数)
适用场景 Web 服务、API 网关、实时流处理 消息队列消费、批量数据处理、ETL 任务
失败处理 依赖 Deployment 的滚动更新和就绪探针 依赖 Job 的 backoffLimit + ScaledJob 重试策略
资源清理 缩容时自动删除 Pod 通过 ttlSecondsAfterFinished 或历史限制清理
选型陷阱: 不要将 ScaledJob 用于需要持续监听或保持长连接的服务。如果任务需要“一直运行”,请使用 ScaledObject + Deployment。ScaledJob 只适合“处理完就结束”的幂等任务。
ScaledJob vs ScaledObject 架构对比 ScaledObject 事件源触发器 HPA 调整 Deployment 副本数 Pod 长期运行,持续处理 ScaledJob 事件源触发器 创建新的 Job 实例 Job 执行一次后自动退出

4.3 Job 触发模式

ScaledJob 支持三种触发模式,通过 scalingStrategy.strategy 字段控制。不同模式决定了 KEDA 如何根据事件源水位计算需要创建的 Job 数量。

4.3.1 默认模式(default)

KEDA 将事件源当前值除以目标值,向上取整得到 Job 数量。例如:SQS 队列有 23 条消息,目标值 queueLength: 5,则创建 ceil(23/5) = 5 个 Job。每个 Job 处理约 5 条消息。

4.3.2 精确模式(accurate)

KEDA 会跟踪当前正在运行的 Job 数量,并考虑它们可能处理的消息量。如果已有 3 个 Job 正在运行(每个预期处理 5 条),队列剩余 8 条,则只创建 ceil(8/5) = 2 个新 Job。此模式可避免过度创建。

4.3.3 积极模式(eager)

KEDA 会立即为队列中每条消息创建一个 Job(1:1 映射)。适用于每条消息处理时间差异大、需要最大并行度的场景。注意:此模式可能创建大量 Job,需配合 maxScale 限制。

模式 计算公式 适用场景 风险
default ceil(当前值 / 目标值) 消息处理时间均匀,每个 Job 处理固定数量 可能创建过多 Job
accurate ceil((当前值 - 运行中 Job 预期处理量) / 目标值) 消息处理时间长,需要精确控制并发 计算略复杂,需监控运行中 Job
eager 当前值(每条消息一个 Job) 消息处理时间差异大,需要最大并行度 可能创建大量 Job,需设置 maxScale
生产建议: 优先使用 accurate 模式。它能在保证处理速度的同时,避免资源浪费。对于消息量波动剧烈的场景,配合 pollingInterval: 5 缩短轮询间隔,提高响应速度。

4.4 失败重试策略

ScaledJob 的失败重试分为两个层级:Job 内部重试和ScaledJob 级别重试。两者协同工作,确保批处理任务的可靠性。

4.4.1 Job 内部重试(backoffLimit)

在 jobTargetRef 中设置 backoffLimit,控制单个 Job 的 Pod 重启次数。默认值为 6。如果 Pod 退出码非零,Kubernetes 会重新创建 Pod,直到达到 backoffLimit 或 Job 成功。

jobTargetRef:
  backoffLimit: 4  # 最多重试 4 次(总共执行 5 次)

4.4.2 ScaledJob 级别重试(failedJobsHistoryLimit + 重新入队)

当 Job 内部重试耗尽后,Job 标记为失败。ScaledJob 通过 failedJobsHistoryLimit 保留失败记录,但不会自动重新创建失败的 Job。真正的重试逻辑需要业务层配合:

  • 消息重新入队: 如果 Job 处理失败,应将消息重新放回队列(如 SQS 的 visibility timeout 超时后自动重新可见)。
  • 死信队列(DLQ): 超过最大重试次数的消息转入 DLQ,由人工或独立消费者处理。
  • 监控告警: 通过 failedJobsHistoryLimit 监控失败 Job 数量,触发告警。
常见误区: ScaledJob 的 failedJobsHistoryLimit 只是历史记录保留,不是自动重试机制。如果希望失败后重新创建 Job,需要在应用层实现消息重新入队逻辑,或使用 KEDA 的 scaleTriggers 配合死信队列指标。
ScaledJob 失败重试流程 Job 创建并执行 Pod 退出码非零? 是 backoffLimit 未耗尽? 重新创建 Pod 否 Job 成功完成 耗尽 Job 标记为失败

4.5 生产配置最佳实践

基于以上分析,总结 ScaledJob 在生产环境中的推荐配置:

  • 始终设置 maxScale:防止事件源突发高峰导致创建数千个 Job,耗尽集群资源。建议根据集群容量和任务资源需求计算上限。
  • 合理设置 pollingInterval:对于延迟敏感场景设为 5-10 秒;对于批量处理场景设为 30-60 秒,减少 KEDA 自身负载。
  • 启用 ttlSecondsAfterFinished:自动清理已完成 Job,避免 etcd 存储压力。建议设为 300-600 秒。
  • 结合 HPA 与 ScaledJob:对于混合负载(如 Web 服务 + 后台批处理),使用 ScaledObject 管理 Web 层,ScaledJob 管理批处理层,互不干扰。
  • 监控失败率:通过 Prometheus 监控 keda_scaledjob_failed_total 指标,设置告警阈值(如 5% 失败率)。
案例参考: 某电商平台使用 ScaledJob + RabbitMQ 处理订单超时取消任务。配置 strategy: accurate、maxScale: 20、pollingInterval: 10。高峰期每秒处理 500 条超时订单,Job 平均执行时间 2 秒,系统稳定运行无积压。