CronJob 做不到的事
作为单个 Pod 的调度器,CronJob 相当好用。缺口出现在批处理超过一个 Pod 的那一刻,而每支团队都会撞上同样的三点。
- CronJob 之间没有依赖。顺序只能靠猜测时间偏移来表达,一旦某个作业跑久了就会悄悄失效
- 默认历史很浅:保留三次成功和一次失败,日志随 Pod 一起消失。要解释上周二的失败往往无从查起
- 如果连续错过超过一百次调度且未设置 startingDeadlineSeconds,CronJob 会彻底停止调度,直到有人发现
作为单个 Pod 的调度器,CronJob 相当好用。缺口出现在批处理超过一个 Pod 的那一刻,而每支团队都会撞上同样的三点。
Dagu 为每个步骤创建一个单容器 Job,等待其结束,流式读取 Pod 日志,并使用已终止容器的退出码。日志写入运行历史,因此在 Pod 和 Job 都消失之后依然存在。
Kubernetes 暴露的是合并后的容器日志流,因此该步骤类型的 stdout 与 stderr 会作为单一交错的流到达。
# k8s-nightly-etl.yaml
schedule: "0 2 * * *"
max_active_runs: 1
kubernetes:
namespace: batch
service_account: dagu-runner
resources:
requests:
cpu: "250m"
memory: "512Mi"
steps:
- id: extract
action: k8s.run
with:
image: ghcr.io/example/extract:1.4.0
command: extract --source warehouse
retry_policy:
limit: 2
interval_sec: 120
- id: transform
action: k8s.run
with:
image: ghcr.io/example/transform:1.4.0
depends: extract
- id: load
action: k8s.run
with:
image: ghcr.io/example/load:1.4.0
resources:
limits:
cpu: "2"
memory: "4Gi"
depends: transform
handler_on:
failure:
run: ./scripts/page-oncall.sh
mail_on:
failure: true
这是 CronJob 无论如何都做不到的部分,因为 CronJob 的作用域就是它所在的集群。跨两个集群、或跨集群与本地系统的工作流,在 Kubernetes 内部没有容身之处。
# k8s-promote-across-clusters.yaml
max_active_runs: 1
kubernetes:
namespace: batch
service_account: dagu-runner
steps:
- id: migrate_staging
action: k8s.run
with:
context: staging
image: ghcr.io/example/migrator:3.2
command: migrate up
- id: verify_staging
run: ./scripts/verify-schema.sh staging
depends: migrate_staging
- id: approve
action: human.task
with:
prompt: Promote the migration to production?
form:
type: object
properties:
change_ticket:
type: string
confirmed:
type: boolean
required: [change_ticket, confirmed]
depends: verify_staging
- id: migrate_production
action: k8s.run
with:
context: production
image: ghcr.io/example/migrator:3.2
command: migrate up
depends: approve
- id: record
run: ./scripts/record-change.sh "${steps.approve.outputs.change_ticket}"
depends: migrate_production
handler_on:
failure:
run: ./scripts/page-oncall.sh
mail_on:
failure: true
Argo Workflows 是同一问题的 Kubernetes 原生答案,对相当一类工作而言它才是正确选择。区别在于编排器住在哪里。
Kubernetes 步骤类型是刻意收窄的。它很好地覆盖了批处理步骤的常见形态,但并不试图成为通用的清单应用工具。
文档未列出的字段请一律视为不支持。需要完整 Job spec 的工作,更适合用命令步骤调用 kubectl,或交给 Argo。
FAQ
不必。它按显式 kubeconfig、常规 kubeconfig 加载规则、集群内配置的顺序解析集群,两种方式都可行。放在集群外正是跨集群与跨边界工作流成立的前提;当它触达的一切都在该集群内时,放在集群内也没问题。
那种做法只给你顺序:包装 Pod 的退出码掩盖了具体哪个阶段失败,重试会重跑整个脚本,日志则是一团无法区分的流并随 Pod 消失。Dagu 为每个步骤创建一个 Job,因此每个阶段都有自己的状态、自己的重试和自己保留的日志。
不共享。每个步骤是独立的 Job,因而是独立的 Pod。步骤间传递数据请使用对象存储、挂载到各步骤的持久卷,或用步骤输出传递小值,与在独立 Job 之间的做法一致。
取消、终止与超时路径即使在 cleanup_policy 设为 keep 时也会强制清理,因此被中止的运行不会遗留 Job。正常运行时的默认行为是完成后删除 Job。
只有当编排器位于集群之外对你有价值时才应如此。若所有任务都是同一集群内的容器,并且你希望工作流是受同一 RBAC 与 GitOps 管理的 Kubernetes 对象,Argo 更合适。Dagu 适合把集群作业与主机、API 和人工审批混合在一起的图。