Docker Compose运行FastAPI项目报ModuleNotFoundError错误求助
问题背景
- 项目为Docker学习练习项目,技术栈为Python + FastAPI,通过Dockerfile、Docker Compose完成容器编排,对接PostgreSQL数据库,使用SQLAlchemy作为ORM框架
- 本地命令行直接启动项目运行正常,通过Docker Compose启动时先后抛出两类模块找不到的异常:
- 首个报错:
ModuleNotFoundError: No module named 'routers'- 尝试修复首个问题后出现第二个报错,且一直未解决:
ModuleNotFoundError: No module named 'sqlalchemy' - 已确认requirements.txt中已正确声明SQLAlchemy依赖:
SQLAlchemy==1.4.39
现有代码与配置
- 主入口文件
main.py:
from fastapi import FastAPI from .routers import roles app = FastAPI() app.include_router(roles.router)
- 路由模块代码:
from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from db.postgres_connection import SessionLocal, engine from models import roles from schemas import roles as schemas roles.Base.metadata.create_all(bind=engine) router = APIRouter() def get_db(): db = SessionLocal() try: yield db finally: db.close() @router.get('/api/v1/roles/', response_model=list[schemas.RoleBase]) async def get_roles(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): roles = get_roles(db, skip=skip, limit=limit) return roles
- Dockerfile配置:
FROM python:3.10.5-slim-buster WORKDIR /code COPY ./app ./code/app COPY ./requirements.txt /code/ RUN pip install -r requirements.txt EXPOSE 8000 CMD [ "uvicorn", "app.main:app", "--reload" ]
- docker-compose.yml配置:
version: '3.9' services: web: build: . ports: - '8000:8000' volumes: - .:/app db: image: postgres restart: always environment: POSTGRES_USER: hamel POSTGRES_PASSWORD: contrasena POSTGRES_DB: bankmel volumes: - /home/isla/storage:/var/lib/postgresql/data ports: - '5432:5432'
- 项目目录结构参考:

问题根因
一共3处核心配置/代码错误,直接触发上述两个报错,还存在隐性运行bug:
- Dockerfile COPY路径配置错误
WORKDIR已设置为/code,COPY ./app ./code/app会将本地app目录复制到容器内/code/code/app路径,和启动命令指定的app.main:app导入路径不匹配,直接触发routers模块找不到的报错。 - Docker Compose卷挂载路径错误
配置的- .:/app挂载路径和容器工作目录/code不一致:镜像构建时依赖是安装在/code目录下的,挂载后容器运行时的Python路径不包含依赖安装位置,直接触发sqlalchemy模块找不到的报错。 - 隐性代码bug:路由接口函数递归调用自身
get_roles接口内部再次调用get_roles(),就算模块问题修复,请求接口时也会触发递归深度超限的错误。 - 额外隐性配置问题:uvicorn未指定
--host 0.0.0.0,启动后宿主机无法访问8000端口;数据库连接如果写localhost会无法连通PostgreSQL容器,需要用Compose服务名db作为数据库地址。
修复方案
- 修正Dockerfile配置,优化构建缓存:
FROM python:3.10.5-slim-buster WORKDIR /code # 先复制依赖文件,利用Docker层缓存避免每次改代码都重装依赖 COPY ./requirements.txt /code/ RUN pip install --no-cache-dir -r requirements.txt # 复制项目代码到工作目录下的app文件夹 COPY ./app /code/app EXPOSE 8000 # 加--host 0.0.0.0允许外部访问 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--reload"]
- 修正docker-compose.yml配置,修正挂载路径、添加服务依赖、使用命名卷:
version: '3.9' services: web: build: . ports: - '8000:8000' volumes: # 挂载本地代码到容器工作目录,实现热重载 - .:/code # 等数据库启动后再启动web服务 depends_on: - db # 配置数据库连接地址,用服务名db连接PostgreSQL environment: - DATABASE_URL=postgresql://hamel:contrasena@db:5432/bankmel db: image: postgres restart: always environment: POSTGRES_USER: hamel POSTGRES_PASSWORD: contrasena POSTGRES_DB: bankmel volumes: # 用命名卷代替本地绝对路径,跨环境兼容性更好 - postgres_data:/var/lib/postgresql/data ports: - '5432:5432' volumes: postgres_data:
- 修正路由代码的递归调用bug:
@router.get('/api/v1/roles/', response_model=list[schemas.RoleBase]) async def get_roles(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): # 替换原递归调用,直接编写数据库查询逻辑 role_list = db.query(roles.Role).offset(skip).limit(limit).all() return role_list
- 清理旧缓存重新部署:
执行以下命令清理旧容器、缓存,重新构建启动:docker compose down -v docker compose build --no-cache docker compose up -d
内容的提问来源于stack exchange,提问作者cosmos multi
相关产品推荐
相关产品推荐

