Airflow Summit 2025 将于 10 月 07-09 日举行。立即注册享受早鸟票优惠!

构建镜像

在深入了解 Airflow 镜像的构建方式之前,让我们先解释一下为什么您可能需要构建自定义容器镜像,并展示几种典型的构建方法。

镜像扩展的快速入门场景

您想要构建自己镜像的最常见场景是添加新的 apt 包、添加新的 PyPI 依赖项(无论是单独添加还是通过 requirements.txt 文件),以及将 dags 嵌入镜像中。

这些场景的 Dockerfile 示例位于下方,您可以继续阅读以了解更复杂的情况,这些情况可能涉及扩展或定制镜像。您将在下方找到有关更复杂场景的更多信息,但如果您的目标是快速扩展 Airflow 镜像以添加新的提供者、包等,那么这里是快速入门指南。

添加新的 apt

以下示例将 vim 添加到 Airflow 镜像中。通过 apt 添加包时,您应该在运行 apt 命令时切换到 root 用户,但安装完成后不要忘记切换回 airflow 用户。

docker-stack-docs/docker-examples/extending/add-apt-packages/Dockerfile

FROM apache/airflow:3.0.0
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
         vim \
  && apt-get autoremove -yqq --purge \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*
USER airflow

单独添加新的 PyPI

以下示例将 PyPI 中的 lxml Python 包添加到镜像中。通过 pip 添加包时,您需要使用 airflow 用户而不是 root。尝试以 root 用户身份安装 pip 包将会失败,并给出相应的错误消息。

注意

在下面的示例中,我们还添加了要安装的 apache-airflow 包 - 版本与您使用的镜像版本完全相同。这不是严格必需的,但始终安装与您正在使用的 Airflow 版本相同的 apache-airflow 版本是一个好习惯。这样可以确保您使用的版本与您正在扩展的版本相同。在某些情况下,如果您的新包具有冲突的依赖项,pip 可能会决定为您降级或升级 apache-airflow,因此明确添加它是一个好习惯 - 这样,如果您有冲突的需求,您将收到带有冲突信息的错误消息,而不是 airflow 的意外降级或升级。如果您升级 airflow 基础镜像,也应该更新版本以匹配新的 airflow 版本。

注意

创建自定义镜像意味着您还需要保持一定程度的自动化,因为当您想要安装的包或 Airflow 升级时,您需要重新创建镜像。请不要忘记保留这些脚本。还要记住,在运行纯 Python 任务的情况下,您可以使用 Python Virtualenv 函数,它将在运行时动态地源化并安装 Python 依赖项。从 Airflow 2.8.0 开始,Virtualenvs 也可以被缓存。

docker-stack-docs/docker-examples/extending/add-pypi-packages/Dockerfile

FROM apache/airflow:3.0.0
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" lxml

从 requirements.txt 添加包

以下示例从 PyPI 的 requirements.txt 中添加了一些 Python 包到镜像中。请注意,与添加单个包类似,您需要使用 airflow 用户而不是 root。尝试以 root 用户身份安装 pip 包将会失败,并给出相应的错误消息。

注意

在下面的示例中,我们还添加了要安装的 apache-airflow 包 - 版本与您使用的镜像版本完全相同。这不是严格必需的,但始终安装与您正在使用的 Airflow 版本相同的 apache-airflow 版本是一个好习惯。这样可以确保您使用的版本与您正在扩展的版本相同。在某些情况下,如果您的新包具有冲突的依赖项,pip 可能会决定为您降级或升级 apache-airflow,因此明确添加它是一个好习惯 - 这样,如果您有冲突的需求,您将收到带有冲突信息的错误消息,而不是 airflow 的意外降级或升级。如果您升级 airflow 基础镜像,也应该更新版本以匹配新的 airflow 版本。

docker-stack-docs/docker-examples/extending/add-requirement-packages/Dockerfile

FROM apache/airflow:3.0.0
COPY requirements.txt /
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" -r /requirements.txt

docker-stack-docs/docker-examples/extending/add-requirement-packages/requirements.txt

lxml
beautifulsoup4

嵌入 dags

以下示例将 test_dag.py 添加到镜像的 /opt/airflow/dags 文件夹中。

docker-stack-docs/docker-examples/extending/embedding-dags/Dockerfile

FROM apache/airflow:3.0.0

COPY --chown=airflow:root test_dag.py /opt/airflow/dags

docker-stack-docs/docker-examples/extending/embedding-dags/test_dag.py

import datetime

import pendulum

from airflow.models.dag import DAG
from airflow.providers.standard.operators.empty import EmptyOperator

now = pendulum.now(tz="UTC")
now_to_the_hour = (now - datetime.timedelta(0, 0, 0, 0, 0, 3)).replace(minute=0, second=0, microsecond=0)
START_DATE = now_to_the_hour
DAG_NAME = "test_dag_v1"

dag = DAG(
    DAG_NAME,
    schedule="*/10 * * * *",
    default_args={"depends_on_past": True},
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
)

run_this_1 = EmptyOperator(task_id="run_this_1", dag=dag)
run_this_2 = EmptyOperator(task_id="run_this_2", dag=dag)
run_this_2.set_upstream(run_this_1)
run_this_3 = EmptyOperator(task_id="run_this_3", dag=dag)
run_this_3.set_upstream(run_this_2)

使用环境变量添加 Airflow 配置

以下示例将 airflow 配置添加到镜像中。$AIRFLOW_HOME 目录中的 airflow.cfg 文件包含 Airflow 的配置。您可以使用以下格式通过环境变量为这些 Airflow 配置设置选项:AIRFLOW__{SECTION}__{KEY}(注意双下划线)。

docker-stack-docs/docker-examples/extending/add-airflow-configuration/Dockerfile

FROM apache/airflow:3.0.0
ENV AIRFLOW__CORE__LOAD_EXAMPLES=True
ENV AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=my_conn_string

扩展与定制镜像

您可能想快速了解自己是需要扩展还是定制现有的 Apache Airflow 镜像。本章将简短地回答这些问题。

以下是两种方法的比较

扩展

定制

使用熟悉的 'FROM' 镜像构建模式

仅需要关于镜像的基础知识

构建速度快

生成高度优化大小的镜像

可以从自定义 airflow 源 (forks) 构建

可以在气隙 (air-gapped) 系统上构建

