Movatterモバイル変換


[0]ホーム

URL:


Перейти к содержанию
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

FastAPI в контейнерах — Docker

🌐 Перевод выполнен с помощью ИИ и людей

Этот перевод был сделан ИИ под руководством людей. 🤝

В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖

Вы можете улучшить этот перевод,помогая нам лучше направлять ИИ LLM.

Английская версия

При развёртывании приложений FastAPI распространённый подход — собиратьобраз контейнера на Linux. Обычно это делают с помощьюDocker. Затем такой образ контейнера можно развернуть несколькими способами.

Использование Linux-контейнеров даёт ряд преимуществ:безопасность,воспроизводимость,простоту и другие.

Подсказка

Нет времени и вы уже знакомы с этим? Перейдите кDockerfile ниже 👇.

Предпросмотр Dockerfile 👀
FROMpython:3.14WORKDIR/codeCOPY./requirements.txt/code/requirements.txtRUNpipinstall--no-cache-dir--upgrade-r/code/requirements.txtCOPY./app/code/appCMD["fastapi","run","app/main.py","--port","80"]# Если запускаете за прокси, например Nginx или Traefik, добавьте --proxy-headers# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]

Что такое контейнер

Контейнеры (в основном Linux-контейнеры) — это оченьлегковесный способ упаковать приложения вместе со всеми их зависимостями и необходимыми файлами, изолировав их от других контейнеров (других приложений или компонентов) в той же системе.

Linux-контейнеры запускаются, используя то же ядро Linux хоста (машины, виртуальной машины, облачного сервера и т.п.). Это означает, что они очень легковесные (по сравнению с полноценными виртуальными машинами, эмулирующими целую операционную систему).

Таким образом, контейнеры потребляютмалое количество ресурсов, сопоставимое с запуском процессов напрямую (виртуальная машина потребовала бы намного больше ресурсов).

У контейнеров также есть собственныеизолированные выполняемые процессы (обычно всего один процесс), файловая система и сеть, что упрощает развёртывание, безопасность, разработку и т.д.

Что такое образ контейнера

Контейнер запускается изобраза контейнера.

Образ контейнера — этостатическая версия всех файлов, переменных окружения и команды/программы по умолчанию, которые должны присутствовать в контейнере. Здесьстатическая означает, чтообраз не запущен, он не выполняется — это только упакованные файлы и метаданные.

В противоположность «образу контейнера» (хранящему статическое содержимое), «контейнер» обычно означает запущенный экземпляр, то, чтовыполняется.

Когдаконтейнер запущен (на основеобраза контейнера), он может создавать или изменять файлы, переменные окружения и т.д.. Эти изменения существуют только внутри контейнера и не сохраняются в исходном образе контейнера (не записываются на диск).

Образ контейнера можно сравнить сфайлами программы, напримерpython и каким-то файломmain.py.

А самконтейнер (в отличие отобраза контейнера) — это фактически запущенный экземпляр образа, сопоставимый спроцессом. По сути, контейнер работает только тогда, когда в нём естьзапущенный процесс (и обычно это один процесс). Контейнер останавливается, когда в нём не остаётся запущенных процессов.

Образы контейнеров

Docker — один из основных инструментов для создания и управленияобразами контейнеров иконтейнерами.

Существует публичныйDocker Hub с готовымиофициальными образами для многих инструментов, окружений, баз данных и приложений.

Например, есть официальныйобраз Python.

А также множество образов для разных вещей, например баз данных:

Используя готовые образы, очень легкокомбинировать разные инструменты и использовать их. Например, чтобы попробовать новую базу данных. В большинстве случаев можно воспользоватьсяофициальными образами и просто настроить их через переменные окружения.

Таким образом, во многих случаях вы можете изучить контейнеры и Docker и переиспользовать эти знания с множеством различных инструментов и компонентов.

Например, вы можете запуститьнесколько контейнеров: с базой данных, Python-приложением, веб-сервером с фронтендом на React и связать их через внутреннюю сеть.

Все системы управления контейнерами (такие как Docker или Kubernetes) имеют интегрированные возможности для такого сетевого взаимодействия.

