入口点¶
如果您使用生产镜像的默认入口点,容器启动时会自动执行一些操作。在某些情况下,您可以通过向镜像传递环境变量来触发这些行为。
控制“执行”行为的变量以 _AIRFLOW
开头,以区分用于构建镜像的以 AIRFLOW
开头的变量。
允许任意用户运行容器¶
Airflow 镜像与 OpenShift 兼容,这意味着您可以使用随机用户 ID 和组 ID 0
(root
) 来启动它。如果您想使用 Airflow 以外的用户运行镜像,则必须将用户的 GID 设置为 0
。如果您尝试使用不同的组,入口点将退出并报错。
OpenShift 在启动容器时随机分配 UID,但您也可以在手动运行镜像时利用这种灵活的 UID。这可能很有用,例如当您想在 Linux 上从主机系统挂载 dag
和 logs
文件夹时,在这种情况下,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,并且使用了 scheduler
、celery
命令之一,则入口点将等待直到 Celery 代理 DB 连接可用。
该脚本根据 URL 方案检测后端类型,并在 URL 中未指定端口号时分配默认端口号。然后它会循环直到可以建立到指定主机/端口的连接。它会尝试 CONNECTION_CHECK_MAX_COUNT
次,并在每次检查之间休眠 CONNECTION_CHECK_SLEEP_TIME
。要禁用检查,请设置 CONNECTION_CHECK_MAX_COUNT=0
。
支持的方案
amqp(s)://
(rabbitmq) - 默认端口 5672redis://
- 默认端口 6379postgres://
- 默认端口 5432mysql://
- 默认端口 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、设置变量并检查数据库正在运行之后运行额外的代码。
与其运行常规命令 - scheduler
、webserver
,不如运行可以嵌入到镜像中的自定义脚本。完成自定义设置后,您甚至可以在自定义脚本中执行 Airflow 的常用组件 - scheduler
、webserver
。与自定义入口点类似,可以通过扩展镜像来添加它。
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-init
的 single-child
行为,该行为仅将信号传播到单个子进程。
下表总结了 DUMB_INIT_SETSID
的可能值及其用例。
变量值 |
用例 |
1 (默认) |
将信号传播到容器中运行的主进程的进程组中的所有进程。 如果您通过 |
0 |
仅将信号传播到主进程。 如果您的主进程正常处理信号,这将非常有用。一个很好的例子是 Celery worker 的温和关闭。 对于 Airflow 的 Celery worker,您应该将变量设置为 0,并使用 |
额外的快速测试选项¶
以下选项主要用于快速测试镜像 - 例如使用 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 |
|
姓氏 |
Admin |
|
邮箱 |
|
|
角色 |
Admin |
|
如果指定了密码,将尝试创建用户,但如果尝试失败,入口点不会失败(这考虑了用户已存在的情况)。
例如,您可以在生产镜像中启动 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 镜像中可用。