Apache Airflow 镜像缓存与约束文件的手动生成与刷新指南
2026/9/20 9:08:39 网站建设 项目流程

Apache Airflow 镜像缓存与约束文件的手动生成与刷新指南

【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow

CI 的自动维护机制(canary 构建、uv.lock变更触发)通常能自行刷新镜像缓存和约束文件,但当 GitHub Actions 运行器抖动、测试偶发失败、发布分支需要提前预热等场景出现时,就需要手工介入。本篇指南以仓库 dev/MANUALLY_GENERATING_IMAGE_CACHE_AND_CONSTRAINTS.md 为主体,结合.github/workflows下的 CI 工作流与 Breeze 源码,系统讲解三类手动操作:刷新镜像缓存、生成约束文件、批量更新已打标签的历史约束文件——并说明每一步的风险与安全边界,帮助维护者(committer / release manager)在不破坏 CI 的前提下完成这些运维动作。

为什么要手动维护:两类核心产物的作用

Apache Airflow 的 GitHub 仓库中存有两类供贡献者和用户共同使用的产物:

  • CI 与 PROD 镜像缓存(image cache):存于ghcr.io容器镜像仓库,供 CI 任务加速镜像构建。在依赖未变化的前提下,带缓存的构建约 1 分钟完成,而无缓存的全新构建通常需要约 15 分钟(见 dev/MANUALLY_GENERATING_IMAGE_CACHE_AND_CONSTRAINTS.md 中的估算)。
  • 约束文件(constraints files):锁定各 Python 版本下 Airflow 及其依赖的精确版本,既供 CI 在普通 PR 中固定依赖版本,也供用户可复现地安装已发布的 Airflow 版本。

正常情况下,二者由 CI 系统 自动维护。约束文件按版本划分为三类(详见 contributing-docs/13_airflow_dependencies_and_extras.rst):

约束文件内容用途
constraints-<PYTHON_MAJOR_MINOR>.txt当前 Airflow 源码 + PyPI 上已发布的 providers用户用 pip 安装 Airflow
constraints-source-providers-<PYTHON_MAJOR_MINOR>.txt当前 Airflow + 当前源码中的 providersCI 与 Breeze 安装
constraints-no-providers-<PYTHON_MAJOR_MINOR>.txt仅 Apache Airflow 核心,不含任何 provider不带 provider 的安装

自动化刷新机制:CI 如何自维护

CI 被设计为"自我维护":定时构建与main分支的合并构建(即canary构建)末尾带有独立维护步骤。具体流程是:

  • 所有测试通过后,最新的约束文件被提交并推送到对应的constraints-*分支;
  • 随后Push Image Cache任务刷新ghcr.io中的镜像缓存。约束先推送、缓存后刷新的顺序保证了二者针对的是同一批依赖版本;
  • 对于基于push触发构建(而非定时 canary)的分支——release-prep 的vX-Y-test分支和 providers 分支——则由独立的Refresh image registry cache工作流负责,因为内嵌在 CI 运行中的缓存刷新会被下一次 push 取消,永远无法落盘。

从该工作流源码(.github/workflows/refresh-image-registry-cache.yml)可以看到其设计要点:on.push只匹配v[0-9]+-[0-9]+-testproviders-*/v[0-9]+-[0-9]+分支;并发组按github.ref + github.event_name区分——push 触发的刷新永不取消(新的一次排队等待,GitHub 每组最多保留一个 pending 运行),而手动触发(workflow_dispatch)的运行互相替换。这正好解决注释中描述的故障模式:v3-3-test分支的 amd64 缓存只覆盖了默认 Python 版本,因为其他四个矩阵任务在完成前就被下一次 push 取消了。

手动刷新镜像缓存

什么场景需要手动刷新

  • CI 运行不稳定、GitHub Actions 运行器抖动或测试偶发失败导致自动刷新未被触发;
  • 想在向vX-Y-test分支(按仓库约定,该分支用于发布某X.Y.*系列的所有版本)推送变更前预先刷新缓存——该分支没有 PR 流程,手动刷新可显著加快后续 PR 构建;
  • 刚手动刷新过约束文件,希望缓存包含这些新约束。

方式一:通过 CI 工作流刷新(推荐)

在 Actions 页签运行Refresh image registry cache工作流:

  1. 在分支下拉框中选择要刷新缓存的分支——缓存按分支命名空间隔离,选中的分支即写入的命名空间;
  2. platform输入决定刷新linux/amd64linux/arm64或两者(默认两者)。

优先使用该方式而非本地刷新,因为:无需在本地配置 buildx / qemu;不依赖本地上传带宽;始终覆盖所有 Python 版本;使用 CI 已有的 registry 凭据,无需以 committer 身份登录ghcr.io。手动启动的运行不会被并发合并取消(工作流使用独立的并发组);对同一分支再次启动会替换正在运行的任务,而 push 触发的刷新永远不会被取消,新任务会等待在途任务完成。注意:工作流定义必须存在于所选分支上,因此发布分支需要先 backport 该工作流才能在其中刷新缓存。

