构建镜像

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

镜像扩展的快速入门场景

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

以下是这些场景的示例 Dockerfile,您可以进一步阅读以了解可能涉及扩展或自定义镜像的更复杂情况。您将在下面找到有关更复杂场景的更多信息,但如果您的目标是使用新的提供程序、软件包等快速扩展 Airflow 镜像,那么这里有一个快速入门。

添加新的 apt 软件包

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

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

FROM apache/airflow:2.9.2
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 软件包 - 版本与您使用的镜像版本完全相同。这不是严格要求的,但始终安装与您正在使用的版本相同的 apache-airflow 版本是一个好习惯。这样,您可以确保您正在使用的版本与您正在扩展的版本相同。在某些情况下,您的新软件包具有冲突的依赖项,pip 可能会决定为您降级或升级 apache-airflow,因此明确添加它是一个好习惯 - 这样,如果您有冲突的要求,您将收到包含冲突信息的错误消息,而不是 airflow 意外降级或升级。如果您升级 airflow 基础镜像,您还应该更新版本以匹配新版本的 airflow。

注意

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

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

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

从 requirements.txt 添加软件包

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

注意

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

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

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

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

lxml
beautifulsoup4

嵌入 DAG

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

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

FROM apache/airflow:2.9.2

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

docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py[源代码]

import datetime

import pendulum

from airflow.models.dag import DAG
from airflow.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}(注意双下划线)。

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

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

扩展与自定义镜像

您可能想很快知道您是需要扩展还是自定义 Apache Airflow 的现有镜像。本章将简要回答这些问题。

以下是两种方法的比较

扩展

自定义

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

只需要有关镜像的基本知识

构建速度快

生成的镜像在大小方面进行了高度优化

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

可以在安全隔离的系统上构建

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

注意

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

Airflow Summit 2020 的 生产 Docker 镜像 演讲提供了有关生产镜像的上下文、架构和自定义/扩展方法的更多详细信息。

为什么要自定义镜像?

Apache Airflow 社区发布了 Apache Airflow 的 参考镜像。但是,Airflow 有 60 多个社区管理的提供程序(可以通过 extras 安装),并且并非每个人都使用安装的一些默认 extras/providers,有时需要其他 extras/providers,有时(实际上经常)您需要添加您自己的自定义依赖项、软件包甚至自定义提供程序。

在 Kubernetes 和 Docker 中,这意味着您需要另一个满足您特定需求的镜像。这就是您应该学习如何构建自己的 Docker(更准确地说是容器)镜像的原因。您可能想使用 reference image 并在启动容器时动态安装新的软件包,但这是一个坏主意,原因有很多 - 从构建的脆弱性到安装这些软件包所需的额外时间 - 每次容器启动时都必须这样做。在生产环境中处理新依赖项和需求的唯一可行方法是构建和使用您自己的镜像。只有在“爱好者”和“快速入门”场景中,当您想快速迭代以尝试一些东西并稍后用您自己的镜像替换它时,才应该动态安装依赖项。

构建镜像入门

注意

在功能和向后兼容性方面,Dockerfile 并不严格遵循 Apache Airflow 的 语义化版本控制 方法。虽然 Airflow 代码严格遵循它,但 Dockerfile 实际上是一种使用标准容器方法方便地打包 Airflow 的方式,偶尔在构建过程中或镜像的入口点会有一些变化,需要稍微调整。有关更改和所需调整的详细信息,请参阅 变更日志

