FastAPI + Docker: контейнеризация
Docker — это платформа для разработки, доставки и запуска приложений в контейнерах. Контейнеризация FastAPI приложений обеспечивает изоляцию зависимостей, упрощает развертывание и гарантирует консистентность среды выполнения между разработкой и продакшеном. В этой статье мы рассмотрим, как эффективно контейнеризировать FastAPI приложения с использованием Docker и docker-compose.
Контейнеризация позволяет “упаковать” приложение вместе со всеми его зависимостями, настройками и окружением в единый переносимый образ. Это избавляет от проблем “у меня работает, а у тебя нет” и делает процесс развертывания быстрым и предсказуемым.
1. Преимущества контейнеризации FastAPI
Контейнеризация FastAPI приложений предоставляет множество преимуществ:
- Изоляция зависимостей — каждое приложение работает в своей среде, что исключает конфликты версий библиотек. Например, если у вас несколько проектов с разными версиями FastAPI, они не будут мешать друг другу.
- Консистентность среды — одинаковое поведение в разработке, тестировании и продакшене. Вы уверены, что код, который работает у вас локально, будет работать и на сервере.
- Упрощение развертывания — один образ работает везде: локально, на сервере, в облаке. Не нужно вручную устанавливать зависимости на каждом сервере.
- Масштабируемость — легко запускать несколько экземпляров приложения для обработки большого количества запросов.
- Управление ресурсами — контроль над использованием CPU и памяти, что важно для стабильности и безопасности.
- Быстрое развертывание — образы можно быстро развертывать и обновлять, что ускоряет релизы и откаты.
В крупных компаниях контейнеризация — стандарт де-факто для всех современных микросервисов и API.
2. Базовая структура проекта
Перед тем как приступить к контейнеризации, важно правильно организовать структуру проекта. Это упростит поддержку и масштабирование приложения в будущем.
fastapi-docker-project/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ └── schemas.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
└── README.md
- app/ — директория с исходным кодом приложения.
- requirements.txt — список Python-зависимостей.
- Dockerfile — инструкция для сборки Docker-образа.
- docker-compose.yml — файл для управления несколькими контейнерами (например, приложение + база данных).
- .dockerignore — список файлов и папок, которые не попадут в образ (ускоряет сборку).
- README.md — документация по проекту.
Всегда отделяйте код приложения от инфраструктурных файлов (Dockerfile, docker-compose.yml и т.д.).
3. Создание FastAPI приложения
Давайте создадим минимальное FastAPI-приложение, чтобы увидеть, как оно будет работать в контейнере.
Создайте файл app/main.py:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn
app = FastAPI(
title="FastAPI Docker App",
description="Пример FastAPI приложения в Docker",
version="1.0.0"
)
class Item(BaseModel):
id: Optional[int] = None
name: str
description: Optional[str] = None
price: float
# Простое хранилище в памяти
items_db = []
item_id_counter = 1
@app.get("/")
def read_root():
return {"message": "Добро пожаловать в FastAPI Docker App!"}
@app.get("/health")
def health_check():
return {"status": "healthy", "service": "fastapi-docker"}
@app.get("/items/", response_model=List[Item])
def get_items():
return items_db
@app.get("/items/{item_id}", response_model=Item)
def get_item(item_id: int):
item = next((item for item in items_db if item["id"] == item_id), None)
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item
@app.post("/items/", response_model=Item)
def create_item(item: Item):
global item_id_counter
new_item = item.dict()
new_item["id"] = item_id_counter
items_db.append(new_item)
item_id_counter += 1
return new_item
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Пояснения к коду:
- Мы создаём экземпляр FastAPI и определяем простую модель данных
Itemс помощью Pydantic. - Для хранения данных используется обычный список
items_db— это временное решение для примера (в реальных проектах используют базы данных). - Реализованы базовые CRUD-эндпоинты: получить все товары, получить товар по id, добавить новый товар.
- Эндпоинт
/healthнужен для проверки работоспособности контейнера (например, в CI/CD или при мониторинге). - Приложение запускается через Uvicorn — это быстрый ASGI-сервер, рекомендованный для FastAPI.
Создайте файл requirements.txt с зависимостями:
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.5.0
Этот файл позволяет Docker быстро и предсказуемо устанавливать все необходимые библиотеки для вашего приложения.
4. Создание Dockerfile
Dockerfile — это инструкция для создания Docker образа. Создайте файл Dockerfile:
# Используем официальный Python образ как базовый
FROM python:3.11-slim
# Устанавливаем рабочую директорию в контейнере
WORKDIR /app
# Устанавливаем системные зависимости
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Копируем файл зависимостей
COPY requirements.txt .
# Устанавливаем Python зависимости
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код приложения
COPY ./app .
# Создаем непривилегированного пользователя
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser
# Открываем порт
EXPOSE 8000
# Команда для запуска приложения
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Объяснение ключевых моментов:
FROM python:3.11-slim— используем легковесный Python образWORKDIR /app— устанавливаем рабочую директориюCOPY requirements.txt .— копируем зависимости отдельно для кэширования слоевRUN pip install— устанавливаем зависимостиUSER appuser— запускаем приложение от непривилегированного пользователяEXPOSE 8000— указываем порт, который будет слушать приложение
5. Создание .dockerignore
Файл .dockerignore исключает ненужные файлы из контекста сборки:
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
.DS_Store
.env
.venv
venv/
ENV/
env.bak/
venv.bak/
.idea/
.vscode/
*.swp
*.swo
*~
6. Сборка и запуск образа
Теперь можно собрать и запустить Docker образ:
# Сборка образа
docker build -t fastapi-docker-app .
# Запуск контейнера
docker run -d -p 8000:8000 --name fastapi-container fastapi-docker-app
# Проверка работы
curl http://localhost:8000/
7. Использование docker-compose
Для более сложных приложений с базами данных и дополнительными сервисами используйте docker-compose. Создайте файл docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/fastapi_db
depends_on:
- db
volumes:
- ./app:/app
restart: unless-stopped
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=fastapi_db
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: unless-stopped
redis:
image: redis:7-alpine
ports:
- "6379:6379"
restart: unless-stopped
volumes:
postgres_data:
Команды для работы с docker-compose:
# Запуск всех сервисов
docker-compose up -d
# Просмотр логов
docker-compose logs -f web
# Остановка сервисов
docker-compose down
# Пересборка и запуск
docker-compose up --build -d
8. Оптимизация Docker образов
Многоэтапная сборка
Для продакшена используйте многоэтапную сборку для уменьшения размера образа:
# Этап сборки
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Этап продакшена
FROM python:3.11-slim
WORKDIR /app
# Копируем установленные пакеты из этапа сборки
COPY --from=builder /root/.local /root/.local
# Копируем код приложения
COPY ./app .
# Создаем пользователя
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
ENV PATH=/root/.local/bin:$PATH
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Использование .dockerignore
Правильно настроенный .dockerignore значительно ускоряет сборку:
# Исключаем ненужные файлы
.git
.gitignore
README.md
.env
.venv
__pycache__
*.pyc
.pytest_cache
.coverage
9. Переменные окружения
Создайте файл .env для конфигурации:
# База данных
DATABASE_URL=postgresql://user:password@db:5432/fastapi_db
# Redis
REDIS_URL=redis://redis:6379
# Настройки приложения
DEBUG=False
LOG_LEVEL=INFO
Обновите docker-compose.yml для использования переменных окружения:
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
env_file:
- .env
environment:
- DEBUG=False
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15
env_file:
- .env
environment:
- POSTGRES_USER=${POSTGRES_USER:-user}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
- POSTGRES_DB=${POSTGRES_DB:-fastapi_db}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
postgres_data:
10. Продакшен конфигурация
Для продакшена создайте отдельный Dockerfile.prod:
FROM python:3.11-slim
WORKDIR /app
# Устанавливаем системные зависимости
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Копируем зависимости
COPY requirements.txt .
# Устанавливаем Python пакеты
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код приложения
COPY ./app .
# Создаем пользователя
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser
# Настройки для продакшена
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
# Запуск с Gunicorn для продакшена
CMD ["gunicorn", "main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
Обновите requirements.txt для продакшена:
fastapi==0.104.1
uvicorn[standard]==0.24.0
gunicorn==21.2.0
pydantic==2.5.0
psycopg2-binary==2.9.9
redis==5.0.1
11. Мониторинг и логирование
Добавьте логирование в приложение:
import logging
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = FastAPI(
title="FastAPI Docker App",
description="Пример FastAPI приложения в Docker",
version="1.0.0"
)
@app.middleware("http")
async def log_requests(request, call_next):
logger.info(f"Request: {request.method} {request.url}")
response = await call_next(request)
logger.info(f"Response: {response.status_code}")
return response
# ... остальной код приложения
12. Безопасность
Обновленный Dockerfile с улучшенной безопасностью:
FROM python:3.11-slim
# Устанавливаем рабочую директорию
WORKDIR /app
# Обновляем систему и устанавливаем зависимости
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Создаем пользователя заранее
RUN adduser --disabled-password --gecos '' appuser
# Копируем зависимости
COPY requirements.txt .
# Устанавливаем Python пакеты
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код приложения
COPY ./app .
# Меняем владельца файлов
RUN chown -R appuser:appuser /app
# Переключаемся на непривилегированного пользователя
USER appuser
# Настройки безопасности
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
13. Развертывание в продакшене
Создайте скрипт развертывания deploy.sh:
#!/bin/bash
# Остановка старых контейнеров
docker-compose down
# Удаление старых образов
docker system prune -f
# Сборка новых образов
docker-compose build --no-cache
# Запуск сервисов
docker-compose up -d
# Проверка здоровья
sleep 10
curl -f http://localhost:8000/health || exit 1
echo "Развертывание завершено успешно!"
Настройте CI/CD pipeline в .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
script: |
cd /path/to/your/app
git pull origin main
chmod +x deploy.sh
./deploy.sh
14. Полезные команды Docker
# Просмотр запущенных контейнеров
docker ps
# Просмотр логов контейнера
docker logs fastapi-container
# Вход в контейнер
docker exec -it fastapi-container /bin/bash
# Просмотр использования ресурсов
docker stats
# Очистка неиспользуемых ресурсов
docker system prune -a
# Просмотр слоев образа
docker history fastapi-docker-app
15. Отладка и troubleshooting
Частые проблемы и решения:
-
Контейнер не запускается:
# Проверьте логи docker logs container_name # Проверьте порты docker port container_name -
Проблемы с правами доступа:
# В Dockerfile добавьте RUN chown -R appuser:appuser /app USER appuser -
Медленная сборка:
# Оптимизируйте порядок слоев COPY requirements.txt . RUN pip install -r requirements.txt COPY . .
Заключение
Контейнеризация FastAPI приложений с Docker предоставляет мощные инструменты для разработки, тестирования и развертывания. Правильно настроенная контейнеризация обеспечивает:
- Изоляцию — каждое приложение работает в своей среде
- Консистентность — одинаковое поведение во всех средах
- Масштабируемость — легко запускать несколько экземпляров
- Упрощение развертывания — один образ работает везде
- Безопасность — изоляция процессов и ресурсов
Используя Docker и docker-compose, вы можете быстро создавать сложные микросервисные архитектуры и эффективно управлять их жизненным циклом. Это особенно важно для современных веб-приложений, где требуется высокая надежность и простота развертывания.