使用 Flask、gunicorn 和 Docker 处理相关导入

Handling relative imports with Flask, gunicorn, and Docker

有人问过类似的问题,但我从未找到解决此问题的有效方法。

我有一个非常基本的 Docker 和 Flask 设置:

目录结构:

├── Dockerfile
├── app
│   ├── __init__.py
│   └── constants.py
├── docker-compose.yml
└── requirements.txt

init.py

的内容
from flask import Flask

from .constants import MYCONSTANT


def create_app():
    """Create the app."""
    app = Flask(__name__)
    @app.route('/', methods=['GET'])
    def index():
        return 'hello world' 

    return app

Docker文件

FROM python:3.6-alpine
COPY requirements.txt /
RUN pip install Flask gunicorn
COPY app/ /app
WORKDIR /app
EXPOSE 8000
CMD gunicorn --bind=0.0.0.0:8000 "app.__init__.create_app()"

docker-compose.yml

docker-compose:

version: '3'
services:
  web:
    build: .
    ports: ['5000:5000']
    volumes: ['./app:/app']

当我运行docker-compose up --build时,应用程序抱怨找不到模块'app':

web_1 | ModuleNotFoundError: No module named 'app'

由于测试套件和其他一些因素,我无法更改为 from constants import MYCONSTANT,所以我不太确定如何解决这个问题。 我尝试将 gunicorn 行更改为 CMD gunicorn --bind=0.0.0.0:8000 "__init__:create_app()" 但随后出现此导入错误:

web_1  |     from .constants import MYCONSTANT
web_1  | ImportError: attempted relative import with no known parent package

你的问题的核心是你丢失了一层目录。您是 /app 目录中的 运行 GUnicorn,并试图导入 Python app 模块,但是没有 ./app/__init__.py 相对于您所在的目录从.

您的第一步应该是从 docker-compose.yml 文件中删除 volumes: 块。特别是如果您遇到 directory-layout 问题,在整个应用程序代码上安装一些东西会使调试问题变得困难,并且您会得到不同的结果,具体取决于当前系统检查出的内容。应用程序代码内置于映像中,因此无需单独注入。

在 Dockerfile 中,我会避​​免将文件复制到容器根目录中。重要的是确保将 app 子目录从构建上下文复制到当前目录的 app 子目录中。

FROM python:3.6-alpine

# Switch out of the root directory before copying anything in.
# (This directory is frequently named /app; picking something
# different for clarity.)
WORKDIR /web

# Install Python dependencies.  (Keep this as a separate early step
# so repeated `docker build` goes faster.)
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy in the main application source.
COPY app/ ./app/ # <-- *** note, ./app relative to WORKDIR

# Set metadata for how to run the container.
EXPOSE 8000
CMD gunicorn --bind=0.0.0.0:8000 "app.__init__.create_app()"
version: '3.8'
services:
  web:
    build: .
    ports: ['5000:5000']
    # no volumes: required