如何使用 docker-compose 运行 分布式气流架构时将新用户添加到 docker 图像
How to add new user to docker image when running distributed airflow architecture using docker-compose
(对原始问题进行了编辑以使其更清楚)
- 问题末尾的解决方案
- 答案中的另一个解决方案
目标和设置
主要目标是 运行 基于容器的处理(使用 DockerOperator),当 airflow celery worker 也在 运行 宁在 docker 容器内时。目前,我正在一台机器上测试设置,但最后我将 运行 在同一网络中运行的不同机器上的 celery worker 容器共享一些气流特定的挂载点(dags,logs ,plugins) 和用户 ID 等
我从 docker-compose.yml 启动整个设置,我设置 AIRFLOW_UID 以匹配我在主机上的 UID,并将 AIRFLOW_GID 设置为 0在气流文档中建议。在主机上,我的 UID 属于 docker 组,但不属于组 0。/var/run/docker.sock
已安装到容器中。
测试 1
我按照此处所示的示例进行操作 https://towardsdatascience.com/using-apache-airflow-dockeroperator-with-docker-compose-57d0217c8219。
将上述设置与官方气流图像 2.1.4 和 DockerOperator 结合使用。任务 运行 失败,这与默认用户没有 /var/run/docker.sock
所需的权限有关。 (我仍然需要检查将用户添加到主机上的组 0 是否会解决@JarekPotiuk 在他的评论中指出的问题。问题是组 0 是根组,很可能我不会获得许可将用户添加到其中)
[2021-09-27 05:38:30,863] {taskinstance.py:1463} ERROR - Task failed with exception
Traceback (most recent call last):
File "/home/airflow/.local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 706, in urlopen
chunked=chunked,
File "/home/airflow/.local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 394, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/usr/local/lib/python3.6/http/client.py", line 1291, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1337, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1286, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1046, in _send_output
self.send(msg)
File "/usr/local/lib/python3.6/http/client.py", line 984, in send
self.connect()
File "/home/airflow/.local/lib/python3.6/site-packages/docker/transport/unixconn.py", line 30, in connect
sock.connect(self.unix_socket)
PermissionError: [Errno 13] Permission denied
测试 2
我通过添加 'newuser' 和与我在主机上的 UID 相匹配的 UID 和 'docker' 与主机上的 UID 相匹配的组来从官方图像创建自定义图像。
但是,当我启动安装程序时,我在映像构建阶段创建的用户不存在,我不明白为什么。有一个 'default' 用户,其 uid=1234 和 gid=0。如果我使用官方图像并在 docker-compose.yml.
中定义 AIRFLOW_UID ,则会创建此默认用户
Docker 文件:
FROM apache/airflow:2.1.0
USER root
RUN useradd newuser -u 1234 -g 0
RUN groupadd --gid 986 docker \
&& usermod -aG docker newuser
USER newuser
此外,如果我不创建新用户而只是将 airflow 用户添加到 docker 组,那么 airflow 用户实际上会按原样添加到 docker 组。
docker-compose 会覆盖镜像构建阶段创建的用户吗?解决此问题的最佳方法是什么?
解决方案
此解决方案使得从 airflow 容器中使用 DockerOperator 以在主机上启动 DockerContainers 成为可能。
您可以选择默认 UID=50000 和 GID=0,也可以选择自定义 UID 和 GID=0。在主机上创建一个 docker 组并将选择的 UID 添加到其中。然后将容器内的airflow用户添加到docker组中。您可以通过在撰写文件中添加组来完成此操作
group_add:
- <docker GID>
此外,你还得把docker.sock文件挂载到容器
volumes:
- /var/run/docker.sock:/var/run/docker.sock
并添加一个变量 AIRFLOW__CORE__ENABLE_XCOM_PICKLING=True
I'm launching the whole setup from a docker-compose.yml where I set AIRFLOW_UID=1234 and AIRFLOW_GID=0. I'm using a docker image based on the official airflow image with the addition that I have created 'newuser' with gid=1234 and 'docker' group with gid that matches the one at the host.
你根本不应该这样做。当您使用不同于默认的 UID 时,Airflow 的图像入口点将自动创建用户 - 请参阅 https://airflow.apache.org/docs/docker-stack/entrypoint.html#allowing-arbitrary-user-to-run-the-container。事实上,您无需扩展 Airflow 图像即可实现所有您想做的事情。
您需要做的是,您需要在主机上的容器中创建您想要 运行 的用户 - 而不是在容器中。它应该属于 docker group
ON THE HOST - 而不是在容器中。
Docker 的工作方式与系统中定义的 kernel/users 相同,因此当您 运行 作为容器中的用户时,它是 运行 具有“主机”用户权限,因此您将 docker 套接字映射到容器内,它将能够使用 socket/run docker 命令,因为它将具有对主机的正确权限。
因此(如果您 运行 您的 docker-组成已经属于 docker 组的普通用户)最好的方法是快速入门中建议的方法 -即 运行 与您登录的“主机”用户的气流:https://airflow.apache.org/docs/apache-airflow/stable/start/docker.html
这也使得在容器中创建的所有文件都属于“登录用户”(如果它们是在安装在内部的目录中创建的 - 例如日志目录)。
但是如果您的目标是在“无人值守”环境中使用它,那么可能会在您的主机上创建新用户并将该用户添加到 0
和 docker
组中应该可以解决问题.
作为对@JarekPotiuk 的出色回答的补充,如果您的评论中指出问题与使用 DockerOperator
时的权限问题有关,您可以尝试以下方法。
这个想法是在 airflow
docker-compose.yml
文件中包含一个基于 bobrik/socat
图像的服务。类似于:
docker-proxy:
image: bobrik/socat
command: "TCP4-LISTEN:2375,fork,reuseaddr UNIX-CONNECT:/var/run/docker.sock"
ports:
- 2375:2375
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always
这将有效地创建一个 bridge 与你的主机 docker
守护进程,并允许你使用 DockerOperator
运行 你的容器而无需通过为 docker_url
参数提供适当的值来解决权限问题:
docker_based_task = DockerOperator(
task_id="a_docker_based_one",
docker_url="tcp://docker-proxy:2375"
# ...
)
(对原始问题进行了编辑以使其更清楚)
- 问题末尾的解决方案
- 答案中的另一个解决方案
目标和设置
主要目标是 运行 基于容器的处理(使用 DockerOperator),当 airflow celery worker 也在 运行 宁在 docker 容器内时。目前,我正在一台机器上测试设置,但最后我将 运行 在同一网络中运行的不同机器上的 celery worker 容器共享一些气流特定的挂载点(dags,logs ,plugins) 和用户 ID 等
我从 docker-compose.yml 启动整个设置,我设置 AIRFLOW_UID 以匹配我在主机上的 UID,并将 AIRFLOW_GID 设置为 0在气流文档中建议。在主机上,我的 UID 属于 docker 组,但不属于组 0。/var/run/docker.sock
已安装到容器中。
测试 1
我按照此处所示的示例进行操作 https://towardsdatascience.com/using-apache-airflow-dockeroperator-with-docker-compose-57d0217c8219。
将上述设置与官方气流图像 2.1.4 和 DockerOperator 结合使用。任务 运行 失败,这与默认用户没有 /var/run/docker.sock
所需的权限有关。 (我仍然需要检查将用户添加到主机上的组 0 是否会解决@JarekPotiuk 在他的评论中指出的问题。问题是组 0 是根组,很可能我不会获得许可将用户添加到其中)
[2021-09-27 05:38:30,863] {taskinstance.py:1463} ERROR - Task failed with exception
Traceback (most recent call last):
File "/home/airflow/.local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 706, in urlopen
chunked=chunked,
File "/home/airflow/.local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 394, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/usr/local/lib/python3.6/http/client.py", line 1291, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1337, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1286, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.6/http/client.py", line 1046, in _send_output
self.send(msg)
File "/usr/local/lib/python3.6/http/client.py", line 984, in send
self.connect()
File "/home/airflow/.local/lib/python3.6/site-packages/docker/transport/unixconn.py", line 30, in connect
sock.connect(self.unix_socket)
PermissionError: [Errno 13] Permission denied
测试 2
我通过添加 'newuser' 和与我在主机上的 UID 相匹配的 UID 和 'docker' 与主机上的 UID 相匹配的组来从官方图像创建自定义图像。
但是,当我启动安装程序时,我在映像构建阶段创建的用户不存在,我不明白为什么。有一个 'default' 用户,其 uid=1234 和 gid=0。如果我使用官方图像并在 docker-compose.yml.
中定义 AIRFLOW_UID ,则会创建此默认用户Docker 文件:
FROM apache/airflow:2.1.0
USER root
RUN useradd newuser -u 1234 -g 0
RUN groupadd --gid 986 docker \
&& usermod -aG docker newuser
USER newuser
此外,如果我不创建新用户而只是将 airflow 用户添加到 docker 组,那么 airflow 用户实际上会按原样添加到 docker 组。
docker-compose 会覆盖镜像构建阶段创建的用户吗?解决此问题的最佳方法是什么?
解决方案
此解决方案使得从 airflow 容器中使用 DockerOperator 以在主机上启动 DockerContainers 成为可能。
您可以选择默认 UID=50000 和 GID=0,也可以选择自定义 UID 和 GID=0。在主机上创建一个 docker 组并将选择的 UID 添加到其中。然后将容器内的airflow用户添加到docker组中。您可以通过在撰写文件中添加组来完成此操作
group_add:
- <docker GID>
此外,你还得把docker.sock文件挂载到容器
volumes:
- /var/run/docker.sock:/var/run/docker.sock
并添加一个变量 AIRFLOW__CORE__ENABLE_XCOM_PICKLING=True
I'm launching the whole setup from a docker-compose.yml where I set AIRFLOW_UID=1234 and AIRFLOW_GID=0. I'm using a docker image based on the official airflow image with the addition that I have created 'newuser' with gid=1234 and 'docker' group with gid that matches the one at the host.
你根本不应该这样做。当您使用不同于默认的 UID 时,Airflow 的图像入口点将自动创建用户 - 请参阅 https://airflow.apache.org/docs/docker-stack/entrypoint.html#allowing-arbitrary-user-to-run-the-container。事实上,您无需扩展 Airflow 图像即可实现所有您想做的事情。
您需要做的是,您需要在主机上的容器中创建您想要 运行 的用户 - 而不是在容器中。它应该属于 docker group
ON THE HOST - 而不是在容器中。
Docker 的工作方式与系统中定义的 kernel/users 相同,因此当您 运行 作为容器中的用户时,它是 运行 具有“主机”用户权限,因此您将 docker 套接字映射到容器内,它将能够使用 socket/run docker 命令,因为它将具有对主机的正确权限。
因此(如果您 运行 您的 docker-组成已经属于 docker 组的普通用户)最好的方法是快速入门中建议的方法 -即 运行 与您登录的“主机”用户的气流:https://airflow.apache.org/docs/apache-airflow/stable/start/docker.html
这也使得在容器中创建的所有文件都属于“登录用户”(如果它们是在安装在内部的目录中创建的 - 例如日志目录)。
但是如果您的目标是在“无人值守”环境中使用它,那么可能会在您的主机上创建新用户并将该用户添加到 0
和 docker
组中应该可以解决问题.
作为对@JarekPotiuk 的出色回答的补充,如果您的评论中指出问题与使用 DockerOperator
时的权限问题有关,您可以尝试以下方法。
这个想法是在 airflow
docker-compose.yml
文件中包含一个基于 bobrik/socat
图像的服务。类似于:
docker-proxy:
image: bobrik/socat
command: "TCP4-LISTEN:2375,fork,reuseaddr UNIX-CONNECT:/var/run/docker.sock"
ports:
- 2375:2375
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always
这将有效地创建一个 bridge 与你的主机 docker
守护进程,并允许你使用 DockerOperator
运行 你的容器而无需通过为 docker_url
参数提供适当的值来解决权限问题:
docker_based_task = DockerOperator(
task_id="a_docker_based_one",
docker_url="tcp://docker-proxy:2375"
# ...
)