Контейнеры и процессы

Образ контейнера обычно включает в свои метаданные программу или команду по умолчанию, которую следует запускать при стартеконтейнера, а также параметры, передаваемые этой программе. Это очень похоже на запуск команды в терминале.

Когдаконтейнер стартует, он выполняет указанную команду/программу (хотя вы можете переопределить это и запустить другую команду/программу).

Контейнер работает до тех пор, пока работает егоглавный процесс (команда или программа).

Обычно в контейнере естьодин процесс, но главный процесс может запускать подпроцессы, и тогда в том же контейнере будетнесколько процессов.

Нельзя иметь работающий контейнер безхотя бы одного запущенного процесса. Если главный процесс останавливается, контейнер останавливается.

Создать Docker-образ для FastAPI

Итак, давайте что-нибудь соберём! 🚀

Я покажу, как собратьDocker-образ для FastAPIс нуля на основеофициального образа Python.

Именно так стоит делать вбольшинстве случаев, например:

  • При использованииKubernetes или похожих инструментов
  • При запуске наRaspberry Pi
  • При использовании облачного сервиса, который запускает для вас образ контейнера и т.п.

Зависимости пакетов

Обычнозависимости вашего приложения описаны в каком-то файле.

Конкретный формат зависит в основном от инструмента, которым выустанавливаете эти зависимости.

Чаще всего используется файлrequirements.txt с именами пакетов и их версиями по одному на строку.

Разумеется, вы будете придерживаться тех же идей, что описаны здесь:О версиях FastAPI, чтобы задать диапазоны версий.

Например, вашrequirements.txt может выглядеть так:

fastapi[standard]>=0.113.0,<0.114.0pydantic>=2.7.0,<3.0.0

И обычно вы установите эти зависимости командойpip, например:

$pipinstall-rrequirements.txt---> 100%Successfully installed fastapi pydantic

Информация

Существуют и другие форматы и инструменты для описания и установки зависимостей.

Создать кодFastAPI

  • Создайте директориюapp и перейдите в неё.
  • Создайте пустой файл__init__.py.
  • Создайте файлmain.py со следующим содержимым:
fromfastapiimportFastAPIapp=FastAPI()@app.get("/")defread_root():return{"Hello":"World"}@app.get("/items/{item_id}")defread_item(item_id:int,q:str|None=None):return{"item_id":item_id,"q":q}

Dockerfile

Теперь в той же директории проекта создайте файлDockerfile:

# (1)!FROMpython:3.14# (2)!WORKDIR/code# (3)!COPY./requirements.txt/code/requirements.txt# (4)!RUNpipinstall--no-cache-dir--upgrade-r/code/requirements.txt# (5)!COPY./app/code/app# (6)!CMD["fastapi","run","app/main.py","--port","80"]
  1. Начинаем с официального базового образа Python.

  2. Устанавливаем текущую рабочую директорию в/code.

    Здесь мы разместим файлrequirements.txt и директориюapp.

  3. Копируем файл с зависимостями в директорию/code.

    Сначала копируйтетолько файл с зависимостями, не остальной код.

    Так как этот файлменяется нечасто, Docker определит это и используеткэш на этом шаге, что позволит использовать кэш и на следующем шаге.

  4. Устанавливаем зависимости из файла с требованиями.

    Опция--no-cache-dir указываетpip не сохранять загруженные пакеты локально, т.к. это нужно только еслиpip будет запускаться снова для установки тех же пакетов, а при работе с контейнерами это обычно не требуется.

    Заметка

    --no-cache-dir относится только кpip и не имеет отношения к Docker или контейнерам.

    Опция--upgrade указываетpip обновлять пакеты, если они уже установлены.

    Поскольку предыдущий шаг с копированием файла может быть обработанкэшем Docker, этот шаг такжеиспользует кэш Docker, когда это возможно.

    Использование кэша на этом шагесэкономит вам многовремени при повторных сборках образа во время разработки, вместо того чтобызагружать и устанавливать все зависимостикаждый раз.

  5. Копируем директорию./app внутрь директории/code.

    Так как здесь весь код, которыйменяется чаще всего, кэш Dockerвряд ли будет использоваться для этого шага илипоследующих шагов.

    Поэтому важно разместить этот шагближе к концуDockerfile, чтобы оптимизировать время сборки образа контейнера.

  6. Указываемкоманду для запускаfastapi run, под капотом используется Uvicorn.

    CMD принимает список строк, каждая из которых — это то, что вы бы ввели в командной строке, разделяя пробелами.

    Эта команда будет выполнена изтекущей рабочей директории, той самой/code, которую вы задали вышеWORKDIR /code.