总结:如果您需要构建自定义镜像,从“扩展”开始更容易。但是,如果您的依赖项需要编译步骤,或者您需要从经过安全审查的包构建镜像,切换到“定制”镜像可以提供更优化的镜像。例如,如果我们比较通过“扩展”和“定制”构建的等效镜像,它们最终的大小分别为 1.1GB 和 874MB - 定制镜像在大小上优化了 20%。

注意

您也可以将两者结合起来 - 在一个流程中定制和扩展镜像。您可以首先使用 customization 方法构建您的优化基础镜像(例如由您的管理团队),其中包含所有需要大量编译的依赖项,然后您可以将其发布到您的注册表中,让其他人使用 FROM 扩展您的镜像并添加他们自己的轻量级依赖项。这很好地反映了“普通”用户通常会扩展镜像,“高级”用户会定制镜像的这种分工。

Airflow Summit 2020 的 生产 Docker 镜像 演讲提供了更多关于生产镜像的背景、架构以及定制/扩展方法的详细信息。

为什么要定制镜像?

Apache Airflow 社区发布的 Docker 镜像被视为 Apache Airflow 的 参考镜像。然而,Airflow 有超过 60 个社区维护的提供者(可通过 extras 安装),并且一些默认安装的 extras/提供者并非所有人都会使用,有时需要其他的 extras/提供者,有时(实际上经常)您需要添加自己的自定义依赖项、包甚至自定义提供者。

在 Kubernetes 和 Docker 术语中,这意味着您需要另一个具有特定要求的镜像。这就是您应该学习如何构建自己的 Docker(或更准确地说是容器)镜像的原因。您可能会倾向于使用 参考镜像 并在容器启动时动态安装新包,但这出于多种原因是一个糟糕的主意 - 从构建的脆弱性到安装这些包所需的额外时间 - 每次每个容器启动时都必须发生。在生产环境中处理新依赖项和要求的唯一可行方法是构建并使用您自己的镜像。只有在“爱好者”和“快速入门”场景下,当您想要快速迭代尝试时,才应该动态安装依赖项,然后用您自己的镜像替代。

镜像构建入门

注意

Dockerfile 在特性和向后兼容性方面并非严格遵循 Apache Airflow 的 SemVer 方法。虽然 Airflow 代码严格遵循该方法,但 Dockerfile 实际上是一种使用标准容器方法便捷地打包 Airflow 的方式,偶尔在构建过程或镜像的入口点会有一些需要微调的更改。有关更改和所需调整的详细信息可以在 变更日志 中找到。

您将会遇到几种最典型的场景,这里有一个快速实现目标的简短秘诀。要了解详细信息,您可以继续阅读,但对于使用典型工具的简单情况,以下是简单的示例。

