入口点

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

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

允许任意用户运行容器

Airflow 镜像与 Open-Shift 兼容,这意味着您可以使用随机用户 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 的快速入门指南中,可以像 初始化 docker compose 环境 中所述的那样通过 AIRFLOW_UID 变量传递 UID。

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

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

这样的任意用户必须能够写入某些需要写入访问权限的目录,并且由于不建议出于安全原因允许对“其他”进行写入访问,因此 OpenShift 指南引入了使所有此类文件夹都具有 0 (root) 组 ID (GID) 的概念。 在 Airflow 生产镜像中需要写入访问权限的所有目录的 GID 都设置为 0(并且该组可写)。 我们正在遵循该概念,并且所有需要写入访问权限的目录都遵循该概念。

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

但是,umask 设置仅适用于容器的运行时 - 它在构建镜像期间不使用。 如果您想扩展镜像并添加自己的软件包,则应记得在 docker 命令前面添加 umask 0002 - 这样,任何需要组访问权限的安装创建的目录也将对该组可写。 例如,可以这样做

RUN umask 0002; \
    do_something; \
    do_otherthing;

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

等待 Airflow 数据库连接

入口点正在等待与数据库的连接,而与数据库引擎无关。 这使我们能够提高环境的稳定性。

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

等待 Celery 代理连接

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

该脚本会根据 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:2.10.4-python3.8 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:2.10.4-python3.8 python -c "print('test')"
test

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

docker run -it apache/airflow:2.10.4-python3.8 airflow webserver

如果有任何其他参数 - 它们将简单地传递给 “airflow” 命令

> docker run -it apache/airflow:2.10.4-python3.8 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 a Airflow webserver instance

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

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

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

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

你的入口点可能会动态修改或添加变量。例如,下面的入口点设置了数据库检查的最大次数,该次数来自作为镜像执行参数传递的第一个参数(这只是一个有点没用的例子,但应该能让读者了解如何使用它)。

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

请确保 Airflow 的入口点使用 exec /entrypoint "${@}" 作为自定义入口点中的最后一个命令运行。这样信号才能正确传播,并且参数会像往常一样传递给入口点(如果需要传递一些额外的参数,可以使用上面的 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 工作进程的温和关闭。在这种情况下,dumb-init 将仅将信号传播到主进程,而不是传播到与主进程在同一进程组中生成的进程。例如,对于 Celery,主进程会将工作进程置于“离线”模式,并等待所有正在运行的任务完成,然后才会终止所有进程。

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

其他快速测试选项

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

升级 Airflow 数据库

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

创建管理员用户

当您输入时,入口点还可以自动创建 Web 服务器用户。您需要将 _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

如果指定了密码,则将尝试创建用户,但如果尝试失败(这种情况说明用户已经创建),则入口点不会失败。

例如,您可以使用以下命令在生产镜像中启动 Web 服务器,同时初始化内部 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:2.10.4-python3.8 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:2.10.4-python3.8 webserver

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

安装其他依赖项

警告

以这种方式安装依赖项是一种非常方便的运行 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:2.10.4-python3.8 webserver

此方法仅从 Airflow 2.1.1 及更高版本的 Docker 镜像开始可用。

此条目是否有帮助?