Вопрос или проблема
Как запустить приложение FastAPI с использованием многослойной сборки и менеджера зависимостей pdm?
Я пытаюсь запустить приложение FastAPI, используя pdm и следующий Dockerfile
:
ARG PYTHON_BASE=3.10-slim
# Этап сборки
FROM python:$PYTHON_BASE as build
COPY pyproject.toml pdm.lock README.md ./
# Установка pdm
RUN python -m pip install --upgrade pip setuptools wheel &&\
pip install pdm
# Установка зависимостей
RUN pdm install --no-lock --no-editable
# Этап выполнения
FROM python:$PYTHON_BASE
# Копирование файлов приложения
COPY src /src
COPY logging_config.yaml .
COPY --from=build /.venv /.venv
ENV PATH="/.venv/bin:{$PATH}"
EXPOSE 5000
CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]
Структура моего проекта следующая:
backend
|- src/backend
| |- main.py
|- Dockerfile
|- pdm.lock
|- pyproject.toml
|- README.md
Я получаю следующую ошибку после выполнения команд для сборки образа и запуска контейнера:
sudo docker build -t backend:v0.1.0 .
sudo docker run -p 5000:5000 backend:v0.1.0
Ошибка:
exec /.venv/bin/uvicorn: нет такого файла или директории
Может кто-то объяснить, как правильно настроить контейнер?
Сначала я подумал, что контейнер не может найти путь к виртуальному окружению для uvicorn, поэтому я зашел в bash контейнера, что дало результат:
docker run -it backend:v0.1.0 /bin/bash
which uvicorn
>>> /.venv/bin/uvicorn
Мне удалось выявить проблему. Похоже, я инициализировал проект pdm с помощью Python 3.12, в то время как пытался развернуть приложение, используя Python 3.10. Это несоответствие привело к тому, что контейнер искал venv для 3.10, в то время как создавался 3.12. Поэтому я просто обновил базовый образ на python:3.12-slim
, и Dockerfile заработал как есть.
Ответ или решение
Для того чтобы правильно запустить приложение FastAPI на базе Docker с использованием pdm
в качестве менеджера зависимостей в многослойной сборке, необходимо учитывать несколько ключевых аспектов. Давайте разберем ваш сценарий и предложим правильное решение.
Проблема и Решение
Ваша проблема заключалась в несоответствии версий Python, используемых в проекте и в Docker-контейнере. Вы инициализировали проект с помощью Python 3.12, но попытались развернуть его с использованием Python 3.10. Это приводит к ошибкам, так как pdm
создает виртуальную среду для версии Python 3.12, однако Docker пытается запускать uvicorn
из виртуальной среды Python 3.10.
Исправленный Dockerfile
Измените ваш Dockerfile
, чтобы обеспечить согласованность версий Python:
ARG PYTHON_BASE=3.12-slim
# Студия сборки
FROM python:$PYTHON_BASE as build
COPY pyproject.toml pdm.lock README.md ./
# Установка pdm
RUN python -m pip install --upgrade pip setuptools wheel && \
pip install pdm
# Установка зависимостей
RUN pdm install --no-lock --no-editable
# Исполняемая стадия
FROM python:$PYTHON_BASE
# Копирование файлов приложения
COPY src /src
COPY logging_config.yaml .
COPY --from=build /.venv /.venv
ENV PATH="/.venv/bin:$PATH"
EXPOSE 5000
CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]
Объяснение
-
Версия Python: Изменена версия базового образа на
python:3.12-slim
для согласованности с версией Python, используемой вpdm
. -
Копирование и установка зависимостей: Сначала происходит установка зависимостей в стадии сборки, затем копируется виртуальная среда в окончательный образ.
-
Настройка PATH: Путь к виртуальной среде правильно добавлен в переменную окружения
PATH
, что обеспечит доступ к исполняемым файлам, установленным в виртуальной среде. - Запуск FastAPI: Используется команда
uvicorn
для запуска приложения, указывая корректный путь к вашему приложению FastAPI.
Построение и Запуск
Для сборки и запуска контейнера выполните команды:
sudo docker build -t backend:v0.1.0 .
sudo docker run -p 5000:5000 backend:v0.1.0
Заключение
С обновленной версией Python в Docker вы сможете избежать проблем, связанных с несовпадением версий. Следите за тем, чтобы версии локального окружения и окружения в Docker совпадали, чтобы избежать подобных ошибок в будущем. Если вы будете следовать этим шагам, ваше приложение FastAPI должно успешно запуститься в контейнере.