在最简单的情况下,构建镜像包括以下步骤:

  1. 创建您自己的 Dockerfile(命名为 Dockerfile),并在其中添加

  • 您的镜像应该基于什么的信息(例如 FROM: apache/airflow:3.0.0-python3.9

  • 应在您的镜像中执行的附加步骤(通常以 RUN <command> 的形式)

  1. 构建您的镜像。这可以使用 docker CLI 工具完成,以下示例假设使用 docker。还有其他工具,如 kanikopodman,它们允许您构建镜像,但 docker 仍然是最流行和开发者友好的工具。典型的镜像构建方式如下所示(my-image:0.0.1 是您的镜像包含版本的自定义标签)。如果您使用某种注册表,您将从其中使用镜像,它通常命名为 registry/image-name 的形式。镜像名称必须配置为您将部署镜像的部署方法。例如,可以在 在 Docker 中运行 Airflow 中或在 Apache Airflow 的 Helm Chart 中设置为镜像名称。

docker build . -f Dockerfile --pull --tag my-image:0.0.1
  1. [可选] 测试镜像。Airflow 包含一个允许您测试镜像的工具。但是,此步骤需要在本地检出或解压 Airflow 源代码。如果您拥有源代码,可以通过运行此命令(在 airflow 根文件夹中)来测试镜像。输出将告诉您镜像是否“准备就绪”。

./scripts/ci/tools/verify_docker_image.sh PROD my-image:0.0.1
  1. 在本地构建镜像后,通常有几种方法可以使其可用于您的部署

  • 对于 docker-compose 部署,如果您已经构建了镜像,并想在需要时继续使用 docker build 手动构建镜像,您可以编辑 docker-compose.yaml 文件,并将“apache/airflow:<version>”镜像替换为您刚刚构建的镜像 my-image:0.0.1 - 它将从您的本地 Docker Engine 缓存中使用。您也可以简单地设置 AIRFLOW_IMAGE_NAME 变量指向您的镜像,docker-compose 将自动使用它,无需修改文件。

  • 对于 docker-compose 部署,您还可以将镜像构建委托给 docker-compose。为此,打开您的 docker-compose.yaml 文件,并搜索“为了添加自定义依赖项”这句话。按照这些说明注释掉“image”行并取消注释“build”行。这是一个标准的 docker-compose 功能,您可以在 Docker Compose 构建参考 中阅读相关内容。运行 docker-compose build 命令来构建镜像。与上一种情况类似,镜像存储在 Docker engine 缓存中,Docker Compose 将从那里使用它。docker-compose build 命令底层使用的就是您可以手动运行的 docker build 命令。

  • 对于一些面向开发的 Kubernetes 部署,您可以将镜像直接加载到 Kubernetes 集群中。像 kindminikube 这样的集群具有专用的 load 方法将镜像加载到集群中。

  • 最后但同样重要的是 - 您可以将镜像推送到远程注册表,这是存储和暴露镜像的最常见方式,也是最便携的发布镜像的方式。Docker-Compose 和 Kubernetes 都可以利用通过注册表暴露的镜像。

扩展镜像

如果您只需要添加一些不需要编译的依赖项,扩展镜像是最容易的。Linux 的编译框架(即 build-essential)非常庞大,而对于生产镜像来说,大小是需要优化的重要因素,因此我们的生产镜像不包含 build-essential。如果您需要像 gcc 或 g++ 或 make/cmake 等编译器,这些在镜像中找不到,建议您遵循“定制”路线。

如何扩展镜像 - 这是您很可能熟悉的方式 - 只需使用 Dockerfile 的 FROM 指令构建一个新镜像,然后添加您需要的任何东西。然后您可以使用 apt 添加 Debian 依赖项,或者使用 pip install 添加 PyPI 依赖项,或者任何其他您需要的东西。

基础镜像

有两种类型的镜像您可以用来扩展您的镜像

  1. 常规 Airflow 镜像,包含最常见的 extras 和 provider,以及 AMD64 平台支持的所有后端数据库客户端和 ARM64 平台的 Postgres。

  2. 精简 (Slim) Airflow 镜像,这是一个最小镜像,包含为 AMD64 平台预安装的所有支持的后端数据库客户端和 ARM64 平台的 Postgres,但不包含任何 extras 或 provider,除了 4 个默认 provider。

注意

精简镜像中的数据库客户端和数据库提供者 精简镜像预装了数据库客户端以便于您的使用,但是包含的默认提供者不包括任何数据库提供者。您仍然需要手动安装任何您需要的数据库提供者

注意

精简镜像与常规镜像的区别。

精简镜像与常规镜像相比体积小 (~500 MB vs ~1.1GB),您可能需要添加更多的包和 provider 才能使其适用于您的情况(但如果您只使用 provider 的一小部分,它可能是个不错的起点)。

精简镜像中的依赖项版本可能与预安装 provider 时使用的版本不同,原因很简单,因为核心 Airflow 对其自身的版本限制可能较少。当您安装某些 provider 时,如果这些 provider 对同一依赖项有不同的限制,它们可能需要降级某些依赖项。

镜像命名约定

镜像

Python

标准镜像

精简镜像

最新默认

3.9

apache/airflow:latest

apache/airflow:slim-latest

默认

3.9

apache/airflow:X.Y.Z

apache/airflow:slim-X.Y.Z

最新

3.9,3.10,3.11,3.12

apache/airflow:latest-pythonN.M

apache/airflow:slim-latest-pythonN.M

特定

3.9,3.10,3.11,3.12

apache/airflow:X.Y.Z-pythonN.M

apache/airflow:slim-X.Y.Z-pythonN.M

  • “latest”镜像始终是最新发布的稳定版本。

基础镜像的重要注意事项

您应该注意几点

  • airflow 的生产镜像使用“airflow”用户,因此如果您想以 root 用户身份添加一些工具,您需要使用 Dockerfile 的 USER 指令切换到该用户,并在完成后切换回 airflow 用户。此外,您应该记住遵循 Dockerfile 的最佳实践,以确保您的镜像精简且体积小。

  • 您可以使用常规的 pip install 命令(以及 Airflow 2.9 中包含的 Dockerfile 中实验性的 uv pip install)来安装 PyPI 包。应该使用常规的 install 命令,但是您应该记住在命令中添加 apache-airflow==${AIRFLOW_VERSION} 以避免意外升级或降级 Apache Airflow 的版本。根据场景,您也可以使用约束文件。从 Airflow 2.9.0 中可用的 Dockerfile 开始,用于构建镜像的约束文件位于 ${HOME}/constraints.txt.

  • Apache Airflow 中的 PyPI 依赖项安装在“airflow”用户的 ~/.local virtualenv 中,因此 PIP 包安装到 ~/.local 文件夹,就像运行 PIP 时指定了 --user 标志一样。这样做的好处是,当您使用 --system-site-packages 标志创建 virtualenv 时,创建的 virtualenv 将自动具有与本地 airflow 安装相同的所有包。还要注意,在 pip 中使用 --no-cache-dir 或在 uv 中使用 --no-cache 是个好主意,可以帮助减小镜像体积。

  • 如果您的 apt 或 PyPI 依赖项需要一些 build-essential 或其他需要编译 Python 依赖项的包,那么您的最佳选择是遵循“定制镜像”路线,因为这样可以构建高度优化(针对大小)的镜像。但是,这需要您使用作为 Apache Airflow 源代码一部分发布的 Dockerfile(也可在 Dockerfile 获取)。

  • 您还可以通过使用 Airflow 的 COPY 指令简单地添加 dags 来将它们嵌入到镜像中。生产镜像中的 dags 位于 /opt/airflow/dags 文件夹中。

  • 您可以在不需要任何 Airflow 源代码的情况下构建镜像。只需将 Dockerfile 和引用的任何文件(如 DAG 文件)放在单独的目录中,并运行命令 docker build . --pull --tag my-image:my-tag(其中 my-image 是您想命名的名称,my-tag 是您想给镜像打的标签)。

  • 如果您的镜像扩展方式需要创建可写目录,您必须记住在您的 RUN 命令中添加 umask 0002 步骤。这是为了适应我们使用任意用户运行镜像的方法所必需的。这样的用户将始终以 GID=0 运行 - 入口点将阻止非 root 的 GID。您可以在入口点文档的 任意 docker 用户 部分阅读更多相关信息。umask 0002 在您进入镜像时默认设置,因此您在运行时创建的任何目录默认都将具有 GID=0 并可被组写入。

镜像扩展示例

设置自有 Airflow providers 的示例

Airflow Providers 是独立于核心 Airflow 发布的,有时您可能只想升级特定的 provider 以修复一些问题或使用该 provider 版本中可用的功能。以下是一个如何实现这一目标的示例

docker-stack-docs/docker-examples/extending/custom-providers/Dockerfile

FROM apache/airflow:3.0.0
RUN pip install "apache-airflow==${AIRFLOW_VERSION}" --no-cache-dir apache-airflow-providers-docker==2.5.1

添加 Airflow Provider 包和 apt 包的示例

以下示例添加了 apache-spark airflow-providers,它需要 PyPI 中的 java 和 python 包。

docker-stack-docs/docker-examples/extending/add-providers/Dockerfile

FROM apache/airflow:3.0.0
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
         openjdk-17-jre-headless \
  && apt-get autoremove -yqq --purge \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*
USER airflow
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" apache-airflow-providers-apache-spark==5.1.1

添加 apt 包的示例

以下示例将 vim 添加到 airflow 镜像中。

docker-stack-docs/docker-examples/extending/add-apt-packages/Dockerfile

FROM apache/airflow:3.0.0
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
         vim \
  && apt-get autoremove -yqq --purge \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*
USER airflow

添加 PyPI 包的示例

以下示例将 PyPI 中的 lxml Python 包添加到镜像中。

docker-stack-docs/docker-examples/extending/add-pypi-packages/Dockerfile

FROM apache/airflow:3.0.0
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" lxml

添加带有约束的 PyPI 包的示例

以下示例将 PyPI 中的 lxml Python 包添加到镜像中,并使用安装 airflow 时使用的约束。这允许您使用与给定 Airflow 版本已测试通过的包版本。如果您不想使用可能在您使用的 Airflow 版本之后发布的更新版本,也可以使用此方法。

docker-stack-docs/docker-examples/extending/add-pypi-packages-constraints/Dockerfile

FROM apache/airflow:3.0.0
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" lxml --constraint "${HOME}/constraints.txt"

使用 uv 添加 PyPI 包的示例

以下示例使用 uv 将 PyPI 中的 lxml Python 包添加到镜像中。这是一个实验性功能,因为 uv 是 Python 生态系统中一个非常快速但也非常新的工具。

docker-stack-docs/docker-examples/extending/add-pypi-packages-uv/Dockerfile

FROM apache/airflow:3.0.0

# The `uv` tools is Rust packaging tool that is much faster than `pip` and other installer
# Support for uv as installation tool is experimental

RUN uv pip install --no-cache "apache-airflow==${AIRFLOW_VERSION}" lxml

从 requirements.txt 添加包的示例

以下示例从 PyPI 的 requirements.txt 中添加了一些 Python 包到镜像中。请注意,与添加单个包类似,您需要使用 airflow 用户而不是 root。尝试以 root 用户身份安装 pip 包将会失败,并给出相应的错误消息。

注意

在下面的示例中,我们还添加了要安装的 apache-airflow 包 - 版本与您使用的镜像版本完全相同。这不是严格必需的,但始终安装与您正在使用的 Airflow 版本相同的 apache-airflow 版本是一个好习惯。这样可以确保您使用的版本与您正在扩展的版本相同。在某些情况下,如果您的新包具有冲突的依赖项,pip 可能会决定为您降级或升级 apache-airflow,因此明确添加它是一个好习惯 - 这样,如果您有冲突的需求,您将收到带有冲突信息的错误消息,而不是 airflow 的意外降级或升级。如果您升级 airflow 基础镜像,也应该更新版本以匹配新的 airflow 版本。

docker-stack-docs/docker-examples/extending/add-requirement-packages/Dockerfile

FROM apache/airflow:3.0.0
COPY requirements.txt /
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" -r /requirements.txt

docker-stack-docs/docker-examples/extending/add-requirement-packages/requirements.txt

lxml
beautifulsoup4

需要可写目录时的示例

以下示例添加了一个新目录,该目录应该对于运行容器的任何任意用户都是可写的。

docker-stack-docs/docker-examples/extending/writable-directory/Dockerfile

FROM apache/airflow:3.0.0
RUN umask 0002; \
    mkdir -p ~/writeable-directory

添加需要编译的包时的示例

以下示例添加了 mpi4py 包,它需要 build-essentialmpi compiler

docker-stack-docs/docker-examples/extending/add-build-essential-extend/Dockerfile

FROM apache/airflow:3.0.0
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
         build-essential libopenmpi-dev \
  && apt-get autoremove -yqq --purge \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*
USER airflow
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" "mpi4py==3.1.6"

构建后的镜像大小约为 1.1 GB。正如您稍后将看到的,如果您使用“定制”而不是“扩展”镜像,可以实现镜像大小减少 20%。

想要嵌入 dags 时的示例

以下示例将 test_dag.py 添加到镜像的 /opt/airflow/dags 文件夹中。

docker-stack-docs/docker-examples/extending/embedding-dags/Dockerfile

FROM apache/airflow:3.0.0

COPY --chown=airflow:root test_dag.py /opt/airflow/dags

docker-stack-docs/docker-examples/extending/embedding-dags/test_dag.py

import datetime

import pendulum

from airflow.models.dag import DAG
from airflow.providers.standard.operators.empty import EmptyOperator

now = pendulum.now(tz="UTC")
now_to_the_hour = (now - datetime.timedelta(0, 0, 0, 0, 0, 3)).replace(minute=0, second=0, microsecond=0)
START_DATE = now_to_the_hour
DAG_NAME = "test_dag_v1"

dag = DAG(
    DAG_NAME,
    schedule="*/10 * * * *",
    default_args={"depends_on_past": True},
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
)

run_this_1 = EmptyOperator(task_id="run_this_1", dag=dag)
run_this_2 = EmptyOperator(task_id="run_this_2", dag=dag)
run_this_2.set_upstream(run_this_1)
run_this_3 = EmptyOperator(task_id="run_this_3", dag=dag)
run_this_3.set_upstream(run_this_2)

使用环境变量更改 airflow 配置的示例

以下示例将 airflow 配置更改添加到 airflow 镜像中。

docker-stack-docs/docker-examples/extending/add-airflow-configuration/Dockerfile

FROM apache/airflow:3.0.0
ENV AIRFLOW__CORE__LOAD_EXAMPLES=True
ENV AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=my_conn_string

定制镜像

警告

在 Airflow 2.8.0 中发布的 Dockerfile 中,镜像基于 Debian Bookworm 镜像作为基础镜像。

注意

您通常可以使用 Airflow 发布的最新 Dockerfile 来构建之前的 Airflow 版本。但是请注意,Dockerfile 和入口点脚本有一些细微的变化,这可能导致其行为略有不同,具体取决于您使用的 Dockerfile 版本。有关每个已发布 Docker 镜像版本中更改的详细信息,请参阅 变更日志

构建定制 docker 镜像的先决条件

  • 您需要启用 Buildkit 来构建镜像。这可以通过设置环境变量 DOCKER_BUILDKIT=1 或安装 buildx 插件 并运行 docker buildx build 命令来实现。Docker Desktop 默认启用了 Buildkit

  • 您需要安装新版本的 Docker 来处理 Dockerfile 的 1.4 语法。已知 Docker 版本 23.0.0 及以上版本可以正常工作。

在尝试定制镜像之前,您需要下载灵活且可定制的 Dockerfile。您可以从 发布的源代码 中提取官方发布的 Dockerfile 版本。您也可以方便地从 GitHub 下载最新发布的版本。您可以将其保存在任何目录中 - 无需存在任何其他文件。如果您希望使用自己的文件(例如自定义 pip 配置、您自己的 requirements 或自定义依赖项),您需要使用 DOCKER_CONTEXT_FILES 构建参数,并将文件放置在由该参数指向的目录中(详细信息请参阅 使用 docker context files)。

定制镜像是一种优化的方式,用于向镜像中添加您自己的依赖项——更适合准备高度优化(针对大小)的生产镜像,特别是当您有一些需要在安装前编译的依赖项时(例如 mpi4py)。

它也允许更复杂的用法,这是“高级用户”(Power-users)所需要的——例如使用 Airflow 的分支版本,或从经过安全审查的源构建镜像。

这种方法的一大优点是,即使您需要一些在最终镜像中不需要的编译时依赖项,它也能生成优化后的镜像。

其缺点是构建镜像需要更长时间,并且要求您使用 Apache Airflow 源代码中发布的 Dockerfile。

其缺点是,使用 --build-arg 构建 Docker 镜像的模式对于此类镜像的开发者来说不太熟悉。然而,这对于“高级用户”来说是相当知名的。这就是为什么定制流程更适合那些更熟悉并且有更多定制需求的用户。

镜像构建通常也比同等的“扩展”镜像耗时更长,因为它不是在现有基础镜像层之上扩展,而是重新构建早期阶段添加额外依赖所需的层。

定制镜像时,您可以选择多种 Airflow 安装方式

  • 从 PyPI 发布版本(默认)

  • 从自定义安装源——使用附加/替换原有的 apt 或 PyPI 仓库

  • 从本地源。这主要用于开发过程中。

  • 从 GitHub Airflow 仓库(或 fork)的标签、分支或特定提交。当您为保存在 fork 中且不想发布到 PyPI 的自定义 Airflow 版本构建镜像时,这特别有用。

  • 从本地存储的 Airflow、Airflow Providers 和其他依赖项的二进制包。如果您想在高度安全的构建环境中构建 Airflow,其中所有此类包必须经过您的安全团队审查并存储在您的私有制品库中,这特别有用。这也允许在隔离网络环境中构建 Airflow 镜像。

  • 旁注:在 隔离网络(air-gaped) 环境中构建 Airflow 听起来有点有趣,不是吗?

构建镜像时,您还可以添加一系列定制项

  • 用于 Airflow 的基础 Python 镜像

  • 要安装的 Airflow 版本

  • 要为 Airflow 安装的附加功能(extras)(甚至可以移除一些默认的附加功能)

  • 在构建 Airflow 时使用的额外 apt/python 依赖项(DEV 依赖项)

  • requirements.txt 文件添加到 docker-context-files 目录以添加额外需求

  • 为 Airflow 运行时版本安装的额外 apt/python 依赖项(RUNTIME 依赖项)

  • 在构建或准备 Airflow 运行时期间需要设置的额外命令和变量

  • 选择安装 Airflow 时使用的约束文件

最后一个点需要额外解释。Airflow 使用约束来确保其可预测地安装,即使 Airflow 依赖项(或我们依赖项的依赖项!)发布了新版本。Docker 镜像和随附脚本通常会根据安装的 Airflow 版本和 Python 版本自动确定要使用的约束文件的正确版本。例如,从 PyPI 安装的 2.0.2 版 Airflow 使用来自 constraints-2.0.2 标签的约束文件)。然而,在某些情况下——例如从 GitHub 安装 Airflow 时——您必须手动指定使用的约束版本,否则它将默认为最新版本的约束文件,这可能与您使用的 Airflow 版本不兼容。

