返回

5招解决Airflow Docker的google-generativeai导入难题

Ai

搞定 Airflow 里的 ModuleNotFoundError: google-generativeai 导入难题

运行 Airflow DAG 时,遇到 ModuleNotFoundError 绝对是个让人头疼的事儿,特别是你明明记得已经安装了那个库!就像这次遇到的 ModuleNotFoundError: No module named 'google.generativeai',明明通过 Docker Compose 的 _PIP_ADDITIONAL_REQUIREMENTS 环境变量把 google-generativeai==0.8.3 装好了,甚至 pip list 也确认过它的存在,结果 DAG 就是不认账。

Broken DAG: \[/opt/airflow/dags/news_gpt_dag.py\]
Traceback (most recent call last):
File "\<frozen importlib.\_bootstrap\>", line 488, in \_call_with_frames_removed
File "/opt/airflow/dags/news_gpt_dag.py", line 10, in \<module\>
import google.generativeai as genai
ModuleNotFoundError: No module named 'google.generativeai'

别急,这个问题通常不是什么玄学,咱们一步步来分析把它揪出来。

问题根源分析

Airflow 在 Docker Compose 环境下运行时,通常会涉及多个服务容器:scheduler(调度器)、webserver(网页服务器)、worker(执行任务的工作单元,可能是 CeleryWorker、KubernetesPodOperator 等)、有时还有 triggerer(处理延迟任务)。

关键点在于:哪个组件真正执行你的 DAG 代码?

  1. DAG 解析 (Parsing): schedulerwebserver 都会扫描并解析 DAG 文件,检查语法、结构等。它们需要能 import DAG 文件里引用的所有库。
  2. 任务执行 (Task Execution): worker (或 executor 对应的组件) 实际运行 DAG 中的任务。它同样需要能 import 任务代码里用到的所有库。

_PIP_ADDITIONAL_REQUIREMENTS 这个环境变量的作用是在构建 Airflow 官方镜像时,顺带帮你把这些额外的 Python 库安装进去。理论上,所有使用这个镜像的服务(scheduler, webserver, worker)都应该包含这些库。

那为什么还会报 ModuleNotFoundError 呢?几种可能的原因:

  • 安装不一致: 虽然你检查了某个容器(比如 webserver 或你 docker exec 进去的那个)发现库已安装,但不代表 所有 需要它的容器(特别是 workerscheduler)都真的安装成功了。构建过程可能在某个环节出了点小差错。
  • 环境隔离问题(可能性小): 虽然 Docker 设计上是隔离的,但如果你的 docker-compose.yaml 或自定义镜像做了些特别的操作(比如挂载了不同的 Python 路径、使用了不同的 Python 解释器),可能导致某个容器找不到正确的库安装位置。对于标准 Airflow 镜像,这种情况不常见。
  • 缓存惹的祸: Docker 的构建缓存有时会“耍赖”。它可能觉得某个安装步骤没变化,就跳过了,导致新的 _PIP_ADDITIONAL_REQUIREMENTS 没有生效。或者,即使库安装了,但 Python 解释器或 Airflow 进程因为某种原因没能立刻识别到新安装的库路径。
  • 容器启动时序: 极少数情况下,DAG 解析进程启动时,pip 安装的依赖可能还没完全就绪或对 Python 环境可见。

解决方案

下面我们来挨个试试看,总有一款适合你!

方案一:确认所有 Airflow 服务容器都已安装依赖

这是最直接也最应该先做的一步。_PIP_ADDITIONAL_REQUIREMENTS 应该作用于所有基于该镜像启动的容器,但咱们得亲手验证下。

原理与作用:

确保 Airflow 的核心组件,尤其是负责解析 DAG (scheduler, webserver) 和执行任务 (worker) 的容器,确实包含了 google-generativeai 这个库。眼见为实嘛!

操作步骤:

你需要分别进入 schedulerworker(如果你有多个 worker,可能需要检查几个代表性的)和 webserver 容器,执行 pip listpip show 命令。