Подсказка

Посмотрите, что делает каждая строка, кликнув по номеру рядом со строкой. 👆

Предупреждение

Всегда используйтеexec-форму инструкцииCMD, как описано ниже.

ИспользуйтеCMD — exec-форма

Инструкцию DockerCMD можно писать в двух формах:

Exec-форма:

# ✅ Делайте такCMD["fastapi","run","app/main.py","--port","80"]

⛔️Shell-форма:

# ⛔️ Не делайте такCMDfastapirunapp/main.py--port80

Обязательно используйтеexec-форму, чтобы FastAPI мог корректно завершаться и чтобы срабатывалисобытия lifespan.

Подробнее об этом читайте вдокументации Docker о shell- и exec-формах.

Это особенно заметно при использованииdocker compose. См. раздел FAQ Docker Compose с техническими подробностями:Почему мои сервисы пересоздаются или останавливаются 10 секунд?.

Структура директорий

Теперь у вас должна быть такая структура:

.├── app│   ├── __init__.py│   └── main.py├── Dockerfile└── requirements.txt

За прокси-сервером TSL-терминации

Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию--proxy-headers. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.

CMD["fastapi","run","app/main.py","--proxy-headers","--port","80"]

Кэш Docker

В этомDockerfile есть важная хитрость: мы сначала копируемтолько файл с зависимостями, а не весь код. Вот зачем.

COPY./requirements.txt/code/requirements.txt

Docker и подобные инструментыстроят образы контейнеровинкрементально, добавляяслой за слоем, начиная с первой строкиDockerfile и добавляя любые файлы, создаваемые каждой инструкциейDockerfile.

Docker и подобные инструменты также используютвнутренний кэш при сборке образа: если файл не изменился с момента предыдущей сборки, будетпереиспользован слой, созданный в прошлый раз, вместо повторного копирования файла и создания нового слоя с нуля.

Само по себе избегание копирования всех файлов не всегда даёт много, но благодаря использованию кэша на этом шаге Docker сможетиспользовать кэш и на следующем шаге. Например, на шаге установки зависимостей:

RUNpipinstall--no-cache-dir--upgrade-r/code/requirements.txt

Файл с зависимостямименяется нечасто. Поэтому, копируя только его, Docker сможетиспользовать кэш для этого шага.

А затем Docker сможетиспользовать кэш и на следующем шаге, где скачиваются и устанавливаются зависимости. Здесь мы как разэкономим много времени. ✨ ...и не скучаем в ожидании. 😪😆

Скачивание и установка зависимостейможет занять минуты, но использованиекэшасекунды.

Поскольку во время разработки вы будете пересобирать образ снова и снова, чтобы проверить изменения в коде, суммарно это сэкономит немало времени.

Затем, ближе к концуDockerfile, мы копируем весь код. Так как онменяется чаще всего, мы ставим этот шаг в конец, потому что почти всегда всё, что после него, уже не сможет использовать кэш.

COPY./app/code/app

Собрать Docker-образ

Теперь, когда все файлы на месте, соберём образ контейнера.

  • Перейдите в директорию проекта (где вашDockerfile и директорияapp).
  • Соберите образ FastAPI:
$dockerbuild-tmyimage.---> 100%

Подсказка

Обратите внимание на точку. в конце — это то же самое, что./. Так мы указываем Docker, из какой директории собирать образ контейнера.

В данном случае это текущая директория (.).

Запустить Docker-контейнер

  • Запустите контейнер на основе вашего образа:
$dockerrun-d--namemycontainer-p80:80myimage