您还可以下载任何版本的 Airflow 约束文件,并使用您自己的一组约束文件进行调整,手动设置您自己的依赖版本,然后使用您手动准备的约束版本。

您可以在从 PyPI 安装中阅读更多关于约束的内容。

请注意,如果您将 requirements.txt 放在 docker-context-files 文件夹中,它将用于安装其中声明的所有需求。建议该文件包含使用 == 版本说明符指定的依赖版本,以实现稳定的一组需求,这与是否有人发布了更新版本无关。但是,您必须确保更新这些需求并重新构建镜像,以包含最新的安全修复。

使用 docker-context-files

定制镜像时,您可以选择让 Airflow 安装自定义二进制文件或在 docker-context-files 中为您的 pip 提供自定义配置。要启用此功能,在构建镜像时需要添加 --build-arg DOCKER_CONTEXT_FILES=docker-context-files 构建参数。您可以传递 docker context 中的任何子目录,在构建期间它总是会被映射到 /docker-context-files

您可以将 docker-context-files 用于以下目的

  • 您可以将 requirements.txt 文件以及您想要安装的任何 pip 包放在 docker-context-file 文件夹中。这些需求将在构建期间自动安装。

注意

在下面的示例中,我们还添加了要安装的 apache-airflow 包 - 版本与您使用的镜像版本完全相同。这不是严格必需的,但始终安装与您正在使用的 Airflow 版本相同的 apache-airflow 版本是一个好习惯。这样可以确保您使用的版本与您正在扩展的版本相同。在某些情况下,如果您的新包具有冲突的依赖项,pip 可能会决定为您降级或升级 apache-airflow,因此明确添加它是一个好习惯 - 这样,如果您有冲突的需求,您将收到带有冲突信息的错误消息,而不是 airflow 的意外降级或升级。如果您升级 airflow 基础镜像,也应该更新版本以匹配新的 airflow 版本。

