使用docker-compose运行分布式Airflow时如何为Docker镜像添加新用户
- 问题末尾附解决方案
- 回答中另有其他解决方案
核心目标是在Airflow Celery Worker运行于Docker容器内的场景下,使用DockerOperator执行基于容器的处理任务。目前我在单台机器上测试部署,最终会将Celery Worker容器部署在同一网络下的多台独立机器上,共享Airflow专用挂载点(dags、logs、plugins)和用户ID等资源。
我通过docker-compose.yml启动整套环境,按照Airflow官方文档建议,将AIRFLOW_UID设为与宿主机我的UID一致,AIRFLOW_GID设为0。宿主机上我的UID属于docker组,但不属于root组(gid 0)。已将/var/run/docker.sock挂载到容器内。
我参考相关技术文章的示例,使用官方Airflow 2.1.4镜像搭配上述部署配置和DockerOperator运行,任务执行失败,原因是默认用户没有/var/run/docker.sock的访问权限。(我仍需验证将宿主机用户加入gid 0组能否解决问题,但gid 0是root组,我大概率没有权限将用户加入该组)
报错日志如下:
[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
我基于官方镜像构建了自定义镜像,新增了UID与宿主机我的UID一致的newuser,以及GID与宿主机docker组一致的docker组。
但启动部署后,镜像构建阶段创建的用户不存在,我无法理解原因。环境中只有一个uid=1234、gid=0的default用户,该用户是我仅使用官方镜像并在docker-compose.yml中定义AIRFLOW_UID时自动创建的。
Dockerfile内容如下:
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
另外,若我不创建newuser,仅将airflow用户加入docker组,该操作会正常生效。
请问docker-compose会覆盖镜像构建阶段创建的用户吗?该问题的最优解决方案是什么?
本方案可实现从Airflow容器内通过DockerOperator在宿主机上启动Docker容器。
你可以选择默认的UID=50000、GID=0,或者自定义UID、GID=0。在宿主机创建docker组并将选中的UID加入该组,再将容器内的airflow用户加入docker组,可通过在compose文件中添加如下配置实现:
group_add: - <docker GID>
此外,你需要将docker.sock文件挂载到容器:
volumes: - /var/run/docker.sock:/var/run/docker.sock
同时添加环境变量AIRFLOW__CORE__ENABLE_XCOM_PICKLING=True
关于用户被覆盖的疑问
docker-compose本身不会覆盖镜像构建阶段创建的用户,该问题是官方Airflow镜像的启动入口脚本的特性:官方Airflow镜像的entrypoint会根据传入的AIRFLOW_UID、AIRFLOW_GID环境变量自动调整运行用户的ID和组,如果你传入的UID和自定义创建的newuser的UID匹配但用户名不同,entrypoint会优先使用默认的airflow用户配置,覆盖你自定义的newuser设置。
最优解决方案
不需要自定义镜像新增用户,直接使用官方镜像配合以下配置即可:
- 保持使用官方镜像默认的airflow用户,无需额外构建自定义用户
- 在docker-compose的Celery Worker服务配置里添加
group_add字段,值为宿主机docker组的GID,直接给容器内的airflow用户授予docker.sock的访问权限 - 保留docker.sock的挂载配置和XCOM Pickling的环境变量
该方案不需要修改宿主机用户组权限,也不需要自定义构建镜像,适配多机器部署场景,只要所有宿主机的docker组GID一致即可正常运行。
内容的提问来源于stack exchange,提问作者MrBronson