Проверка

Проверьте работу по адресу вашего Docker-хоста, например:http://192.168.99.100/items/5?q=somequery илиhttp://127.0.0.1/items/5?q=somequery (или аналогичный URL вашего Docker-хоста).

Вы увидите что-то вроде:

{"item_id":5,"q":"somequery"}

Интерактивная документация API

Теперь зайдите наhttp://192.168.99.100/docs илиhttp://127.0.0.1/docs (или аналогичный URL вашего Docker-хоста).

Вы увидите автоматическую интерактивную документацию API (на базеSwagger UI):

Swagger UI

Альтернативная документация API

Также можно открытьhttp://192.168.99.100/redoc илиhttp://127.0.0.1/redoc (или аналогичный URL вашего Docker-хоста).

Вы увидите альтернативную автоматическую документацию (на базеReDoc):

ReDoc

Собрать Docker-образ для однофайлового FastAPI

Если ваше приложение FastAPI — один файл, напримерmain.py без директории./app, структура файлов может быть такой:

.├── Dockerfile├── main.py└── requirements.txt

Тогда вDockerfile нужно изменить пути копирования:

FROMpython:3.14WORKDIR/codeCOPY./requirements.txt/code/requirements.txtRUNpipinstall--no-cache-dir--upgrade-r/code/requirements.txt# (1)!COPY./main.py/code/# (2)!CMD["fastapi","run","main.py","--port","80"]
  1. Копируем файлmain.py напрямую в/code (без директории./app).

  2. Используемfastapi run для запуска приложения из одного файлаmain.py.

Когда вы передаёте файл вfastapi run, он автоматически определит, что это одиночный файл, а не часть пакета, и поймёт, как его импортировать и запустить ваше FastAPI-приложение. 😎

Концепции развертывания

Ещё раз рассмотримконцепции развертывания применительно к контейнерам.

Контейнеры главным образом упрощаютсборку и развёртывание приложения, но не навязывают конкретный подход к этимконцепциям развертывания, и существует несколько стратегий.

Хорошая новость в том, что при любой стратегии есть способ охватить все концепции развертывания. 🎉

Рассмотрим этиконцепции развертывания в терминах контейнеров:

  • HTTPS
  • Запуск при старте
  • Перезапуски
  • Репликация (количество запущенных процессов)
  • Память
  • Предварительные шаги перед запуском

HTTPS

Если мы рассматриваем толькообраз контейнера для приложения FastAPI (и далее запущенныйконтейнер), то HTTPS обычно обрабатываетсявнешним инструментом.

Это может быть другой контейнер, например сTraefik, который берёт на себяHTTPS иавтоматическое получениесертификатов.

Подсказка

У Traefik есть интеграции с Docker, Kubernetes и другими, поэтому очень легко настроить и сконфигурировать HTTPS для ваших контейнеров.

В качестве альтернативы HTTPS может быть реализован как сервис облачного провайдера (при этом приложение всё равно работает в контейнере).

Запуск при старте и перезапуски

Обычно есть другой инструмент, отвечающий зазапуск и работу вашего контейнера.

Это может быть самDocker,Docker Compose,Kubernetes,облачный сервис и т.п.

В большинстве (или во всех) случаев есть простая опция, чтобы включить запуск контейнера при старте системы и перезапуски при сбоях. Например, в Docker это опция командной строки--restart.

Без контейнеров обеспечить запуск при старте и перезапуски может быть сложно. Но приработе с контейнерами в большинстве случаев этот функционал доступен по умолчанию. ✨

Репликация — количество процессов

Если у вас естькластер машин сKubernetes, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будетеуправлять репликацией науровне кластера, а не использоватьменеджер процессов (например, Uvicorn с воркерами) в каждом контейнере.

Одна из таких систем управления распределёнными контейнерами, как Kubernetes, обычно имеет встроенный способ управлятьрепликацией контейнеров, поддерживаябалансировку нагрузки для входящих запросов — всё это науровне кластера.

В таких случаях вы, скорее всего, захотите собратьDocker-образ с нуля, какописано выше, установить зависимости и запускатьодин процесс Uvicorn вместо множества воркеров Uvicorn.