假设你的 docker-compose.yaml 文件定义的服务名分别是 airflow-schedulerairflow-workerairflow-webserver(请根据你的实际文件名调整):

# 检查 scheduler 容器
docker-compose exec airflow-scheduler pip list | grep google-generativeai
docker-compose exec airflow-scheduler pip show google-generativeai

# 检查 worker 容器
docker-compose exec airflow-worker pip list | grep google-generativeai
docker-compose exec airflow-worker pip show google-generativeai

# 检查 webserver 容器 (虽然通常不是执行任务的主力,但有时调试或 DAG 解析也需要)
docker-compose exec airflow-webserver pip list | grep google-generativeai
docker-compose exec airflow-webserver pip show google-generativeai

预期结果:

每个命令都应该能输出类似 google-generativeai 0.8.3 的信息,并且 pip show 能显示库的详细安装路径。

如果发现某个容器没有:

这就说明 _PIP_ADDITIONAL_REQUIREMENTS 没有在所有容器中正确生效。这时候,方案二(重建镜像)或者方案三(自定义 Dockerfile)就是你需要考虑的了。检查一下 docker-compose.yaml 文件,确认所有 Airflow 服务都使用了配置了 _PIP_ADDITIONAL_REQUIREMENTS 的同一个镜像。

方案二:强制重建 Docker 镜像并清理环境

Docker 的缓存机制是为了加速构建,但有时它会“好心办坏事”,导致依赖没有按预期更新。彻底清理并强制重新构建可以解决这个问题。

原理与作用:

通过清除旧的 Docker 镜像缓存和可能存在的旧数据卷(volumes),确保 docker-compose build 完全基于最新的配置(包括 _PIP_ADDITIONAL_REQUIREMENTS)来构建镜像,并且启动一个“干净”的运行环境。

操作步骤:

  1. 停止并移除当前运行的容器:

    docker-compose down -v
    

    注意: -v 参数会同时移除关联的数据卷!如果你的数据卷里有不想丢失的数据(比如 Airflow 的元数据数据库、日志等,并且 你没有配置外部存储),请先备份或移除 -v 参数(但这可能无法彻底解决问题,如果问题出在卷内数据)。对于依赖安装问题,移除卷通常是安全的。

  2. (可选但推荐) 清理 Docker 缓存:

    docker system prune -af
    

    警告: 这个命令会删除所有未使用的 Docker 镜像、容器、网络和构建缓存。非常彻底! 确认你没有其他项目依赖这些未使用的资源。如果担心影响太大,可以考虑只清理特定镜像的缓存,或者干脆跳过这步,直接用 --no-cache 构建。

  3. 强制重新构建镜像(不使用缓存):

    docker-compose build --no-cache
    

    --no-cache 参数告诉 Docker 在构建过程中忽略所有缓存层,从头开始执行 Dockerfile(或基于 _PIP_ADDITIONAL_REQUIREMENTS 的内部构建步骤)中的每一条指令。

  4. 重新启动服务:

    docker-compose up -d
    

安全建议:

  • 再次强调,docker-compose down -vdocker system prune -af 是破坏性操作。操作前务必了解其后果,特别是生产环境或存有重要数据的开发环境。
  • 如果不确定,可以先尝试 docker-compose build --no-cache,如果问题依旧,再考虑清理卷和系统缓存。

方案三:使用自定义 Dockerfile 安装依赖

_PIP_ADDITIONAL_REQUIREMENTS 是个方便的快捷方式,但不够灵活。当你需要更精细地控制安装过程,或者遇到 _PIP_ADDITIONAL_REQUIREMENTS 解决不了的问题时,自定义 Dockerfile 是更可靠的选择。

原理与作用:

创建一个你自己的 Dockerfile,继承自官方 Airflow 镜像,然后在里面明确地使用 pip install 命令来安装 google-generativeai 和其他依赖。这让你对安装环境有完全的掌控权。

