使用 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
有人问过类似的问题,但我从未找到解决此问题的有效方法。
我有一个非常基本的 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