您将遇到几种最典型的场景,以下是如何快速实现目标的快速方法。为了理解细节,您可以进一步阅读,但对于使用典型工具的简单情况,这里有一些简单的例子。

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

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

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

  • 应该在您的镜像中执行的额外步骤(通常以 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 引擎缓存中,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 镜像,包含最常见的额外组件和提供程序,以及 AMD64 平台支持的所有后端数据库客户端和 ARM64 平台的 Postgres。

  2. 精简 Airflow 镜像,这是一个最小化的镜像,包含为 AMD64 平台安装的所有受支持的后端数据库客户端和 ARM64 平台的 Postgres,但不包含任何额外组件或提供程序,除了 4 个默认提供程序。

注意

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

注意

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

与常规镜像相比,精简镜像很小(约 500 MB 对比约 1.1 GB),您可能需要添加更多软件包和提供程序才能使其适用于您的情况(但如果您只使用一小部分提供程序,它可能是一个很好的起点)。

精简镜像中的依赖项版本可能与预装提供程序时使用的版本不同,这仅仅是因为核心 Airflow 对其自身版本的限制可能更少。当您安装某些提供程序时,如果提供程序对相同的依赖项有不同的限制,则可能需要降级某些依赖项。

镜像的命名约定

镜像

Python

标准镜像

精简镜像

最新默认

3.8

apache/airflow:latest

apache/airflow:slim-latest

默认

3.8

apache/airflow:X.Y.Z

apache/airflow:slim-X.Y.Z

最新

3.8,3.9,3.10,3.11

apache/airflow:latest-pythonN.M

apache/airflow:slim-latest-pythonN.M

特定

3.8,3.9,3.10,3.11

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

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

  • 如果您的 apt 或 PyPI 依赖项需要一些 build-essential 或其他需要编译 Python 依赖项的软件包,那么最好的选择是遵循“自定义镜像”路线,因为您可以通过这种方式构建高度优化(针对大小)的镜像。但是,它要求您使用作为 Apache Airflow 源代码一部分发布的 Dockerfile(也可在 Dockerfile 中找到)。

  • 您还可以通过使用 Airflow 的 COPY 指令简单地添加 dag 来将它们嵌入到镜像中。生产镜像中的 DAG 位于 /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 Provider 包的示例

Airflow Providers 的发布独立于核心 Airflow,有时您可能只想升级特定的提供程序来修复一些问题或使用该提供程序版本中提供的功能。以下是如何做到这一点的示例

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

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

添加 Airflow Provider 包和 apt 包的示例

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

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

FROM apache/airflow:2.9.2
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==2.1.3

添加 apt 包的示例

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

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

FROM apache/airflow:2.9.2
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 包添加到镜像中。

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

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

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

以下示例将 PyPI 中的 lxml python 包添加到镜像中,并带有用于安装 airflow 的约束。这允许您使用您知道已针对给定版本的 Airflow 进行过测试的软件包版本。如果您不想使用在您使用的 Airflow 版本之后发布的潜在更新版本,也可以使用它。

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

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

使用 uv 添加 PyPI 包的示例

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

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

FROM apache/airflow:2.9.2

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

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

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

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

lxml
beautifulsoup4

需要可写目录时的示例

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

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

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

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

以下示例添加了需要 build-essentialmpi compilermpi4py 包。

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

FROM apache/airflow:2.9.2
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

构建后,此镜像的大小约为 1.1 GB。正如您将在后面看到的那样,如果您使用“自定义”而不是“扩展”镜像,则可以将镜像的大小减少 20%。

想要嵌入 DAG 时的示例

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

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

FROM apache/airflow:2.9.2

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

docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py[源代码]

import datetime

import pendulum

from airflow.models.dag import DAG
from airflow.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 镜像中。

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

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

自定义镜像

警告

在 Airflow 2.8.0 中发布的 Dockerfile 中,镜像基于 Debian Bookworm 镜像作为基础镜像。对于作为 2.8.* 系列一部分发布的 Dockerfile,您仍然可以选择(现在已弃用)Debian Bullseye 镜像作为基础镜像,但此选项将在 2.9.0 中删除。

注意

您通常可以使用 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 上下文文件)。

自定义镜像是将您自己的依赖项添加到镜像中的优化方式 - 更适合于准备高度优化(针对大小)的生产镜像,尤其是当您拥有需要在安装之前编译的依赖项时(例如 mpi4py)。

它还允许“高级用户”进行更复杂的使用 - 例如使用 Airflow 的分支版本,或从经过安全审查的源代码构建镜像。

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

缺点是构建镜像需要更长的时间,并且需要您使用作为 Apache Airflow 源代码一部分发布的 Dockerfile。

缺点是使用 --build-arg 构建 Docker 镜像的模式对于此类镜像的开发人员来说不太熟悉。但是,对于“高级用户”来说,这是众所周知的。这就是为什么自定义流程更适合那些更熟悉并有更多自定义需求的用户。

镜像的构建时间通常也比等效的“扩展”镜像长得多,因为与其扩展已经来自基础镜像的层,不如重建在镜像构建的早期阶段添加额外依赖项所需的层。

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

  • 从 PyPI 版本(默认)

  • 从自定义安装源 - 使用其他/替换原始 apt 或 PyPI 存储库

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

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

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

  • 旁注。在 air-gaped 环境中构建 Airflow 听起来很有趣,不是吗?