方式二:本地刷新(备选)

本地刷新是工作流完全不可用时的回退方案——更慢,也更容易出错。

前置条件:除安装 docker 的buildx命令外,还需配置好 buildkit builder。若只刷新本平台(AMD 或 ARM)缓存,buildx 足够;若需双平台缓存,则需要 qemu 模拟或硬件 ARM/AMD builder。

用模拟方式(emulation)配置:模拟方式构建速度比硬件构建慢 10 倍以上,仅适合少量构建:

docker run --privileged --rm tonistiigi/binfmt --install all

用硬件 ARM/AMD 支持配置:批量构建场景建议为"另一平台"配置远程硬件 builder,并加入 docker buildx 的airflow_cachebuilder:

docker buildx create --name airflow_cache # 本地 builder docker buildx create --name airflow_cache --append HOST:PORT # 远程 builder

其中HOST:PORT的一种实现方式是通过 SSH 登录远程机器并做端口转发到其 docker engine。配置完成后docker buildx ls应能看到本地与远程两个 builder 均处于正常状态:

docker buildx ls airflow_cache docker-container airflow_cache0 unix:///var/run/docker.sock airflow_cache1 tcp://127.0.0.1:2375

刷新镜像:约束推送完成后即可重建并刷新镜像。运行 dev/refresh_images.sh 脚本会并行重建所有 Python 版本并推送至 registry。运行前需执行docker login ghcr.io,且必须是 committer 才有权限推送缓存。默认同时刷新 ARM 与 AMD:

./dev/refresh_images.sh

也可通过PLATFORM变量只刷新单平台:

export PLATFORM=linux/amd64 ./dev/refresh_images.sh
export PLATFORM=linux/arm64 ./dev/refresh_images.sh

从脚本源码(dev/refresh_images.sh)可以看到其实际逻辑:脚本强制设置ANSWER=yesCI=trueGITHUB_TOKEN="",默认PLATFORM="linux/amd64,linux/arm64";先breeze setup self-upgrade --use-current-airflow-sources,再对 Python 3.10~3.13 逐个执行breeze ci-image build --builder airflow_cache --prepare-buildx-cache --platform "${PLATFORM}" --python ${PYTHON} --verbose。PROD 镜像部分的构建命令在脚本中处于注释状态。

刷新镜像缓存安全吗?

安全。镜像缓存仅用于加速 CI 构建,最坏情况是缓存损坏后 PR 构建变慢——无缓存时从零构建约 15 分钟,"Wait for CI Image" 与 "Wait for PROD image" 步骤会多等待一会儿。缓存具备自愈能力:main分支构建全量测试通过后缓存即自动更新;新 CI 流程中Push Early Image Cache任务会在 PR 合并到main后很快刷新缓存,因此自愈通常相当迅速。

刷新命令实际做了什么

结合脚本与文档描述,整个流程为:

  1. 使用 buildx 配置的 builder 并行构建 AMD/ARM 双架构的 CI 镜像,并通过--prepare-buildx-cache标志将缓存推送到apache/airflowregistry;
  2. 基于最新源码在dist目录准备 airflow 与 providers 包;
  3. 将包移动到docker-context-files目录,供构建 PROD 镜像时使用;
  4. 使用同一批 builder 与包构建 PROD 镜像,并通过--prepare-buildx-cache推送缓存(同样并行覆盖双架构)。

手动生成约束文件

什么场景需要手动生成

当无法或不愿等待main/vY-Z-test分支测试全部通过时才需要手动生成。约束只在所有测试通过后由 CI 刷新——这通常是好事,但当测试存在无法解决的偶发问题、而你确认分支顶端代码是好的、需要发布新版本或让贡献者 PR 尽快用上新约束时,可以谨慎地手动生成。

方式一:通过 CI 工作流刷新(推荐)

运行Refresh constraints工作流即可刷新约束(例如在提升 RC 前从 PyPI 拉取新发布的 providers/依赖),无需本地执行 breeze 命令。它执行与uv.lock变更时自动运行的完全相同的步骤(内部调用该工作流),构建 CI 镜像,并提交/推送到匹配的constraints-*分支。

它一次性刷新全部三种约束风格,而非仅 PyPI 一种constraints-source-providers-X.Y.txt(源码 providers,CI 和 Breeze 安装用)、constraints-no-providers-X.Y.txt(仅核心)、constraints-X.Y.txt(PyPI 上发布的 providers,用户安装用)。只刷新 PyPI 风格会让 CI 和 Breeze 继续固定旧版本,导致三者对同一依赖的版本不一致。

