3、DAG调度概念:DAG(有向无环图)定义、调度器角色、任务依赖关系、DAG vs 传统流水线
3.1 DAG(有向无环图)定义
DAG(Directed Acyclic Graph,有向无环图)是调度系统的核心数据结构。它由节点(Vertex)和有向边(Directed Edge)组成,且图中不存在任何环路。在调度场景中,每个节点代表一个任务或作业,每条有向边代表任务之间的依赖关系。
核心特征:
- 有向性:边具有方向,表示任务执行的先后顺序(A → B 表示 A 完成后 B 才能开始)。
- 无环性:图中不存在闭合回路,确保调度可以终止,不会出现无限循环等待。
- 拓扑排序:DAG 必然存在至少一种拓扑排序,即所有任务可以排成一个线性序列,满足依赖关系。
3.2 调度器角色
在 DAG 调度系统中,调度器(Scheduler)是核心控制组件,负责解析 DAG 结构、决定任务执行顺序、管理资源分配并处理异常。调度器通常包含以下角色:
| 角色 | 职责 | 典型实现 |
|---|---|---|
| DAG 解析器 | 读取 DAG 定义文件(YAML/JSON/Python),构建内存中的 DAG 对象,验证无环性 | Airflow DAG 解析器、Argo Workflow 控制器 |
| 拓扑排序器 | 计算 DAG 的拓扑排序,确定任务执行顺序,识别可并行执行的任务 | Kahn 算法、DFS 后序遍历 |
| 任务调度器 | 将就绪任务(所有上游依赖已完成)分配给执行器,管理任务队列 | Airflow Scheduler、Celery Worker |
| 状态管理器 | 跟踪每个任务的运行状态(pending/running/success/failed/skipped),维护 DAG Run 状态 | 数据库 + 缓存(Redis/PostgreSQL) |
| 重试与回滚控制器 | 处理任务失败重试、超时、回滚策略,触发下游依赖的跳过或回滚 | 自定义重试策略、Docker 容器回滚 |
实践要点:在 Docker 容器化调度中,调度器通常以独立容器运行,通过 API 与 Docker Daemon 交互,动态启停任务容器。调度器本身需要高可用部署,避免单点故障。
3.3 任务依赖关系
任务依赖关系定义了 DAG 中节点之间的连接规则。常见的依赖类型包括:
- 顺序依赖(Sequential):任务 B 必须在任务 A 完成后才能执行,表示为 A → B。
- 扇出依赖(Fan-out):一个上游任务触发多个下游任务并行执行,如 A → {B, C, D}。
- 扇入依赖(Fan-in):多个上游任务全部完成后才能执行下游任务,如 {A, B, C} → D。
- 条件依赖(Conditional):根据上游任务的输出结果决定是否执行下游任务(分支/跳过)。
- 时间依赖(Time-based):任务在特定时间点触发,与上游任务无关(定时调度)。
3.4 DAG vs 传统流水线
传统流水线(Pipeline)通常采用线性或阶段式结构,任务按固定顺序依次执行。DAG 调度相比传统流水线在灵活性、并行性和容错性方面有显著优势。以下从多个维度进行对比:
| 对比维度 | 传统流水线 | DAG 调度 |
|---|---|---|
| 结构 | 线性顺序,阶段式,任务串行执行 | 有向无环图,支持并行、分支、合并 |
| 依赖表达 | 隐式依赖(按阶段顺序),难以表达复杂关系 | 显式依赖(边定义),支持扇入/扇出/条件 |
| 并行度 | 低,通常一次只执行一个阶段 | 高,无依赖的任务可并行执行 |
| 容错性 | 单点失败导致整个流水线中断 | 支持局部重试、跳过、回滚,不影响无关分支 |
| 扩展性 | 添加新阶段需要修改整体流程 | 可动态添加/删除节点和边,不影响已有依赖 |
| 回滚粒度 | 全量回滚,无法精确控制 | 支持边界回滚(仅回滚受影响的下游任务) |
| 适用场景 | 简单 ETL、编译构建、固定步骤流程 | 复杂数据管道、微服务编排、CI/CD 多分支 |
注意:DAG 调度虽然灵活,但引入的复杂度也更高。需要关注 DAG 解析性能(大 DAG 可能耗时数秒)、任务状态一致性(分布式环境下状态同步)、以及循环依赖检测(必须在 DAG 提交时验证无环性)。在 Docker 容器化场景中,每个任务运行在独立容器中,DAG 调度器需要管理容器的生命周期、日志收集和资源限制。
3.5 Docker 环境中的 DAG 调度特点
当 DAG 调度与 Docker 容器化结合时,每个任务被封装为独立的 Docker 容器运行,调度器负责容器的创建、启动、监控和销毁。这种模式带来了以下优势:
- 环境隔离:每个任务拥有独立的容器环境,依赖冲突被彻底消除。
- 资源限制:通过 Docker 的 CPU/内存限制,精确控制每个任务的资源使用。
- 快速回滚:任务失败时,调度器可以快速启动旧版本镜像的容器,实现边界回滚。
- 可观测性:容器日志、指标和状态通过 Docker API 统一采集,便于监控和调试。
Docker DAG 调度核心流程:
- 调度器解析 DAG 定义,构建任务依赖图。
- 拓扑排序,识别就绪任务(所有上游依赖已完成)。
- 调度器调用 Docker API 创建并启动任务容器(指定镜像、环境变量、挂载卷)。
- 监控容器运行状态,等待容器退出(成功/失败/超时)。
- 根据容器退出码和 DAG 定义,决定下一步:继续下游、重试、跳过或回滚。
- 所有任务完成后,DAG Run 标记为成功或失败,触发后续动作(通知、清理)。