Балансировщик нагрузки

При использовании контейнеров обычно есть компонент,слушающий главный порт. Это может быть другой контейнер —прокси-сервер TSL-терминации для обработкиHTTPS или похожий инструмент.

Поскольку этот компонент принимаетнагрузку запросов и распределяет её между воркерамисбалансированно, его часто называютбалансировщиком нагрузки.

Подсказка

Тот же компонентпрокси-сервер TSL-терминации, который обрабатывает HTTPS, скорее всего также будетбалансировщиком нагрузки.

При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачисетевого взаимодействия (например, HTTP-запросов) отбалансировщика нагрузки (который также может бытьпрокси-сервером TSL-терминации) к контейнеру(-ам) с вашим приложением.

Один балансировщик — несколько контейнеров-воркеров

При работе сKubernetes или похожими системами управления распределёнными контейнерами их внутренние механизмы сети позволяют одномубалансировщику нагрузки, слушающему главныйпорт, передавать запросы внесколько контейнеров, где запущено ваше приложение.

Каждый такой контейнер с вашим приложением обычно имееттолько один процесс (например, процесс Uvicorn с вашим приложением FastAPI). Все они —одинаковые контейнеры, запускающие одно и то же, но у каждого свой процесс, память и т.п. Так вы используетепараллелизм поразным ядрам CPU или дажеразным машинам.

Система распределённых контейнеров сбалансировщиком нагрузки будетраспределять запросы между контейнерами с вашим приложениемпо очереди. То есть каждый запрос может обрабатываться одним из несколькихреплицированных контейнеров.

Обычно такойбалансировщик нагрузки может также обрабатывать запросы кдругим приложениям в вашем кластере (например, к другому домену или под другим префиксом пути URL) и направлять их к нужным контейнерам этогодругого приложения.

Один процесс на контейнер

В таком сценарии, скорее всего, вы захотите иметьодин (Uvicorn) процесс на контейнер, так как репликация уже управляется на уровне кластера.

Поэтому в контейнерене нужно поднимать несколько воркеров, например через опцию командной строки--workers. Нуженодин процесс Uvicorn на контейнер (но, возможно, несколько контейнеров).

Наличие отдельного менеджера процессов внутри контейнера (как при нескольких воркерах) только добавитлишнюю сложность, которую, вероятно, уже берёт на себя ваша кластерная система.

Контейнеры с несколькими процессами и особые случаи

Конечно, естьособые случаи, когда может понадобитьсяконтейнер с несколькимиворкерами Uvicorn внутри.

В таких случаях вы можете использовать опцию командной строки--workers, чтобы указать нужное количество воркеров:

FROMpython:3.14WORKDIR/codeCOPY./requirements.txt/code/requirements.txtRUNpipinstall--no-cache-dir--upgrade-r/code/requirements.txtCOPY./app/code/app# (1)!CMD["fastapi","run","app/main.py","--port","80","--workers","4"]
  1. Здесь мы используем опцию--workers, чтобы установить число воркеров равным 4.

Примеры, когда это может быть уместно:

Простое приложение

Вам может понадобиться менеджер процессов в контейнере, если приложениедостаточно простое, чтобы запускаться наодном сервере, а не в кластере.

Docker Compose

Вы можете развёртывать наодном сервере (не кластере) сDocker Compose, и у вас не будет простого способа управлять репликацией контейнеров (в Docker Compose), сохраняя общую сеть ибалансировку нагрузки.

Тогда вы можете захотетьодин контейнер сменеджером процессов, который запускаетнесколько воркеров внутри.


Главное —ни одно из этих правил не являетсястрого обязательным. Используйте эти идеи, чтобыоценить свой конкретный случай и решить, какой подход лучше для вашей системы, учитывая:

  • Безопасность — HTTPS
  • Запуск при старте
  • Перезапуски
  • Репликацию (количество запущенных процессов)
  • Память
  • Предварительные шаги перед запуском

Память

Если вы запускаетеодин процесс на контейнер, у каждого контейнера будет более-менее чётко определённый, стабильный и ограниченный объём потребляемой памяти (контейнеров может быть несколько при репликации).