手动运行总是从main启动,通过ref输入选择刷新来源的 commit-ish(分支、标签或提交哈希):

  • 不要在 GitHub 分支下拉框中选择发布分支,也不需要先把任何改动 cherry-pick 到vX-Y-test/vX-Y-stable分支——工作流定义与 breeze 来自main,源码来自ref
  • 唯一要求是 breeze 的约束生成命令能对那个ref正常工作(这些命令在近期各分支间是稳定的);
  • 手动运行仅限 release manager(与 PROD 镜像发布工作流使用同一份 allowlist),自动的uv.lockpush 运行不受限。

具体操作步骤:

  1. 在 Actions 页签打开Refresh constraints工作流;
  2. 点击Run workflow,分支保持main(这是工作流运行所在分支,不是被刷新约束的分支);
  3. Repo reference to refresh constraints from字段输入要刷新约束的 ref,例如v3-3-testv3-3-stableconstraints-3-3、某个 RC 标签或提交哈希;留空则刷新运行分支(mainconstraints-main)。目标constraints-X-Y分支由该 ref 的dev/breeze/src/airflow_breeze/branch_defaults.py自动推导——指向 3.3 线的任意 ref 都会刷新constraints-3-3
  4. 保持Upgrade deps to newest from PyPI开启(默认),以便拾取 PyPI 上最新发布的 providers/依赖;仅当想严格按该 ref 的uv.lock重新生成、不升级时才关闭它;
  5. 运行结束后,在对应的 constraints 分支上核对新提交(例如 3.3 刷新后检查constraints-3-3分支的提交历史)。

[!NOTE] 由于 RC 投票期间v3-3-testv3-3-stable可能分叉(修复会 cherry-pick 到v3-3-test),请慎重选择刷新来源的ref。应选择与即将提升的产物一致的 ref——发布时通常用v3-3-stable(或 RC 标签),而不是v3-3-test

从 .github/workflows/refresh-constraints.yml 源码可见其实现:工作流把refupgrade-to-newer-dependencies(默认true)透传给update-constraints-on-push.yml,后者先做 selective checks 推导目标 constraints 分支,再构建 CI 镜像、生成三种约束,最后经 scripts/ci/constraints/ci_diff_constraints.sh 与 scripts/ci/constraints/ci_commit_constraints.sh 提交并推送。notify-on-failure任务会在失败时向internal-airflow-ci-cdSlack 频道告警。

方式二:本地生成约束文件

若无法或不愿使用工作流,可用本地 breeze 命令生成:

breeze ci-image build --run-in-parallel --upgrade-to-newer-dependencies --answer yes breeze release-management generate-constraints --airflow-constraints-mode constraints --run-in-parallel --answer yes breeze release-management generate-constraints --airflow-constraints-mode constraints-source-providers --run-in-parallel --answer yes breeze release-management generate-constraints --airflow-constraints-mode constraints-no-providers --run-in-parallel --answer yes AIRFLOW_SOURCES=$(pwd)

约束文件生成于files/constraints-PYTHON_VERSION/constraints-*.txt。之后需要在单独克隆的仓库中检出正确的constraints-分支,再拷贝、提交并推送生成的文件。必须是 committer,且已对 apache/airflow 仓库完成 git 认证:

cd <AIRFLOW_WITH_CONSTRAINTS-MAIN_DIRECTORY> git pull cp ${AIRFLOW_SOURCES}/files/constraints-*/constraints*.txt . git diff git add . git commit -m "Your commit message here" --no-verify git push

对应的 breeze 命令定义在 dev/breeze/src/airflow_breeze/commands/release_management_commands.py 的generate-constraints命令中:它会先做环境检查、清理生成的 Python 文件,并提示确认是否已用--upgrade-to-newer-dependencies构建过 CI 镜像(若回答否,会打印推荐的构建命令并退出),随后按 Python 版本并行生成约束并列出产物。

手动生成约束安全吗?

存在轻微风险:若约束有问题影响普通 PR 和测试,可能让所有 PR 变红,直到约束修复。但通常应通过修复测试或依赖来解决,自动化 CI 流程具备自愈能力。此外,main构建本身不使用约束,会尝试将依赖升级(或降级)到pyproject.toml中依赖说明所匹配的最新版本(providers 依赖由 provider.yaml 生成,后者是 provider 依赖的唯一事实来源);约束以非--force方式推送,不会破坏任何东西;Git 保留了完整历史,可随时回滚到之前的版本。

批量更新已打标签的历史约束文件

为什么需要更新历史约束(很少发生)