操作步骤:

  1. 在项目根目录(或你的 docker-compose.yaml 文件旁边)创建一个名为 Dockerfile 的文件。

  2. 编辑 Dockerfile

    # 使用你当前 docker-compose.yaml 文件中指定的 Airflow 基础镜像
    FROM apache/airflow:2.10.2
    
    # (可选) 如果需要安装系统依赖,可能需要切换到 root 用户
    # USER root
    # RUN apt-get update && apt-get install -y --no-install-recommends some-system-package && apt-get clean && rm -rf /var/lib/apt/lists/*
    # USER airflow
    
    # 切换回 airflow 用户,这是官方镜像的推荐做法
    USER airflow
    
    # 可以复制一个 requirements.txt 文件进去安装 (推荐)
    # COPY requirements.txt /requirements.txt
    # RUN pip install --no-cache-dir -r /requirements.txt
    
    # 或者直接安装指定的包
    # --user 参数可以将包安装到用户目录下,有时可以避免权限问题
    # 但对于需要在所有 Airflow 进程中使用的库,全局安装通常更简单直接
    # 使用 --no-cache-dir 可以减小镜像体积
    RUN pip install --no-cache-dir google-generativeai==0.8.3 google-auth aiohttp
    
    • 你可以将所有 Python 依赖写入一个 requirements.txt 文件,然后用 COPYpip install -r,这样更方便管理。
    • --no-cache-dir 阻止 pip 缓存下载的包,有助于减小最终镜像的大小。
  3. 修改 docker-compose.yaml 文件:
    找到所有使用 Airflow 镜像的服务(airflow-scheduler, airflow-webserver, airflow-worker 等),将 image: 指令替换为 build: 指令。

    修改前:

    services:
      airflow-scheduler:
        image: apache/airflow:2.10.2
        environment:
          # 移除这行,因为它现在由 Dockerfile 处理了
          # _PIP_ADDITIONAL_REQUIREMENTS: ${_PIP_ADDITIONAL_REQUIREMENTS:- google-generativeai google-auth aiohttp}
        # ... 其他配置 ...
      # ... 其他服务类似 ...
    

    修改后:

    services:
      airflow-scheduler:
        build:
          context: . # Dockerfile 所在的目录,通常是项目根目录
          # dockerfile: Dockerfile # 如果你的 Dockerfile 文件名不是 Dockerfile,用这个指定
        # environment: # _PIP_ADDITIONAL_REQUIREMENTS 可以移除了
        # ... 其他配置 ...
      airflow-webserver:
        build:
          context: .
        # ... 其他配置 ...
      airflow-worker:
        build:
          context: .
        # ... 其他配置 ...
      # ... 其他服务确保都改成 build ...
    

    重要: 别忘了从 environment 部分移除 _PIP_ADDITIONAL_REQUIREMENTS,避免混淆或潜在冲突。

  4. 重新构建并启动:

    # 先停掉旧的 (如果还在运行)
    docker-compose down -v # 同样注意 -v 参数
    
    # 构建新镜像
    docker-compose build # 这次不用 --no-cache,除非你想强制完全重构
    
    # 启动服务
    docker-compose up -d
    

进阶使用技巧:

  • 多阶段构建 (Multi-stage builds): 对于复杂的构建(比如需要编译 C 扩展),可以使用多阶段构建。在一个临时的构建阶段安装编译工具链和依赖,编译好库,然后把最终的产物(比如 wheel 文件或安装好的库目录)复制到一个干净的、不含编译工具的最终镜像里,可以大大减小最终镜像的体积。
  • 管理 requirements.txt: 将所有 Python 依赖(包括 Airflow 本身,如果使用自定义基础镜像的话)以及像 google-generativeai 这样的第三方库都放在 requirements.txt 文件里统一管理,更容易维护版本。

方案四:检查 Python 环境和路径