docker-stack-docs/docker-examples/customizing/own-requirements.sh

mkdir -p docker-context-files

cat <<EOF >./docker-context-files/requirements.txt
beautifulsoup4==4.10.0
apache-airflow==2.9.0.dev0
EOF

export DOCKER_BUILDKIT=1
docker build . \
    --build-arg DOCKER_CONTEXT_FILES=./docker-context-files \
    --tag "my-beautifulsoup4-airflow:0.0.1"
docker run -it my-beautifulsoup4-airflow:0.0.1 python -c 'import bs4; import sys; sys.exit(0)' && \
    echo "Success! Beautifulsoup4 installed" && echo
  • 您可以将 pip.conf 文件(以及遗留的 .piprc)放在 docker-context-files 文件夹中,它们将用于所有 pip 命令(例如,您可以配置您自己的源或认证机制)

docker-stack-docs/docker-examples/customizing/custom-pip.sh

mkdir -p docker-context-files

cat <<EOF >./docker-context-files/pip.conf
[global]
verbose = 2
EOF

export DOCKER_BUILDKIT=1
docker build . \
    --build-arg DOCKER_CONTEXT_FILES=./docker-context-files \
    --tag "my-custom-pip-verbose-airflow:0.0.1"
  • 您可以将下载的 .whl 包放在此处,并通过将 INSTALL_DISTRIBUTIONS_FROM_CONTEXT 设置为 true 来安装它们。如果您在受限的安全环境中构建镜像,这会很有用(详情请参阅:在受限安全环境中构建镜像)。

docker-stack-docs/docker-examples/restricted/restricted_environments.sh