极少数情况下,历史约束文件需要修复——当 Airflow 在旧约束下无法安装时。这通常发生在 Python 构建工具链(pip、setuptools、wheel、Cython 等)出现不兼容变更时:默认情况下 pip 使用build isolation,会为每个包独立安装最新版本的构建工具,而工具在包发布后发布的新版本可能破坏安装。典型例子是 2023 年 7 月 Cython 发布 3.0.0 大版本后破坏了pymssql的安装——约束中pymssql==2.2.7无法与新版 Cython 兼容,需更新为pymssql==2.2.8以恢复约束所承诺的安装可复现性。

如何更新约束

Breeze 的release-management组提供update-constraints命令,可批量更新约束:

  1. 单独检出一份 "airflow" 仓库(与当前工作仓库分开),例如放在/home/myuser/airflow-constraints
  2. 在该仓库检出constraints-main分支。命令默认期望存在名为upstream的 remote 指向 apache/airflow 仓库(标准命名约定,见 contributing-docs/10_working_with_git.rst),可通过--remote-name覆盖;
  3. main分支的 Airflow 克隆中运行breeze release-management update-constraints,并传入:
    • constraints 仓库路径;
    • 推送目标 remote 名(可选,默认upstream);
    • 需要更新约束的 airflow 版本列表;
    • 形如package==version的约束更新项(可重复指定多个);
    • 提交信息;
    • 包名必须与约束中已有的形式完全一致——PyPI 会规范化包名,同一包可能存在不同写法(如pyamlPyYaml),请先查证约束中的现有形式;
  4. constraints-<airflow-version>分支上人工核验变更是否符合预期。

该命令的 CLI 定义见 dev/breeze/src/airflow_breeze/commands/release_management_commands.py:支持--constraints-repo--remote-name--airflow-versions--commit-message--updated-constraint(可重复)、--comment-file(在约束文件首包前插入注释)、--airflow-constraints-mode等选项,且--updated-constraint--comment-file至少提供一个。

更新历史约束安全吗?

命令设计以安全为先,即使出错也有退路。几点验证手段:

  • 先加--dry-run查看将要更新的内容;
  • 即便不用--dry-run,命令也会在每次版本变更前要求确认;
  • 首次运行时可用--verbose观察命令执行的 git 操作;
  • Airflow 约束标签以--force移动(因为要移动已存在的标签),但分支不以 force 推送,仓库历史不会丢失;命令执行前会打印标签当前指向的 commit 哈希,便于追溯——通常"最终"标签与同版本的最后一个rc*标签指向相同,容易找回原指向。

命令的底层工作机制

对每个指定的 Airflow 版本,命令依次执行(见 dev/breeze/src/airflow_breeze/commands/release_management_commands.py 的实现):

  1. git fetch <remote>拉取远端;
  2. 检出constraints-<version>标签,并把constraints-<version>-fix分支硬重置到该标签(git reset --hard+git checkout -B);
  3. 就地更新约束文件——按airflow-constraints-mode选择匹配constraints-*.txt/constraints-[0-9.]*.txt/constraints-source-providers-[0-9.]*.txt/constraints-no-providers-[0-9.]*.txt的 glob,用正则逐行替换package==.*
  4. 展示git diff并请求确认;
  5. 提交变更(git commit -a --no-verify)并以--force -s方式打constraints-<version>标签;
  6. 推送constraints-<version>-fix分支(无 force)和标签到所选 remote。

命令运行示例

更新 Airflow 2.5.0~2.6.3 并将pymssql约束改为 2.2.8:

breeze release-management update-constraints --constraints-repo /home/user/airflow-constraints \ --airflow-versions 2.5.0,2.5.1,2.5.2,2.5.3,2.6.0,2.6.1,2.6.2,2.6.3 \ --updated-constraint pymssql==2.2.8 \ --commit-message "Update pymssql constraint to 2.2.8" \ --airflow-constraints-mode constraints

同时更新多个约束:

breeze release-management update-constraints --constraints-repo /home/user/airflow-constraints \ --airflow-versions 2.5.0,2.5.1,2.5.2,2.5.3,2.6.0,2.6.1,2.6.2,2.6.3 \ --updated-constraint pymssql==2.2.8 \ --updated-constraint Authlib==1.3.0 \ --commit-message "Update pymssql constraint to 2.2.8 and Authlib to 1.3.0" \ --airflow-constraints-mode constraints

小结:三类手动操作的选择建议

操作推荐路径备选路径权限要求
刷新镜像缓存Refresh image registry cache工作流dev/refresh_images.sh + buildx/qemucommitter(本地路径)
生成约束文件Refresh constraints工作流breezegenerate-constraints命令release manager(手动运行)
更新历史约束breeze release-management update-constraintscommitter

理解这三类操作的触发场景与内部机制,可以在自动化 CI 失效或发布节奏紧张时从容介入:镜像缓存只影响构建速度、可自愈;约束文件影响所有 PR 与用户的安装可复现性,务必优先走工作流路径、保持三种约束风格一致,并对历史约束的批量更新使用--dry-run先行演练。

【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询