3、DAG调度概念:DAG(有向无环图)定义、调度器角色、任务依赖关系、DAG vs 传统流水线

3.1 DAG(有向无环图)定义

DAG(Directed Acyclic Graph,有向无环图)是调度系统的核心数据结构。它由节点(Vertex)和有向边(Directed Edge)组成,且图中不存在任何环路。在调度场景中,每个节点代表一个任务或作业,每条有向边代表任务之间的依赖关系。

核心特征:
  • 有向性:边具有方向,表示任务执行的先后顺序(A → B 表示 A 完成后 B 才能开始)。
  • 无环性:图中不存在闭合回路,确保调度可以终止,不会出现无限循环等待。
  • 拓扑排序:DAG 必然存在至少一种拓扑排序,即所有任务可以排成一个线性序列,满足依赖关系。
DAG 基本结构示例 任务 A 任务 B 任务 C 任务 D 任务 E 图例 ● 节点 = 任务 → 箭头 = 依赖 无环路 = 可调度 拓扑序: A→B→C→D→E 或 A→C→B→E→D

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):任务在特定时间点触发,与上游任务无关(定时调度)。
任务依赖关系类型 任务 A 任务 B 顺序 任务 A 任务 B 任务 C 扇出 任务 A 任务 B 任务 C 扇入 任务 A 任务 B 任务 C 条件 成功→B 失败→C 定时触发 任务 A 时间

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 调度核心流程:
  1. 调度器解析 DAG 定义,构建任务依赖图。
  2. 拓扑排序,识别就绪任务(所有上游依赖已完成)。
  3. 调度器调用 Docker API 创建并启动任务容器(指定镜像、环境变量、挂载卷)。
  4. 监控容器运行状态,等待容器退出(成功/失败/超时)。
  5. 根据容器退出码和 DAG 定义,决定下一步:继续下游、重试、跳过或回滚。
  6. 所有任务完成后,DAG Run 标记为成功或失败,触发后续动作(通知、清理)。