虽然可能性相对较低,但确认 Airflow 进程使用的 Python 环境和 sys.path 是否包含了库的安装位置,总归是个好习惯。

原理与作用:

Python 在 import 模块时,会搜索 sys.path 列表中的路径。我们需要确保 google-generativeai 安装的路径(site-packages 目录)在 Airflow 进程的 sys.path 里。

操作步骤:

  1. 找出库的实际安装位置:
    进入一个应该已安装库的容器(比如 worker):

    docker-compose exec airflow-worker pip show google-generativeai
    

    留意输出中的 Location: 字段,例如 /home/airflow/.local/lib/python3.8/site-packages/usr/local/lib/python3.8/site-packages

  2. 查看 Airflow 进程的 sys.path
    还是在同一个容器里:

    docker-compose exec airflow-worker python -c "import sys; from pprint import pprint; pprint(sys.path)"
    

    检查输出的路径列表里,是否包含了上面 pip show 显示的 Location 值(或者是其父目录,比如 /home/airflow/.local/lib/python3.8/)。

  3. 确认使用的 Python 解释器:

    docker-compose exec airflow-worker which python
    

    确保这个 Python 解释器就是你期望的那个(通常是 /usr/local/bin/python 或类似路径)。

如果路径不匹配:

这种情况比较少见,通常意味着你的环境被深度定制过。可能的原因包括:

  • 你在 Dockerfile 或启动脚本里修改了 PYTHONPATH 环境变量。
  • 你安装了多个 Python 版本,并且 Airflow 运行的 Python 版本不是你安装库时用的那个。
  • 有奇怪的卷挂载覆盖了 Python 的 site-packages 目录。

解决方法需要根据具体情况来定,主要是检查你的自定义配置,确保库安装到了正确的 Python 环境,并且该环境的 site-packages 目录在 sys.path 中。

方案五:重启相关服务

有时候,就是这么简单。可能是某些进程没有及时感知到新安装的库。

原理与作用:

重启 Airflow 的关键服务(特别是 schedulerworker)会强制它们重新加载环境和代码,有时能解决因时序或缓存问题导致的 ModuleNotFoundError

操作步骤:

在确认库已经通过方案一验证存在于所有相关容器后,可以尝试重启服务:

docker-compose restart airflow-scheduler airflow-worker airflow-webserver

或者,如果你只想重启出问题的组件(比如只有 worker 报这个错):

docker-compose restart airflow-worker

注意: 这通常是解决安装好库但尚未被识别的“最后一招”,或者是在应用了其他修复(如重建镜像)后的确认步骤。如果每次启动都需要手动重启才能解决,那说明根源问题(可能是安装过程、镜像构建或环境配置)还没真正解决。

其他注意事项

  • 依赖版本冲突: 有时 google-generativeai 可能依赖了某个库的特定版本,而这个版本恰好与 Airflow 或你安装的其他库有冲突。可以尝试在容器内运行 pip check 检查下:

    docker-compose exec airflow-worker pip check
    

    如果发现冲突,你可能需要调整 google-generativeai 的版本,或者寻找能兼容的版本组合。

  • Airflow 版本兼容性: 虽然 google-generativeai 是一个通用 Python 库,但它底层可能依赖某些 Airflow 也在用的核心库(例如 grpcio, protobuf)。确保使用的库版本与你的 Airflow 版本(2.10.2)大致兼容,可以减少潜在问题。不过对于 0.8.3 这个版本来说,直接冲突的可能性不大。

  • 资源限制: 如果你的 Docker 容器资源(内存、CPU)被限制得太紧,pip 安装过程可能会失败或被中断,即使 _PIP_ADDITIONAL_REQUIREMENTS 写对了。检查 Docker 容器的日志,看看有没有 OOM (Out Of Memory) 或其他相关的错误信息。

通常情况下,通过方案一确认问题所在,然后采用方案二(强制重建)或方案三(自定义 Dockerfile)都能彻底解决这类 ModuleNotFoundError。祝你好运!