入口点¶
如果您使用的是生产镜像的默认入口点,则在容器启动时会自动执行一些操作。在某些情况下,您可以将环境变量传递给镜像以触发某些行为。
控制“执行”行为的变量以 _AIRFLOW
开头,以区别于以 AIRFLOW
开头的用于构建镜像的变量。
允许任意用户运行容器¶
Airflow 镜像与 Open-Shift 兼容,这意味着您可以使用随机用户 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 的快速入门指南中,可以通过 初始化 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) - 默认端口 5672redis://
- 默认端口 6379postgres://
- 默认端口 5432mysql://
- 默认端口 3306
等待连接涉及检查匹配的端口是否打开。主机信息来自 Airflow 配置。
执行命令¶
如果第一个参数等于“bash” - 您将进入 bash shell,或者如果您指定了额外的参数,则可以执行 bash 命令。例如
docker run -it apache/airflow:2.9.2-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.9.2-python3.8 python -c "print('test')"
test
如果第一个参数等于“airflow” - 则其余参数将被视为要执行的 airflow 命令。例如
docker run -it apache/airflow:2.9.2-python3.8 airflow webserver
如果有任何其他参数 - 它们将被简单地传递给“airflow”命令
> docker run -it apache/airflow:2.9.2-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
指令调用。但是,您必须记住以与 Airflow 入口点相同的方式使用 dumb-init
,否则您可能会遇到信号传播不正确的问题(请参阅下一章)。
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、设置变量并检查数据库是否正在运行后运行其他代码。
除了运行常规命令(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,并使用 |
其他快速测试选项¶
以下选项主要用于快速测试镜像,例如使用快速入门 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 |
|
姓氏 |
Admin |
|
电子邮件 |
|
|
角色 |
Admin |
|
如果指定了密码,则将尝试创建用户,但如果尝试失败,入口点不会失败(这说明了用户已创建的情况)。
例如,您可以使用以下命令启动生产镜像中的 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.9.2-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.9.2-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.9.2-python3.8 webserver
此方法仅适用于 Airflow 2.1.1 及更高版本的 Docker 镜像。