Затем вы можете задать такие же лимиты и требования по памяти в конфигурации вашей системы управления контейнерами (например, вKubernetes). Так система сможетреплицировать контейнеры надоступных машинах, учитывая объём необходимой памяти и доступной памяти в машинах кластера.

Если приложениепростое, это, вероятно,не будет проблемой, и жёсткие лимиты памяти можно не указывать. Но если выиспользуете много памяти (например, с моделямиМашинного обучения), проверьте, сколько памяти потребляется, и отрегулируйтечисло контейнеров накаждой машине (и, возможно, добавьте машины в кластер).

Если вы запускаетенесколько процессов в контейнере, нужно убедиться, что их суммарное потреблениене превысит доступную память.

Предварительные шаги перед запуском и контейнеры

Если вы используете контейнеры (например, Docker, Kubernetes), есть два основных подхода.

Несколько контейнеров

Если у васнесколько контейнеров, и, вероятно, каждый запускаетодин процесс (например, в кластереKubernetes), то вы, скорее всего, захотите иметьотдельный контейнер, выполняющийпредварительные шаги в одном контейнере и одном процесседо запуска реплицированных контейнеров-воркеров.

Информация

Если вы используете Kubernetes, это, вероятно, будетInit Container.

Если в вашем случае нет проблемы с тем, чтобы выполнять эти предварительные шагимногократно и параллельно (например, вы не запускаете миграции БД, а только проверяете готовность БД), вы можете просто выполнить их в каждом контейнере прямо перед стартом основного процесса.

Один контейнер

Если у вас простая схема содним контейнером, который затем запускает нескольковоркеров (или один процесс), можно выполнить подготовительные шаги в этом же контейнере непосредственно перед запуском процесса с приложением.

Базовый Docker-образ

Ранее существовал официальный Docker-образ FastAPI:tiangolo/uvicorn-gunicorn-fastapi. Сейчас он помечен как устаревший. ⛔️

Скорее всего, вамне стоит использовать этот базовый образ (или какой-либо аналогичный).

Если вы используетеKubernetes (или другое) и уже настраиваетерепликацию на уровне кластера через несколькоконтейнеров, в этих случаях лучшесобрать образ с нуля, как описано выше:Создать Docker-образ для FastAPI.

А если вам нужны несколько воркеров, просто используйте опцию командной строки--workers.

Технические подробности

Этот Docker-образ был создан в то время, когда Uvicorn не умел управлять и перезапускать «упавших» воркеров, и приходилось использовать Gunicorn вместе с Uvicorn, что добавляло заметную сложность, лишь бы Gunicorn управлял и перезапускал воркеров Uvicorn.

Но теперь, когда Uvicorn (и командаfastapi) поддерживают--workers, нет причин использовать базовый Docker-образ вместо сборки своего (кода получается примерно столько же 😅).

Развёртывание образа контейнера

После того как у вас есть образ контейнера (Docker), его можно развёртывать несколькими способами.

Например:

  • СDocker Compose на одном сервере
  • В кластереKubernetes
  • В кластере Docker Swarm Mode
  • С другим инструментом, например Nomad
  • С облачным сервисом, который принимает ваш образ контейнера и разворачивает его

Docker-образ сuv

Если вы используетеuv для установки и управления проектом, следуйте ихруководству по Docker для uv.

Резюме

Используя системы контейнеризации (например,Docker иKubernetes), довольно просто закрыть всеконцепции развертывания:

  • HTTPS
  • Запуск при старте
  • Перезапуски
  • Репликация (количество запущенных процессов)
  • Память
  • Предварительные шаги перед запуском

В большинстве случаев вы, вероятно, не захотите использовать какой-либо базовый образ, а вместо этогособерёте образ контейнера с нуля на основе официального Docker-образа Python.

Заботясь опорядке инструкций вDockerfileи используякэш Docker, вы можетеминимизировать время сборки, чтобы повысить продуктивность (и не скучать). 😎


[8]ページ先頭

©2009-2026 Movatter.jp