Airflow Docker Compose 快速启动测试实战:Breeze 驱动、手动运行与源码级原理解析
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
Apache Airflow 官方文档向用户提供了基于 Docker Compose 的快速启动(Quick Start)部署方案,它允许开发者在几分钟内用docker compose up拉起一套包含 webserver、scheduler、worker、triggerer 等组件的完整 Airflow 环境。为了让这套部署方案始终可用、可复现,Airflow 在 CI 中专门运行 "Test docker-compose quick start" 测试,本文基于仓库中的 docker_compose_tests.rst 贡献者文档,系统讲解如何用 Breeze 或 pytest 本地运行这套测试、如何保留并调试部署现场,并结合 docker-tests 下的真实测试源码,剖析这套测试的完整执行链路。读完本文,你将掌握 Airflow Docker Compose 部署测试的完整运行方法、关键参数与底层实现细节。
一、测试背景与设计动机
Airflow 在官方文档中通过 Running Airflow in Docker 向用户推荐 Docker Compose 快速启动方式。为了让文档中展示的这份docker-compose.yaml真正可用,而不是停留在纸面上,Airflow 项目在 CI 中持续对它进行验证——这就是docker-compose-tests测试组存在的意义。
需要特别说明的是,该测试不是在 Breeze CI 镜像内部运行的,而是在本地 Docker 环境中直接执行的。测试会使用COMPOSE_PROJECT_NAME设置为quick-start,以避免与你本机其他正在运行的 docker compose 部署发生命名冲突。
从 dev/breeze/src/airflow_breeze/commands/testing_commands_config.py 的测试分组定义可以看到,docker-compose-tests被归入 "Other Tests" 组,与system-tests、helm-tests、python-api-client-tests、airflow-e2e-tests并列,属于集成验证性质的测试套件。
二、测试的工作方式:三步走
根据原文档描述,这套测试的整体流程非常直观,共分三步:
- 构建 Airflow 生产镜像(prod image):测试必须以本地已存在的 Airflow 生产镜像为蓝本;
- 启动 Docker Compose 部署:取出项目自带的
docker-compose.yaml,用上一步构建的镜像把整套 Airflow 环境拉起来; - 触发示例 DAG 并校验结果:通过简单的 DAG 触发测试,确认 Airflow 已经正常启动,并且能够真正调度、执行一个示例 DAG。
从 test_docker_compose_quick_start.py 的源码注释可以看到测试的定位:"Simple test which reproduce setup docker-compose environment and trigger example dag",即复现 docker-compose 环境的搭建并触发示例 DAG——这正是对文档描述的代码级印证。
三、运行前置条件
在运行 Docker Compose 测试之前,需要确认以下环境就绪:
- Docker 引擎已安装并正常运行;
- Docker Compose 可用:
docker-compose(v1 独立命令)或docker compose插件(v2)二者之一存在于PATH中。测试代码在 test_docker_compose_quick_start.py 中会先调用docker.compose.version()探测,若抛DockerException则直接pytest.fail("docker composenot available. Make sure compose plugin is installed"),即缺少 compose 时测试会立即失败并给出明确提示; - 内存充足:Airflow 的 Docker Compose 部署包含多个服务,请确保 Docker Engine 分配了足够内存(官方快速启动文档建议至少 4GB,理想 8GB)。
四、用 Breeze 运行完整测试
Breeze 是 Airflow 贡献者使用的开发环境管理工具,它将镜像构建与测试执行封装成一条龙命令。运行完整测试只需两条命令:
breeze prod-image build --python 3.10 breeze testing docker-compose-tests第一条命令构建 Airflow 生产镜像(以 Python 3.10 为例),第二条命令执行 docker-compose 测试。测试对应的底层实现位于 dev/breeze/src/airflow_breeze/utils/run_tests.py 的run_docker_compose_tests()函数,其关键逻辑包括:
- 先用
docker inspect <image_name>检查镜像是否存在于本地,若不存在会提示 "The image ... does not exist locally. It should be build before running docker-compose tests.",并交互式询问是否立即构建; - 组装 pytest 命令在
docker-tests目录下执行,默认追加-s参数,以便实时看到测试的 print 输出(测试源码也通过rich.console.Console输出大量运行日志); - 通过环境变量把参数传递给测试进程:
DOCKER_IMAGE指定被测镜像、SKIP_DOCKER_COMPOSE_DELETION控制是否保留部署、AIRFLOW_UID使用当前用户 uid。
4.1 测试失败时的日志转储
测试过程中如果失败,Breeze 会把正在运行的各容器日志转储到控制台,随后关闭整个 Docker Compose 部署。在测试源码中,这一行为由print_diagnostics()函数实现(test_docker_compose_quick_start.py):它会依次输出健康检查结果、DAG Run 与 TaskInstance 状态、Docker 与 Docker Compose 版本、compose config渲染结果,以及每个服务的名称、状态、配置与完整日志,方便定位问题根因。
4.2 保留部署以便调试
默认情况下测试结束(无论成功或失败)都会执行compose.down(remove_orphans=True, volumes=True, quiet=True)删除部署。如果你需要保留部署现场进行调试,有两种方式:
- 在 Breeze 命令中传递
--skip-docker-compose-deletion标志; - 或者导出环境变量
SKIP_DOCKER_COMPOSE_DELETION为"true"。
当该开关生效时,测试结束会打印 "Skipping docker-compose deletion",并输出一条可直接复制的docker compose ...命令前缀,方便你继续手动操作这套部署。
4.3 容器等待超时控制
可以用--wait-for-containers-timeout标志指定容器的最大等待超时时间;同时也可以给命令追加-s选项,将其透传给底层 pytest,以便实时观察测试输出(原文档特别注明该行为也可通过WAIT_FOR_CONTAINERS_TIMEOUT环境变量设置)。从测试源码看,DAG 状态的轮询等待上限为 400 秒(for _ in range(400)循环中每秒查询一次),--wait-for-containers-timeout正是用来调节这类容器就绪与执行等待窗口的参数。
五、手动运行 pytest 测试
除了 Breeze,你也可以在本地 Python 环境中直接运行 pytest:
pytest docker_tests/test_docker_compose_quick_start.py手动运行的前提是:
- 本地存在一个 Airflow venv,且安装了
devextra(确保python_on_whales、requests、rich等测试依赖可用,见 docker-tests/pyproject.toml); - 设置
DOCKER_IMAGE环境变量,指向你要测试的镜像:export DOCKER_IMAGE=ghcr.io/apache/airflow/main/prod/python3.10:latest该变量在 constants.py 中定义默认值:
ghcr.io/apache/airflow/main/prod/python3.10:latest——正是breeze prod-image build --python 3.10默认构建出的镜像;pytest fixturedefault_docker_image(见 conftest.py)会优先读取DOCKER_IMAGE,未设置时回落为默认镜像。
注意:手动运行 pytest 时,--skip-docker-compose-deletion与--wait-for-containers-timeout这两个开关只能通过环境变量传递(即SKIP_DOCKER_COMPOSE_DELETION与WAIT_FOR_CONTAINERS_TIMEOUT),Breeze 的命令行标志在纯 pytest 场景下不生效。
六、调试保留下来的部署
如果你使用了SKIP_DOCKER_COMPOSE_DELETION保留了部署,想要用docker compose命令手动查看容器,需要先设置项目名,与测试保持一致:
export COMPOSE_PROJECT_NAME=quick-start也可以在 docker compose 命令中显式添加--project-name quick-start。由于测试代码在创建 compose 客户端时指定了compose_project_name="breeze-quick-start"(见 test_docker_compose_quick_start.py),调试时请以实际输出的命令前缀为准。此外,测试每次重新运行时,会先自动compose.down关闭上一次的部署,再启动新部署,因此重复执行测试是安全的。
七、手动运行 Docker Compose 部署(独立于 pytest)
你还可以完全不经过 pytest,手动用刚构建的镜像拉起这套 Docker Compose 环境:
export AIRFLOW_IMAGE_NAME=ghcr.io/apache/airflow/main/prod/python3.10:latest然后按照 Running Airflow in Docker 的指引操作,但务必使用仓库源码自带的 compose 文件:它位于仓库的airflow-core/docs/howto/docker-compose/docker-compose.yaml(测试代码也正是从AIRFLOW_ROOT_PATH / "airflow-core" / "docs" / "howto" / "docker-compose" / "docker-compose.yaml"复制该文件到临时目录后启动的)。随后即可用常规的docker compose/docker命令调试运行中的实例,也可以连接 Airflow 的 Web UI(默认localhost:8080)手动触发 DAG 做验证。
八、源码级剖析:测试到底做了什么
为了帮助读者深入理解这套测试,下面结合源码逐段拆解核心用例test_trigger_dag_and_wait_for_result(test_docker_compose_quick_start.py):
- 准备临时目录:用
tmp_path_factory.mktemp("airflow-quick-start")创建独立目录,把仓库内的docker-compose.yaml复制进去,并创建dags、logs、plugins、config四个子目录(对应 compose 文件中的卷挂载点); - 写入
.env文件:内容为AIRFLOW_UID=<当前用户 uid>,确保容器内以当前用户身份运行,避免文件权限问题(compose 文件默认user: "${AIRFLOW_UID:-50000}:0"); - 清理旧部署:执行
compose.down(remove_orphans=True, volumes=True, quiet=True),保证测试从干净状态开始; - 启动部署:
compose.up(detach=True, wait=True, ...)拉起整套服务; - 确保 DAG 已解析:在
airflow-dag-processor服务中执行airflow dags reserialize,强制元数据库中的 DAG 序列化数据就绪; - 健康检查:调用
GET /api/v2/monitor/health,断言metadatabase.status == "healthy"; - 触发 DAG:先
PATCH /api/v2/dags/example_simplest_dag将is_paused置为false,再POST /api/v2/dags/example_simplest_dag/dagRuns创建一次 DAG Run(dag_run_id=test_dag_run_id,logical_date固定为2020-06-11T18:00:00+00:00); - 轮询等待终态:
wait_for_terminal_dag_state()每秒查询一次 DAG Run 状态,最多等待 400 秒,直到进入success或failed; - 断言结果:最终断言 DAG Run 状态为
success,否则测试失败并转储全部诊断日志。
所有 API 请求都通过api_request()封装(test_docker_compose_quick_start.py),它先借助tests_common.test_utils.api_client_helpers.generate_access_token获取 JWT 访问令牌,再携带Authorization: Bearer <token>请求/api/v2接口——这恰好验证了 Airflow 3.x 中由 FabAuthManager 负责认证、JWT 签发访问令牌的完整链路(compose 文件中也配置了AIRFLOW__CORE__AUTH_MANAGER为 FAB 认证管理器及AIRFLOW__API_AUTH__JWT_SECRET等参数)。
仓库还包含第二个用例test_airflow_uid_default_in_chown(test_docker_compose_quick_start.py):它渲染 compose 配置并断言airflow-init服务初始化命令中的chown参数——当未显式设置AIRFLOW_UID时,默认以50000作为用户 ID(chown -R "50000:0" /opt/airflow/),而不是空用户组写法。该用例守护了 compose 快速启动在默认参数下的文件权限正确性,也是文档中AIRFLOW_UID默认值 50000 的代码级证据。
九、测试运行的环境变量速查
结合原文档与源码,整理本测试涉及的全部环境变量与等效 Breeze 开关如下:
| 环境变量 | Breeze 开关 | 默认值 | 作用 |
|---|---|---|---|
DOCKER_IMAGE | --image-name(间接) | ghcr.io/apache/airflow/main/prod/python3.10:latest | 被测 Airflow 生产镜像 |
SKIP_DOCKER_COMPOSE_DELETION | --skip-docker-compose-deletion | 未设置(即删除) | 测试结束后是否保留部署 |
WAIT_FOR_CONTAINERS_TIMEOUT | --wait-for-containers-timeout | 由 pytest 调用约定 | 容器就绪等待超时 |
COMPOSE_PROJECT_NAME | --project-name(调试时) | 测试内部固定为breeze-quick-start | Docker Compose 项目名,避免冲突 |
AIRFLOW_UID | — | 50000 | 容器内运行 Airflow 的用户 ID |
HOST_PORT | — | localhost:8080 | API 服务访问地址(见测试源码DOCKER_COMPOSE_HOST_PORT) |
INCLUDE_SUCCESS_OUTPUTS | --include-success-outputs | 未设置 | 成功后是否同样转储诊断输出 |
十、总结
Airflow 的 Docker Compose 测试是一套"轻量而完整"的端到端验证:它构建生产镜像、拉起官方文档同款 compose 部署、通过 REST API 触发真实 DAG 并断言执行成功,从而保证对外发布的快速启动方案在每次代码变更后依然开箱即用。通过 Breeze 的breeze testing docker-compose-tests或直接运行 test_docker_compose_quick_start.py,任何贡献者都可以在本地复现 CI 中的这一验证环节;配合SKIP_DOCKER_COMPOSE_DELETION保留现场,再结合print_diagnostics转储的服务日志,即可高效定位部署或镜像相关问题。关于 Airflow 更广泛的测试体系(单元测试、集成测试、系统测试等),可进一步阅读 09_testing.rst 测试总览文档。
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考