您还可以在构建镜像时添加一系列自定义项

  • 您用于 Airflow 的基础 Python 镜像

  • 要安装的 Airflow 版本

  • 要为 Airflow 安装的额外组件(甚至可以删除一些默认的额外组件)

  • 在构建 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 上下文的任何子目录,它将在构建期间始终映射到 /docker-context-files

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

  • 您可以放置 requirements.txt 并将您要安装的任何 pip 包添加到 docker-context-file 文件夹中。这些依赖项将在构建期间自动安装。

注意

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

docs/docker-stack/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 命令(例如,您可以配置您自己的源或身份验证机制)

docs/docker-stack/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_PACKAGES_FROM_CONTEXT 设置为 true 的情况下安装它们。如果您在受限的安全环境中构建镜像,这将非常有用(有关详细信息,请参阅:在安全受限环境中构建镜像

docs/docker-stack/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.8.txt" \
    "https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-3.8.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.8.txt  \
    "apache-airflow[async,celery,elasticsearch,kubernetes,postgres,redis,ssh,statsd,virtualenv]==${AIRFLOW_VERSION}"

注意

如果您想将 requirements.txt 放在主目录中而不创建专用文件夹,您还可以传递 --build-arg DOCKER_CONTEXT_FILES=.。但是,将您复制到镜像上下文中的任何文件保存在子文件夹中是一种很好的做法。这使得将主机上使用的内容与 Docker 上下文中传递的内容分开变得更容易。当然,默认情况下,当您运行 docker build . 时,整个文件夹都可用作“Docker 构建上下文”并发送到 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 包构建

这是从源代码构建自定义镜像的基本方法。

以下示例使用最新 PyPI 发布的 Airflow 和默认的 Airflow 额外组件和依赖项集构建版本 3.8 的生产镜像。将自动使用最新 PyPI 发布的 Airflow 约束。

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

export DOCKER_BUILDKIT=1

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

以下示例使用来自 2.3.0 Airflow 包的默认额外组件构建版本 3.8 的生产镜像。将自动使用 2.3.0 约束。

docs/docker-stack/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.8-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --tag "my-pypi-selected-version:0.0.1"

以下示例使用来自 2.3.0 PyPI 包的额外 airflow 额外组件(mssql,hdfs)和额外依赖项(oauth2client)构建版本 3.8 的生产镜像。

docs/docker-stack/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.8-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"

以下示例添加了需要 build-essentialmpi compilermpi4py 包。

docs/docker-stack/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.8-slim-bookworm" \
    --build-arg AIRFLOW_VERSION="${AIRFLOW_VERSION}" \
    --build-arg ADDITIONAL_PYTHON_DEPS="mpi4py==3.1.5" \
    --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% 的改进。如果您有更复杂的依赖项要构建,则节省的空间可能会增加。

构建优化镜像

以下示例使用来自 PyPI 包的额外 airflow 额外组件构建版本 3.8 的生产镜像,但它包含额外的 apt 开发和运行时依赖项。

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

docs/docker-stack/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.8-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"

构建基于 Debian Bullseye 的镜像

警告

默认情况下,从 Airflow 2.8.0 开始,Airflow 镜像基于 Debian Bookworm。但是,您也可以构建基于 - 已弃用 - 的 Debian Bullseye 的镜像。此选项将在 Airflow 2.9.0 中发布的 Dockerfile 中删除

以下示例构建基于 Debian Bullseye 基础镜像的版本 3.8 的生产镜像。

docs/docker-stack/docker-examples/customizing/debian-bullseye.sh

export DOCKER_BUILDKIT=1

docker build . \
    --build-arg PYTHON_BASE_IMAGE="python:3.8-slim-bullseye" \
    --tag "my-bullseye-airflow:0.0.1"

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

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

docs/docker-stack/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 客户端构建镜像。以下示例使用“MySQL”客户端以默认 Python 版本构建生产镜像。

docs/docker-stack/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 构建

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

如果是 GitHub 构建,如果您想使用特定的约束,则需要手动传递约束引用,否则将使用默认的 constraints-main

以下示例使用最新主版本中的默认附加组件构建版本为 3.8 的生产镜像,并且约束取自 GitHub 中 constraints-main 分支的最新版本。

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

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.8-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)。请注意,此命令可能会偶尔失败,因为只有在构建版本时“已发布版本”约束和在构建主版本时“主”约束才能保证正常工作。

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

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.8-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 分支构建生产镜像,并且约束也从该存储库下载。

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

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.8-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(见下文)并注入所需的命令可以实现类似的结果,但是通过 build-args 指定自定义,您可以避免同步来自未来 Airflow Dockerfile 的更改。这些自定义应该适用于未来版本的 Airflow 官方 Dockerfile,最多只需对参数名称进行少量修改(如果有),因此使用构建命令进行自定义可以使您的自定义镜像更加面向未来。

