Airflow Summit 2025 将于 10 月 07-09 日举行。立即注册获取早鸟票!

入口点

如果您使用生产镜像的默认入口点,容器启动时会自动执行一些操作。在某些情况下,您可以通过向镜像传递环境变量来触发这些行为。

控制“执行”行为的变量以 _AIRFLOW 开头,以区分用于构建镜像的以 AIRFLOW 开头的变量。

允许任意用户运行容器

Airflow 镜像与 OpenShift 兼容,这意味着您可以使用随机用户 ID 和组 ID 0 (root) 来启动它。如果您想使用 Airflow 以外的用户运行镜像,则必须将用户的 GID 设置为 0。如果您尝试使用不同的组,入口点将退出并报错。

OpenShift 在启动容器时随机分配 UID,但您也可以在手动运行镜像时利用这种灵活的 UID。这可能很有用,例如当您想在 Linux 上从主机系统挂载 daglogs 文件夹时,在这种情况下,UID 应设置为与主机用户相同的 ID。

这可以通过多种方式实现 - 您可以在扩展或定制镜像时更改 USER,或者通过在 docker run 命令中添加 --user 标志来动态地将用户传递给该命令,格式如下(详情请参阅 Docker Run 参考

[ user | user:group | uid | uid:gid | user:gid | uid:group ]

在使用 Docker Compose 环境时,可以通过 docker-compose.yaml 文件中的 user: 条目进行更改。详情请参阅 Docker compose 参考。在我们使用 Docker-Compose 的快速入门指南中,UID 可以通过 AIRFLOW_UID 变量传递,如 初始化 docker compose 环境 中所述。

用户可以是任何 UID。如果 UID 与默认的 airflow (UID=50000) 不同,则在进入容器时将自动创建用户。

为了适应许多外部库和项目,Airflow 将在 (/etc/passwd) 中自动创建这样一个任意用户,并将其主目录指向 /home/airflow。许多第三方库和包要求用户的 home 目录存在,因为它们需要在那里写入一些缓存信息,因此动态创建用户是必要的。

这样的任意用户必须能够写入需要写入权限的特定目录,并且由于安全原因,不建议允许“其他”用户具有写入权限,因此 OpenShift 指南引入了一个概念,即将所有此类文件夹的组 ID (GID) 设置为 0 (root)。Airflow 生产镜像中所有需要写入权限的目录的 GID 都设置为 0(并且组可写)。我们遵循这个概念,所有需要写入权限的目录都遵循这个原则。

GID=0 被设置为 airflow 用户的默认值,因此它创建的任何目录默认都将 GID 设置为 0。入口点将 umask 设置为 0002 - 这意味着用户创建的任何目录对组 0 也具有“组写入”权限 - 它们将可由具有 root 组的其他用户写入。此外,无论何时任何“任意”用户创建一个文件夹(例如在挂载的卷中),该文件夹都将具有“组写入”权限和 GID=0,这样即使该目录后来被另一个任意用户挂载,使用另一个任意用户执行操作仍将继续有效。

然而,umask 设置仅在容器运行时有效 - 在构建镜像期间不使用。如果您想扩展镜像并添加自己的包,您应该记住在 docker 命令前面添加 umask 0002 - 这样任何需要组访问的安装创建的目录也将可由组写入。例如,可以通过以下方式实现

RUN umask 0002; \
    do_something; \
    do_otherthing;

您可以在 Openshift 最佳实践 的“支持任意用户 ID”章节中阅读更多相关信息。

等待 Airflow DB 连接

入口点正在等待数据库连接,与数据库引擎无关。这有助于提高环境的稳定性。

等待连接涉及执行 airflow db check 命令,这意味着将执行一条 select 1 as is_alive; 语句。然后它会循环直到命令成功。它会尝试 CONNECTION_CHECK_MAX_COUNT 次,并在每次检查之间休眠 CONNECTION_CHECK_SLEEP_TIME。要禁用检查,请设置 CONNECTION_CHECK_MAX_COUNT=0

等待 Celery 代理连接

如果使用 CeleryExecutor,并且使用了 schedulercelery 命令之一,则入口点将等待直到 Celery 代理 DB 连接可用。

该脚本根据 URL 方案检测后端类型,并在 URL 中未指定端口号时分配默认端口号。然后它会循环直到可以建立到指定主机/端口的连接。它会尝试 CONNECTION_CHECK_MAX_COUNT 次,并在每次检查之间休眠 CONNECTION_CHECK_SLEEP_TIME。要禁用检查,请设置 CONNECTION_CHECK_MAX_COUNT=0

支持的方案

  • amqp(s):// (rabbitmq) - 默认端口 5672

  • redis:// - 默认端口 6379

  • postgres:// - 默认端口 5432

  • mysql:// - 默认端口 3306

等待连接涉及检查匹配的端口是否打开。主机信息派生自 Airflow 配置。

执行命令

如果第一个参数等于“bash” - 您将进入 bash shell,或者如果您指定额外参数,则可以执行 bash 命令。例如

docker run -it apache/airflow:3.0.0-python3.9 bash -c "ls -la"
total 16
drwxr-xr-x 4 airflow root 4096 Jun  5 18:12 .
drwxr-xr-x 1 root    root 4096 Jun  5 18:12 ..
drwxr-xr-x 2 airflow root 4096 Jun  5 18:12 dags
drwxr-xr-x 2 airflow root 4096 Jun  5 18:12 logs

如果第一个参数等于 python - 您将进入 python shell,或者如果您传递额外参数,则执行 python 命令。例如

> docker run -it apache/airflow:3.0.0-python3.9 python -c "print('test')"
test

如果第一个参数等于 airflow - 其余参数将被视为要执行的 Airflow 命令。示例

docker run -it apache/airflow:3.0.0-python3.9 airflow webserver

如果还有其他参数 - 它们将直接传递给“airflow”命令

> docker run -it apache/airflow:3.0.0-python3.9 help
  usage: airflow [-h] GROUP_OR_COMMAND ...

  positional arguments:
    GROUP_OR_COMMAND

      Groups:
        celery         Celery components
        config         View configuration
        connections    Manage connections
        dags           Manage dags
        db             Database operations
        jobs           Manage jobs
        kubernetes     Tools to help run the KubernetesExecutor
        pools          Manage pools
        providers      Display providers
        roles          Manage roles
        tasks          Manage tasks
        users          Manage users
        variables      Manage variables

      Commands:
        cheat-sheet    Display cheat sheet
        info           Show information about current Airflow and environment
        kerberos       Start a Kerberos ticket renewer
        plugins        Dump information about loaded plugins
        rotate-fernet-key
                       Rotate encrypted connection credentials and variables
        scheduler      Start a scheduler instance
        sync-perm      Update permissions for existing roles and optionally dags
        version        Show the version
        webserver      Start an Airflow webserver instance

  optional arguments:
    -h, --help         show this help message and exit

在 Airflow 入口点之前执行自定义代码

如果您想在 Airflow 的入口点之前执行一些自定义代码,可以使用自定义脚本,并在自定义脚本中将 Airflow 的入口点作为最后一个 exec 指令调用。但是,您必须记住以与 Airflow 入口点相同的方式使用 dumb-init,否则可能会在信号传播方面遇到问题(请参阅下一章)。

FROM airflow:2.9.0.dev0
COPY my_entrypoint.sh /
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/my_entrypoint.sh"]

您的入口点可以例如动态修改或添加变量。例如,下面的入口点根据作为镜像执行参数传递的第一个参数来设置 DB 检查的最大次数(一个有点没用的例子,但应该能给读者一个如何使用的示例)。

#!/bin/bash
export CONNECTION_CHECK_MAX_COUNT=${1}
shift
exec /entrypoint "${@}"

确保在自定义入口点中将 exec /entrypoint "${@}" 作为最后一个命令运行 Airflow 的入口点。这样信号将得到正确传播,并且参数将照常传递给入口点(如果需要传递一些额外参数,可以使用上面的 shift)。请注意,从安全角度来看,以这种方式传递秘密值或将秘密存储在镜像中是一个糟糕的主意 - 因为镜像和运行镜像的参数都可以被有权访问 Kubernetes 或镜像注册表日志的任何人访问。

另请注意,在 Airflow 入口点执行之前执行的代码不应在容器内创建任何文件或目录,并且执行时所有功能可能无法正常工作。在 Airflow 入口点执行之前,以下功能不可用:

  • umask 未正确设置,不允许 group 写访问

  • 如果使用任意用户运行镜像,则用户尚未在 /etc/passwd 中创建

  • 数据库和代理可能尚未可用

添加自定义镜像行为

Airflow 镜像在入口点执行许多步骤,并设置正确的环境,但您可能希望在入口点创建用户、设置 umask、设置变量并检查数据库正在运行之后运行额外的代码。

与其运行常规命令 - schedulerwebserver,不如运行可以嵌入到镜像中的自定义脚本。完成自定义设置后,您甚至可以在自定义脚本中执行 Airflow 的常用组件 - schedulerwebserver。与自定义入口点类似,可以通过扩展镜像来添加它。

FROM airflow:2.9.0.dev0
COPY my_after_entrypoint_script.sh /

构建您的镜像,然后您可以通过运行以下命令来执行此脚本

docker build . --pull --tag my-image:0.0.1
docker run -it my-image:0.0.1 bash -c "/my_after_entrypoint_script.sh"

信号传播

Airflow 在入口点中使用 dumb-init 作为“init”运行。这样做是为了正确传播信号并回收子进程。这意味着您运行的进程不必安装信号处理程序即可正常工作并在容器正常终止时被杀死。信号传播的行为由 DUMB_INIT_SETSID 变量配置,该变量默认为 1 - 这意味着信号将传播到整个进程组,但您可以将其设置为 0 以启用 dumb-initsingle-child 行为,该行为仅将信号传播到单个子进程。

下表总结了 DUMB_INIT_SETSID 的可能值及其用例。

变量值

用例

1 (默认)

将信号传播到容器中运行的主进程的进程组中的所有进程。

如果您通过 ["bash", "-c"] 命令运行您的进程,并且 bash 在没有 exec 的情况下创建新进程,这将有助于正常终止容器,因为所有进程都将接收到信号。

0

仅将信号传播到主进程。

如果您的主进程正常处理信号,这将非常有用。一个很好的例子是 Celery worker 的温和关闭。dumb-init 在这种情况下只会将信号传播到主进程,而不会传播到与主进程在同一进程组中创建的进程。例如,在 Celery 的情况下,主进程会将 worker 置于“离线”模式,并等待所有正在运行的任务完成,然后才会终止所有进程。

对于 Airflow 的 Celery worker,您应该将变量设置为 0,并使用 ["celery", "worker"] 命令。如果您通过 ["bash", "-c"] 命令运行它,则需要将 exec airflow celery worker 作为最后一个执行的命令来启动 worker。

额外的快速测试选项

以下选项主要用于快速测试镜像 - 例如使用 quick-start docker-compose,或在添加新包时进行本地测试。它们不应在生产环境中运行,因为它们会增加执行额外命令的开销。在生产环境中,这些选项应作为数据库上的维护操作实现,或嵌入到使用的自定义镜像中(当您想添加新包时)。

升级 Airflow DB

如果您将 _AIRFLOW_DB_MIGRATE 变量设置为非空值,入口点将在验证连接后立即运行 airflow db migrate 命令。当您使用内部 SQLite 数据库(默认)运行 Airflow 时,也可以使用此选项在入口点升级数据库并创建管理员用户,以便您可以立即启动 webserver。注意 - 使用 SQLite 仅用于测试目的,切勿在生产环境中使用 SQLite,因为它在并发方面存在严重限制。

创建管理员用户

入口点也可以在您进入时自动创建 webserver 用户。为此,您需要将 _AIRFLOW_WWW_USER_CREATE 设置为非空值。这不适用于生产环境,仅在您想使用生产镜像进行快速测试时有用。您至少需要通过 _AIRFLOW_WWW_USER_PASSWORD_AIRFLOW_WWW_USER_PASSWORD_CMD 传递密码来创建此类用户,类似于其他 *_CMD 变量,*_CMD 的内容将被评估为 shell 命令,其输出将设置为密码。

如果未设置任何 PASSWORD 变量,则用户创建将失败 - 出于安全原因,密码没有默认值。

参数

默认值

环境变量

用户名

admin

_AIRFLOW_WWW_USER_USERNAME

密码

_AIRFLOW_WWW_USER_PASSWORD_CMD_AIRFLOW_WWW_USER_PASSWORD

名字

Airflow

_AIRFLOW_WWW_USER_FIRSTNAME

姓氏

Admin

_AIRFLOW_WWW_USER_LASTNAME

邮箱

airflowadmin@example.com

_AIRFLOW_WWW_USER_EMAIL

角色

Admin

_AIRFLOW_WWW_USER_ROLE

如果指定了密码,将尝试创建用户,但如果尝试失败,入口点不会失败(这考虑了用户已存在的情况)。

例如,您可以在生产镜像中启动 webserver,同时初始化内部 SQLite 数据库并使用以下命令创建 admin/admin 管理员用户

docker run -it -p 8080:8080 \
  --env "_AIRFLOW_DB_MIGRATE=true" \
  --env "_AIRFLOW_WWW_USER_CREATE=true" \
  --env "_AIRFLOW_WWW_USER_PASSWORD=admin" \
    apache/airflow:3.0.0-python3.9 webserver
docker run -it -p 8080:8080 \
  --env "_AIRFLOW_DB_MIGRATE=true" \
  --env "_AIRFLOW_WWW_USER_CREATE=true" \
  --env "_AIRFLOW_WWW_USER_PASSWORD_CMD=echo admin" \
    apache/airflow:3.0.0-python3.9 webserver

上述命令执行 SQLite 数据库的初始化,使用 admin 密码和 Admin 角色创建管理员用户。它们还将本地端口 8080 转发到 webserver 端口,最后启动 webserver。

安装额外的依赖项

警告

以这种方式安装依赖项是一种非常方便的运行 Airflow 的方法,对于测试和调试非常有用。然而,不要被它的便利性所迷惑。您绝不应在生产环境中使用它。我们特意将其作为开发/测试依赖项,并且每当使用它时,我们都会打印警告。在生产环境中使用此方法存在固有的安全相关问题。以这种方式安装依赖项可能发生在任何时候 - 当您的容器重启时,当您的 K8S 集群中的机器重启时。在 K8S 集群中,这些事件可能在任何时候发生。这使您面临严重的漏洞,您的生产环境可能因 PyPI 中移除单个依赖项 - 甚至您依赖项的依赖项 - 而瘫痪。这意味着您将生产服务的可用性置于第三方开发人员手中。在任何时间、任何时刻,包括周末和节假日,这些第三方开发人员都可能导致您的生产 Airflow 实例崩溃,而您甚至可能不知道。这是一个严重的漏洞,类似于臭名昭著的 leftpad 问题。您可以通过构建自己的、不可变的自定义镜像来完全防止这种情况,其中依赖项已内置。您已收到警告。

可以通过指定 _PIP_ADDITIONAL_REQUIREMENTS 变量来安装额外的依赖项。该变量应包含在进入容器时应额外安装的依赖项列表。请注意,此选项会减慢 Airflow 的启动速度,因为每当任何容器启动时,它都必须安装新包,并且在生产环境中使用时会带来巨大的潜在安全漏洞(见下文)。因此,此选项仅应用于测试。测试完成后,您应创建内置依赖项的自定义镜像。

示例

docker run -it -p 8080:8080 \
  --env "_PIP_ADDITIONAL_REQUIREMENTS=lxml==4.6.3 charset-normalizer==1.4.1" \
  --env "_AIRFLOW_DB_MIGRATE=true" \
  --env "_AIRFLOW_WWW_USER_CREATE=true" \
  --env "_AIRFLOW_WWW_USER_PASSWORD_CMD=echo admin" \
    apache/airflow:3.0.0-python3.9 webserver

此方法仅在 Airflow 2.1.1 及以上版本的 Docker 镜像中可用。

本条目有帮助吗?