mkdir -p docker-context-files
export AIRFLOW_VERSION="2.5.3"
rm docker-context-files/*.whl docker-context-files/*.tar.gz docker-context-files/*.txt || true

curl -Lo "docker-context-files/constraints-3.9.txt" \
    "https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-3.9.txt"

echo
echo "Make sure you use the right python version here (should be same as in constraints)!"
echo
python --version

pip download --dest docker-context-files \
    --constraint docker-context-files/constraints-3.9.txt  \
    "apache-airflow[async,celery,elasticsearch,kubernetes,postgres,redis,ssh,statsd,virtualenv]==${AIRFLOW_VERSION}"

注意

如果您想将 requirements.txt 文件放在主目录中而不创建专用文件夹,也可以传递 --build-arg DOCKER_CONTEXT_FILES=.。然而,将您复制到镜像 context 的任何文件保存在子文件夹中是一个好习惯。这样更容易区分在宿主机上使用的文件和在 Docker context 中传递的文件。当然,默认情况下,当您运行 docker build . 时,整个文件夹都作为“Docker build context”可用并发送到 docker 引擎,但 DOCKER_CONTEXT_FILES 总是复制到镜像的 build 段,因此复制所有本地文件夹可能会不必要地增加构建镜像所需的时间,并且每当本地文件夹中的任何文件发生变化时,您的缓存都会失效。

警告

破坏性更改!自 Airflow 2.3.0 起,您需要指定附加标志:--build-arg DOCKER_CONTEXT_Files=docker-context-files 才能使用 docker-context-files 中放置的文件。以前不需要这个开关。不幸的是,为了使 Dockerfile 成为不带任何额外文件的独立 Dockerfile,此更改是必需的。自 Airflow 2.3.0 起,随 Airflow 发布的 Dockerfile 不需要任何额外的文件夹或文件,并且可以从任何文件夹复制和使用。以前您需要将 Airflow 源代码与 Dockerfile 一起复制,因为需要一些脚本才能使其工作。使用 Airflow 2.3.0,我们正在使用 Buildkit 功能,使 Dockerfile 成为一个完全独立的、可以“按原样”使用的文件。

镜像定制示例

从 PyPI 包构建

这是从源构建定制镜像的基本方法。

以下示例构建了版本为 3.9 的生产镜像,使用最新的 PyPI 发布版 Airflow,以及默认的 Airflow 附加功能和依赖项集。最新的 PyPI 发布版 Airflow 约束文件会自动使用。

docker-stack-docs/docker-examples/customizing/stable-airflow.sh

export DOCKER_BUILDKIT=1

docker build . \
    --tag "my-stable-airflow:0.0.1"

以下示例构建了版本为 3.9 的生产镜像,使用来自 2.3.0 Airflow 包的默认附加功能。 2.3.0 约束文件会自动使用。

docker-stack-docs/docker-examples/customizing/pypi-selected-version.sh

export AIRFLOW_VERSION=2.3.4
export DOCKER_BUILDKIT=1

docker build . \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --tag "my-pypi-selected-version:0.0.1"

以下示例构建了版本为 3.9 的生产镜像,使用来自 2.3.0 PyPI 包的额外 Airflow 附加功能(mssql,hdfs),以及额外依赖项(oauth2client)。

docker-stack-docs/docker-examples/customizing/pypi-extras-and-deps.sh

export AIRFLOW_VERSION=2.3.4
export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="mssql,hdfs" \
    --build-arg ADDITIONAL_PYTHON_DEPS="oauth2client" \
    --tag "my-pypi-extras-and-deps:0.0.1"

以下示例添加了 mpi4py 包,它需要 build-essentialmpi compiler

docker-stack-docs/docker-examples/customizing/add-build-essential-custom.sh

export AIRFLOW_VERSION=2.8.2
export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg ADDITIONAL_PYTHON_DEPS="mpi4py==3.1.6" \
    --build-arg ADDITIONAL_DEV_APT_DEPS="libopenmpi-dev" \
    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="openmpi-common" \
    --tag "my-build-essential-image:0.0.1"

上述镜像与上一章中的“扩展”镜像等效,但其大小仅为 874 MB。与 1.1 GB 的“扩展镜像”相比,这减少了约 230 MB,因此通过使用“定制”而不是扩展,您可以实现约 20% 的镜像大小改进。如果您有更复杂的依赖项需要构建,节省的空间会更多。

构建优化后的镜像

以下示例构建了版本为 3.9 的生产镜像,使用来自 PyPI 包的额外 Airflow 附加功能,但其中包含额外的 apt 开发(dev)和运行时(runtime)依赖项。

开发依赖项是指那些需要 build-essential 的依赖项,并且通常需要重新编译一些 Python 依赖项,因此这些包在重新编译期间可能需要一些额外的 DEV 依赖项。这些包在运行时不需要,所以我们只在“构建”时安装它们。它们不会安装在最终镜像中,从而生成更小的镜像。在这种情况下,pandas 需要重新编译,因此它也需要 gcc 和 g++ 作为开发 APT 依赖项。jre-headless 不需要重新编译,因此可以作为运行时 APT 依赖项安装。

docker-stack-docs/docker-examples/customizing/pypi-dev-runtime-deps.sh

export AIRFLOW_VERSION=2.2.4
export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="jdbc" \
    --build-arg ADDITIONAL_PYTHON_DEPS="pandas" \
    --build-arg ADDITIONAL_DEV_APT_DEPS="gcc g++" \
    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="default-jre-headless" \
    --tag "my-pypi-dev-runtime:0.0.1"

使用 UV 作为包安装程序构建生产镜像

以下示例构建了默认设置下的生产镜像,但使用 uv 来构建镜像。这是一项实验性功能,因为 uv 是 Python 生态系统中一个非常快速但也非常新的工具。

docker-stack-docs/docker-examples/customizing/use-uv.sh

export DOCKER_BUILDKIT=1
docker build . \
    --build-arg AIRFLOW_USE_UV="true" \
    --tag "my-custom-use-uv-airflow:0.0.1"

构建带有 MySQL 客户端的镜像

警告

默认情况下,自 Airflow 2.8.0 起的 Airflow 镜像在“X86_64”和“ARM64”平台都默认使用“MariaDB”客户端。然而,您也可以构建带有 MySQL 客户端的镜像。以下示例构建了默认 Python 版本且带有“MySQL”客户端的生产镜像。

docker-stack-docs/docker-examples/customizing/mysql-client.sh

export DOCKER_BUILDKIT=1

docker build . \
    --build-arg INSTALL_MYSQL_CLIENT_TYPE="mysql" \
    --tag "my-mysql-airflow:0.0.1"

从 GitHub 构建

这种方法通常用于开发目的。但是,如果您有自己的 fork,您可以将其指向您的 fork 版本的源代码,而无需将其发布到 PyPI。只需在您的仓库中有一个分支或标签,并在指向安装目标的 URL 中使用该标签或分支即可。

在 GitHub 构建的情况下,如果您想使用特定的约束文件,需要手动传递约束文件的引用,否则会使用默认的 constraints-main

以下示例构建了版本为 3.9 的生产镜像,使用来自最新 main 版本 的默认附加功能,约束文件取自 GitHub 中 latest constraints-main 分支 的最新版本。

docker-stack-docs/docker-examples/customizing/github-main.sh

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow @ https://github.com/apache/airflow/archive/main.tar.gz" \
    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-main" \
    --tag "my-github-main:0.0.1"

以下示例构建了带有来自最新 v2-*-test 版本默认附加功能的生产镜像,约束文件取自 GitHub 中最新 constraints-2-* 分支 的最新版本(例如,v2-2-test 分支对应 constraints-2-2)。请注意,此命令可能偶尔会失败,因为只有在构建版本时使用“已发布版本”约束以及在构建 main 时使用“main”约束才能保证工作。

docker-stack-docs/docker-examples/customizing/github-v2-2-test.sh

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow @ https://github.com/apache/airflow/archive/v2-2-test.tar.gz" \
    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-2" \
    --tag "my-github-v2-2:0.0.1"

您还可以指定另一个仓库进行构建。如果您还想使用不同的约束文件仓库源,则必须将其指定为额外的 CONSTRAINTS_GITHUB_REPOSITORY 构建参数。

以下示例使用 Airflow 的 potiuk/airflow fork 构建生产镜像,约束文件也从该仓库下载。

docker-stack-docs/docker-examples/customizing/github-different-repository.sh

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow @ https://github.com/potiuk/airflow/archive/main.tar.gz" \
    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-main" \
    --build-arg CONSTRAINTS_GITHUB_REPOSITORY="potiuk/airflow" \
    --tag "github-different-repository-image:0.0.1"

使用自定义安装源

您可以定制镜像的更多方面——例如在安装 apt 依赖项之前执行的额外命令,或添加额外的源来安装您的依赖项。您可以在下面看到所有参数的描述,但这里有一个相当复杂的命令示例,用于根据此评论中的示例来定制镜像。

如果您需要使用自定义的 PyPI 包索引,也可以通过在构建镜像时添加 docker-context-files/pip.conf 文件来定制镜像构建期间使用的 PyPI 源。此 pip.conf 文件不会被提交到仓库(它已被添加到 .gitignore),并且不会出现在最终的生产镜像中。它仅在镜像的构建阶段添加和使用。因此,此 pip.conf 文件可以安全地包含您要使用的包索引列表、用于认证的用户名和密码。关于 pip.conf 文件的更多详情,请参阅 pip 配置

如果您以前使用过 .piprc 文件(一些较旧版本的 pip 用它进行定制),您可以将其放在 docker-context-files/.piprc 文件中,它将自动复制到 airflow 用户的 HOME 目录。

请注意,这些定制仅在 Airflow 镜像的 build 段中可用,并且不会出现在 final 镜像中。如果您希望扩展最终镜像并添加自定义的 .piprcpip.conf 文件,您应该在用于扩展 Airflow 镜像的自己的 Dockerfile 中添加它们。

这些定制与 Airflow 的安装方式无关。

注意

通过手动修改 Dockerfile(见下文)并注入所需的命令,也可以实现类似的结果,但通过构建参数指定定制,您可以避免与未来 Airflow Dockerfile 的更改同步的需要。这些定制在未来的 Airflow 官方 Dockerfile 版本中,最多只需要对参数名进行极少的修改(如果需要),因此使用构建命令进行定制可以使您的定制镜像更具未来兼容性。

以下这个——相当复杂——的示例展示了以下能力:

  • 添加 Airflow 附加功能 (slack, odbc)

  • 添加 PyPI 依赖项 (azure-storage-blob, oauth2client, beautifulsoup4, dateparser, rocketchat_API,typeform)

  • 在安装 apt 依赖项时添加自定义环境变量——包括 DEV 和 RUNTIME (ACCEPT_EULA=Y')

  • 添加自定义 curl 命令,用于添加密钥和配置安装 apt 依赖项(包括 DEV 和 RUNTIME)所需的额外 apt 源

  • 添加自定义 apt 依赖项,包括 DEV (msodbcsql17 unixodbc-dev g++) 和 runtime (msodbcsql17 unixodbc git procps vim)

docker-stack-docs/docker-examples/customizing/custom-sources.sh

export AIRFLOW_VERSION=2.2.4
export DOCKER_BUILDKIT=1

docker build . -f Dockerfile \
    --pull \
    --platform 'linux/amd64' \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="slack,odbc" \
    --build-arg ADDITIONAL_PYTHON_DEPS=" \
        azure-storage-blob<12.9.0 \
        oauth2client \
        beautifulsoup4 \
        dateparser \
        rocketchat_API \
        typeform" \
    --build-arg ADDITIONAL_DEV_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
    apt-key add --no-tty - && \
    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
    --build-arg ADDITIONAL_DEV_APT_ENV="ACCEPT_EULA=Y" \
    --build-arg ADDITIONAL_DEV_APT_DEPS="msodbcsql17 unixodbc-dev g++" \
    --build-arg ADDITIONAL_RUNTIME_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
    apt-key add --no-tty - && \
    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
    --build-arg ADDITIONAL_RUNTIME_APT_ENV="ACCEPT_EULA=Y" \
    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="msodbcsql17 unixodbc git procps vim" \
    --tag "my-custom-sources-image:0.0.1"

在受限安全环境中构建镜像

您还可以确保您的镜像仅使用本地约束文件和本地下载的 wheel 文件构建。这在二进制文件经过安全团队验证和审查的企业环境中通常很有用。这也是构建镜像最复杂的方式。如果您想走这条路,您应该成为构建和使用 Dockerfile 的专家,并且有特定的安全需求。

以下构建使用本地 docker-context-files 中的包和约束文件来构建生产镜像,而不是从 PyPI 或 GitHub 安装。它还禁用了 MySQL 客户端的安装,因为它使用了外部安装方法。

请注意,前提是您需要已下载 wheel 文件。在下面的示例中,我们首先在本地下载了这样的约束文件,然后使用 pip download 获取所需的 .whl 文件,但在最可能的情况下,这些 wheel 文件应该从内部的 .whl 文件仓库中复制。请注意,AIRFLOW_VERSION_SPECIFICATION 仅供参考,正确版本的 apache airflow .whl 文件是下载的 .whl 文件的一部分。

请注意,‘pip download’ 只在 Linux 宿主机上有效,因为有些包需要从源编译,您无法通过提供 --platform 开关来安装它们。下载时还需要使用与目标镜像相同的 Python 版本。

pip download 可以在单独的环境中进行。这些文件可以提交到一个单独的二进制仓库,由安全团队审查/验证,然后在需要在隔离网络系统上构建 Airflow 镜像时后续使用。

准备约束文件和 wheel 文件的示例。请注意,mysql 依赖项被移除,因为 mysqlclient 是从 Oracle 的 apt 仓库安装的;如果您想添加它,并且想在“隔离网络(air-gaped)”系统中构建 Airflow 镜像,则需要从您的仓库提供此库。

docker-stack-docs/docker-examples/restricted/restricted_environments.sh

mkdir -p docker-context-files
export AIRFLOW_VERSION="2.5.3"
rm docker-context-files/*.whl docker-context-files/*.tar.gz docker-context-files/*.txt || true

curl -Lo "docker-context-files/constraints-3.9.txt" \
    "https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-3.9.txt"

echo
echo "Make sure you use the right python version here (should be same as in constraints)!"
echo
python --version

pip download --dest docker-context-files \
    --constraint docker-context-files/constraints-3.9.txt  \
    "apache-airflow[async,celery,elasticsearch,kubernetes,postgres,redis,ssh,statsd,virtualenv]==${AIRFLOW_VERSION}"

完成此步骤后,您的 docker-context-files 文件夹将包含安装 Airflow 所需的所有包。

这些下载的包和约束文件在您尝试安装镜像之前可以由您的安全团队预先审查。您还可以将这些下载的二进制包存储在您的私有制品库中,这允许这样一种流程:您在一台机器上下载包,仅提交新包进行安全审查,并且仅在使用新包时才使用经过审查的包。

在一个单独的(隔离网络)系统上,所有 PyPI 包都可以复制到 docker-context-files 中,您可以通过传递这些构建参数来使用下载的包构建镜像

  • INSTALL_DISTRIBUTIONS_FROM_CONTEXT="true" - 使用 docker-context-files 中存在的包

  • AIRFLOW_CONSTRAINTS_LOCATION=/docker-context-files/YOUR_CONSTRAINT_FILE.txt - 指向下载的约束文件

  • (可选)INSTALL_MYSQL_CLIENT="false" 如果您不想从 Oracle 仓库安装 MySQL 客户端。

  • (可选)INSTALL_MSSQL_CLIENT="false" 如果您不想从 Microsoft 仓库安装 MsSQL 客户端。

  • (可选)INSTALL_POSTGRES_CLIENT="false" 如果您不想从 Postgres 仓库安装 Postgres 客户端。

请注意,我们提供的从本地包安装 Python 包的解决方案,只解决了“隔离网络”下的 Python 安装问题。Docker 镜像还会下载 apt 依赖项和 node-modules。这些类型的依赖项更有可能通过透明代理在您的“隔离网络”系统中使用,并且它应该自动连接到您的私有制品库。然而,未来此解决方案可能会应用于这两个安装步骤。

您还可以使用上一章中描述的技术,使 docker build 使用您的私有 apt 源或私有 PyPI 仓库(通过 .pypirc),这些源或仓库可以经过安全审查并可用。

如果您满足所有条件,您可以通过运行类似于下面的命令,在隔离网络系统上构建镜像

docker-stack-docs/docker-examples/restricted/restricted_environments.sh

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.9-slim-bookworm" \
    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg INSTALL_MYSQL_CLIENT="false" \
    --build-arg INSTALL_MSSQL_CLIENT="false" \
    --build-arg INSTALL_POSTGRES_CLIENT="true" \
    --build-arg DOCKER_CONTEXT_FILES="docker-context-files" \
    --build-arg INSTALL_DISTRIBUTIONS_FROM_CONTEXT="true" \
    --build-arg AIRFLOW_CONSTRAINTS_LOCATION="/docker-context-files/constraints-3.9.txt" \
    --tag airflow-my-restricted-environment:0.0.1

修改 Dockerfile

构建参数方法是一种方便的方法,如果您不想手动修改 Dockerfile。我们的方法足够灵活,可以开箱即用地满足大多数需求和定制。使用此方法时,您无需担心每次发布新版本的 Airflow 时都需要调整镜像。然而,如果您有非常具体的需求并想构建一个非常定制的镜像,有时这种方法还不够。在这种情况下,您可以根据需要简单地手动修改 Dockerfile,并将其存储在您的 fork 仓库中。但是,每当发布新版本的 Airflow 时,您都必须确保 rebase 您的更改,因为我们未来可能会修改 Dockerfile 构建方法,并且您可能需要解决冲突并 rebase 您的更改。

修改 Dockerfile 时需要记住以下几点:

  • 我们使用了 .dockerignore 推荐的广泛模式,其中默认忽略所有内容,仅通过排除(!)添加所需的文件夹。这有助于保持 docker context 的小巧,因为 Airflow 源代码中生成了许多二进制制品,如果它们被添加到 context 中,构建镜像的时间将显著增加。如果您想将任何新文件夹添加到镜像中,必须在此处添加它们,并在前面加上 !

    # Ignore everything
    **
    
    # Allow only these directories
    !airflow
    ...
    
  • docker-context-files 文件夹会自动添加到镜像的 context 中,因此如果您想添加单个文件、二进制文件、需求文件等,您可以在那里添加。 docker-context-files 被复制到镜像构建段的 /docker-context-files 文件夹中,因此它不会出现在最终镜像中——这使得最终镜像更小,以防您只想在 build 段中使用这些文件。如果您想将文件获取到最终镜像中(在主镜像段中),必须使用 COPY 命令手动从该目录复制任何文件。

更多详情

构建参数参考

详细的 --build-arg 参考可以在镜像构建参数参考中找到。

镜像的架构

您可以在 镜像文档 中阅读更多关于镜像的详细信息——包括上下文、参数和内部结构。

依赖缓存

为了在本地构建镜像时实现更快的迭代(特别是当您测试不同的 Python 包组合时),我们启用了 pip 缓存。缓存 ID 基于四个不同的参数

  1. TARGETARCH:避免共享架构特定的缓存包

  2. DEPENDENCY_CACHE_EPOCH:通过将 DEPENDENCY_CACHE_EPOCH 作为 --build-arg 传递来启用缓存失效

本条目是否有帮助?