首页 > 解决方案 > pathlib / configparser 的气流问题 - “PosixPath”对象不可迭代

问题描述

我正在尝试将我的气流设置容器化。我的任务是保持环境不变,只需将其移动到 docker 容器中即可。我们目前在 anaconda 环境中安装了 Airflow 和所有依赖项。所以我所做的是创建了一个自定义 docker 镜像,它安装了 anaconda 并创建了我的环境。问题是,我们当前的环境使用 systemd 服务来启动气流,Docker 需要它通过气流命令“airflow webserver/scheduler/worker”运行它,当我这样运行它时,我得到一个错误。启动调度程序后出现错误。

我们的 DAG 需要一个自定义存储库来帮助与我们的数据库服务器进行通信。在该 repo 中,我们使用 pathlib 获取配置文件的路径并将其传递给 configparser。

基本上是这样的:

import configparser
from pathlib import Path

config = configparser.ConfigParser()
p = Path(__file__)
p = p.parent
config_file_name = 'comms.conf'
config.read(p.joinpath('config', config_file_name))

这会为我在 Airflow 中的所有 DAG 引发以下错误:

Broken DAG: [/opt/airflow/dags/example_folder/example_dag.py] 'PosixPath' object is not iterable

在命令行上,错误是:

[2021-01-11 19:53:13,868] {dagbag.py:259} ERROR - Failed to import: /opt/airflow/dags/example_folder/example_dag.py
Traceback (most recent call last):
  File "/opt/anaconda3/envs/airflow/lib/python3.7/site-packages/airflow/models/dagbag.py", line 256, in process_file
    m = imp.load_source(mod_name, filepath)
  File "/opt/anaconda3/envs/airflow/lib/python3.7/imp.py", line 172, in load_source
    module = _load(spec)
  File "<frozen importlib._bootstrap>", line 696, in _load
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/opt/airflow/example_folder/example_dag.py", line 8, in <module>
    dag = Dag()
  File "/opt/airflow/dags/util/dag_base.py", line 27, in __init__
    self.comms = get_comms(Variable.get('environment'))
  File "/opt/airflow/repository/repo_folder/custom_script.py", line 56, in get_comms
    config = get_config('comms.conf')
  File "/opt/airflow/repository/repo_folder/custom_script.py", line 39, in get_config
    config.read(p.joinpath('config', config_file_name))
  File "/opt/anaconda3/envs/airflow/lib/python3.7/site-packages/backports/configparser/__init__.py", line 702, in read
    for filename in filenames:
TypeError: 'PosixPath' object is not iterable

我能够在 docker 容器之外复制这种行为,所以我认为这与它没有任何关系。气流作为系统服务运行的方式与通过cli运行的方式之间必须有所不同吗?

这是我工作的气流服务文件:

[Unit]
Description=Airflow webserver daemon
After=network.target postgresql.service mysql.service redis.service rabbitmq-server.service
Wants=postgresql.service mysql.service redis.service rabbitmq-server.service

[Service]
EnvironmentFile=/etc/sysconfig/airflow
User=airflow
Group=airflow
Type=simple
ExecStart=/opt/anaconda3/envs/airflow/bin/airflow webserver
Restart=on-failure
RestartSec=5s
PrivateTmp=true

[Install]
WantedBy=multi-user.target

这是我在服务文件中使用的气流环境文件。请注意,我需要在本地导出这些环境变量以使气流在 cli 中运行到这一点。另请注意,自定义存储库位于 /opt/airflow 目录中。

AIRFLOW_CONFIG=/opt/airflow/airflow.cfg
AIRFLOW_HOME=/opt/airflow
PATH=/bin:/opt/anaconda3/envs/airflow/bin:/opt/airflow/etl:/opt/airflow:$PATH
PYTHONPATH=/opt/airflow/etl:/opt/airflow:$PYTHONPATH

我的气流配置是默认的,除了以下更改:

executor = CeleryExecutor
sql_alchemy_conn = postgresql+psycopg2://airflow:airflow@192.168.x.x:5432/airflow
load_examples = False
logging_level = WARN
broker_url = amqp://guest:guest@127.0.0.1:5672/
result_backend = db+postgresql://airflow:airflow@192.168.x.x:5432/airflow
catchup_by_default =  False

配置解析器==3.5.3

我的 conda 环境使用的是 python 3.7,airflow 版本是 1.10.14。它在 Centos7 服务器上运行。如果有人有任何可以帮助的想法,我会适当的!

编辑:如果我将行更改为config.read(p.joinpath('config', config_file_name))直接指向这样的配置,config.read('/opt/airflow/repository/repo_folder/config/comms.conf')它可以正常工作。那么它与configparser如何处理pathlib输出有关吗?但是,如果气流通过 systemd 服务运行,这没有问题吗?

Edit2:我也可以将 pathlib 对象包装在 str() 中,它可以工作。config.read(str(p.joinpath('config', config_file_name)))我只是想知道为什么这与 systemd 服务配合得很好。我担心其他东西会被破坏?

标签: pythonairflowconfigparserpathlib

解决方案


配置文件的路径计算错误。

这是因为以下行

# filename: custom_script.py
p = p.parent
confpath = p.joinpath('config', config_file_name))

confpath评估为/opt/airflow/repository/repo_folder/config/comms.conf

您共享的配置文件所在的路径是/opt/airflow/repository/repo_folder/conn.conf.

您需要repo_folder通过使用文件夹所在的路径构造其路径来解析配置文件custom_script.py

# filename: custom_script.py

from pathlib import Path

p = Path(dirname(__file__))
p = p.parent
confpath = p.joinpath(config_file_name)

推荐阅读