以下相当复杂的示例展示了

  • 添加 airflow 附加组件(slack、odbc)

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

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

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

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

docs/docker-stack/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.8-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 文件,但在最有可能的情况下,应从此类 .whl 文件的内部存储库复制这些 wheel 文件。请注意,AIRFLOW_VERSION_SPECIFICATION 仅供参考,正确版本的 apache airflow .whl 文件是下载的 .whl 文件的一部分。

请注意,“pip download”仅适用于 Linux 主机,因为某些软件包需要从源代码编译,并且您无法在提供 --platform 开关的情况下安装它们。还需要使用与目标镜像相同的 python 版本下载它们。

pip download 可能会在单独的环境中发生。这些文件可以提交到单独的二进制存储库,并由安全团队进行审查/验证,并在需要时用于在隔离系统上构建 Airflow 镜像。

准备约束文件和 wheel 文件的示例。请注意,mysql 依赖项已删除,因为 mysqlclient 是从 Oracle 的 apt 存储库安装的,如果您想添加它,则需要从您的存储库提供此库,如果您想在“隔离”系统中构建 Airflow 镜像。

docs/docker-stack/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.8.txt" \
    "https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-3.8.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.8.txt  \
    "apache-airflow[async,celery,elasticsearch,kubernetes,postgres,redis,ssh,statsd,virtualenv]==${AIRFLOW_VERSION}"

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

在您尝试安装镜像之前,您的安全团队可以预先审查这些下载的软件包和约束文件。您还可以将这些下载的二进制软件包存储在您的私有工件注册表中,这允许您在一台机器上下载软件包,仅提交新软件包以进行安全审查,并且仅在新软件包经过审查后才使用它们。

在单独的(隔离)系统上,可以将所有 PyPI 软件包复制到 docker-context-files 中,您可以在其中使用通过传递以下构建参数下载的软件包来构建镜像

  • INSTALL_PACKAGES_FROM_CONTEXT="true" - 使用 docker-context-files 中存在的软件包

  • AIRFLOW_PRE_CACHED_PIP_PACKAGES="false" - 在构建镜像时不预先缓存来自 PyPI 的软件包

  • 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 包的解决方案仅解决了“air-gaped”Python 安装的问题。Docker 镜像还会下载 apt 依赖项和 node-modules。这些类型的依赖项更有可能通过透明代理在您的“air-gaped”系统中可用,并且应该会自动连接到您的私有注册表。但是,将来该解决方案可能会应用于这两个安装步骤。

您还可以使用上一章中介绍的技术,使 docker build 使用您的私有 apt 源或可用的私有 PyPI 存储库(通过 .pypirc),这些源和存储库可以进行安全审查。

如果满足所有条件,您可以通过运行类似于以下命令的命令在 air-gaped 系统上构建镜像

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

export DOCKER_BUILDKIT=1

docker build . \
    --pull \
    --build-arg PYTHON_BASE_IMAGE="python:3.8-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 AIRFLOW_PRE_CACHED_PIP_PACKAGES="false" \
    --build-arg DOCKER_CONTEXT_FILES="docker-context-files" \
    --build-arg INSTALL_PACKAGES_FROM_CONTEXT="true" \
    --build-arg AIRFLOW_CONSTRAINTS_LOCATION="/docker-context-files/constraints-3.8.txt" \
    --tag airflow-my-restricted-environment:0.0.1

修改 Dockerfile

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

修改 Dockerfile 时,需要注意以下几点

  • 我们正在使用广泛推荐的 .dockerignore 模式,其中默认情况下所有内容都被忽略,并且仅通过排除添加所需的文件夹(!)。这允许保持 docker 上下文较小,因为在 Airflow 的源代码中生成了许多二进制工件,如果将它们添加到上下文中,构建镜像的时间将显着增加。如果要添加任何新文件夹以在镜像中可用,则必须在此处添加它们,并在前面加上 !

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

更多详细信息

构建参数参考

有关 --build-arg 的详细参考,请参阅镜像构建参数参考

镜像的架构

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

Pip 包缓存

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

  1. PYTHON_BASE_IMAGE:避免根据 Python 版本和目标操作系统共享相同的缓存

  2. AIRFLOW_PIP_VERSION

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

  4. PIP_CACHE_EPOCH:通过传递 PIP_CACHE_EPOCH 作为 --build-arg 来启用更改缓存 ID

此条目有帮助吗?