Представь, что ты написал крутое FastAPI приложение — оно быстрое, типизированное и покрыто тестами. Но теперь возникает главный вопрос: как вывести его в продакшен так, чтобы оно работало стабильно, безопасно и выдерживало нагрузку? Давай разберем весь путь fastapi deploy от локальной машины до облачного сервера.
Подготовка приложения к деплою
Перед тем как начать развертывание FastAPI, нужно подготовить само приложение. Вот что обязательно должно быть:
requirements.txtилиpyproject.tomlс зафиксированными версиями зависимостей- Отдельные конфиги для разработки и продакшена
- Логирование, настроенное для продакшена
- Корректная обработка ошибок
- Проверенные миграции базы данных (если используются)
Структура проекта для деплоя
my_fastapi_app/
├── app/
│ ├── __init__.py
│ ├── main.py # Точка входа FastAPI
│ ├── api/
│ ├── core/
│ │ ├── config.py # Конфигурация
│ │ └── logger.py # Логирование
│ └── db/
├── requirements.txt
├── Dockerfile # Для контейнеризации
├── docker-compose.yml # Для оркестрации
├── nginx/
│ └── nginx.conf # Конфиг обратного прокси
└── scripts/
└── start.sh # Скрипт запуска
Контейнеризация с Docker
Docker — стандарт де-факто для деплоя Python приложений. Он гарантирует, что приложение будет работать одинаково в любой среде.
Оптимизированный Dockerfile для FastAPI
# Базовый образ Python
FROM python:3.11-slim as builder
# Устанавливаем зависимости системы
RUN apt-get update && apt-get install -y \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Рабочая директория
WORKDIR /app
# Копируем зависимости
COPY requirements.txt .
# Создаем виртуальное окружение и устанавливаем зависимости
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Финальный образ
FROM python:3.11-slim
# Копируем виртуальное окружение из builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Создаем непривилегированного пользователя
RUN useradd -m -u 1000 fastapi-user
USER fastapi-user
# Рабочая директория
WORKDIR /app
# Копируем код приложения
COPY --chown=fastapi-user:fastapi-user ./app ./app
# Порт приложения
EXPOSE 8000
# Команда запуска
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Продакшен-сервер с Gunicorn + Uvicorn
В разработке мы используем uvicorn напрямую, но для продакшена нужен Gunicorn как менеджер процессов. Он обеспечивает:
- Запуск нескольких воркеров для обработки параллельных запросов
- Автоматический перезапуск упавших воркеров
- Балансировку нагрузки между воркерами
- Graceful shutdown при получении сигналов
Конфигурация Gunicorn для FastAPI
# gunicorn_conf.py
import multiprocessing
import os
# Количество воркеров
workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
host = os.getenv("HOST", "0.0.0.0")
port = os.getenv("PORT", "8000")
bind_env = os.getenv("BIND", None)
if bind_env:
use_bind = bind_env
else:
use_bind = f"{host}:{port}"
# Расчет количества воркеров
cores = multiprocessing.cpu_count()
workers_per_core = float(workers_per_core_str)
default_web_concurrency = workers_per_core * cores + 1
if web_concurrency_str:
web_concurrency = int(web_concurrency_str)
assert web_concurrency > 0
else:
web_concurrency = int(default_web_concurrency)
# Gunicorn конфиг
worker_class = "uvicorn.workers.UvicornWorker"
workers = web_concurrency
bind = use_bind
keepalive = 120
errorlog = "-"
loglevel = "info"
accesslog = "-"
graceful_timeout = 120
timeout = 120
worker_tmp_dir = "/dev/shm"
Обновленный CMD в Dockerfile
# Заменяем последнюю строку в Dockerfile:
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "gunicorn_conf.py", "app.main:app"]
Настройка Nginx как обратного прокси
Nginx выполняет критически важные функции в нашем fastapi деплое:
- Принимает входящие HTTP/HTTPS запросы
- Раздает статические файлы (гораздо эффективнее, чем через Python)
- Обеспечивает сжатие gzip
- Защищает от некоторых типов DDoS атак
- Балансирует нагрузку между несколькими инстансами приложения
Конфигурация Nginx для FastAPI
# nginx/nginx.conf
upstream fastapi_app {
server app:8000; # Docker service name
keepalive 32;
}
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Логи
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Безопасные заголовки
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Размер загружаемых файлов
client_max_body_size 10M;
# Статические файлы
location /static/ {
alias /app/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Проксирование к FastAPI
location / {
proxy_pass http://fastapi_app;
# Прокси заголовки
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Таймауты
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Полный docker-compose для продакшена
# docker-compose.prod.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: fastapi_app
restart: unless-stopped
env_file:
- .env.production
volumes:
- ./app:/app/app:ro
- static_volume:/app/static
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
- postgres
nginx:
image: nginx:alpine
container_name: nginx_proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
- static_volume:/app/static
networks:
- backend
depends_on:
- app
postgres:
image: postgres:15-alpine
container_name: postgres_db
restart: unless-stopped
env_file:
- .env.production
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
networks:
backend:
driver: bridge
volumes:
postgres_data:
static_volume:
Настройка SSL/TLS с Let's Encrypt
HTTPS — обязательное требование для любого продакшен приложения. Используем Certbot для автоматического получения сертификатов.
Скрипт настройки SSL
#!/bin/bash
# scripts/setup_ssl.sh
domain="yourdomain.com"
email="admin@yourdomain.com"
# Создаем директории для Certbot
mkdir -p certbot/conf certbot/www
# Получаем сертификат
docker run -it --rm \
-v "$(pwd)/certbot/conf:/etc/letsencrypt" \
-v "$(pwd)/certbot/www:/var/www/certbot" \
certbot/certbot certonly \
--webroot --webroot-path /var/www/certbot \
--agree-tos \
--no-eff-email \
--email "${email}" \
-d "${domain}" \
-d "www.${domain}"
# Обновляем nginx конфиг для HTTPS
cat > nginx/nginx_ssl.conf << EOF
server {
listen 80;
server_name ${domain} www.${domain};
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${domain} www.${domain};
ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;
# Остальная конфигурация как в примере выше...
}
EOF
# Скрипт для автообновления сертификатов
echo "0 0 1 * * docker run --rm -v $(pwd)/certbot/conf:/etc/letsencrypt -v $(pwd)/certbot/www:/var/www/certbot certbot/certbot renew >> /var/log/certbot.log 2>&1" | crontab -
Мониторинг и логирование
После развертывания FastAPI приложения нужно настроить мониторинг его работы.
| Компонент | Инструмент | Что мониторит |
|---|---|---|
| Логи приложения | ELK Stack / Loki | Ошибки, запросы, время выполнения |
| Метрики | Prometheus + Grafana | CPU, память, кол-во запросов, latency |
| Трассировка | Jaeger / OpenTelemetry | Распределенная трассировка запросов |
| Доступность | UptimeRobot / Pingdom | Uptime, response time |
Добавляем метрики Prometheus
# app/core/metrics.py
from prometheus_client import Counter, Histogram, generate_latest
from fastapi import Response
from fastapi.routing import APIRoute
import time
REQUEST_COUNT = Counter(
'http_requests_total',
'Total HTTP Requests',
['method', 'endpoint', 'http_status']
)
REQUEST_LATENCY = Histogram(
'http_request_duration_seconds',
'HTTP request latency in seconds',
['method', 'endpoint']
)
class PrometheusMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope['type'] != 'http':
return await self.app(scope, receive, send)
method = scope['method']
path = scope['path']
start_time = time.time()
async def send_wrapper(message):
if message['type'] == 'http.response.start':
status_code = message['status']
REQUEST_COUNT.labels(
method=method,
endpoint=path,
http_status=status_code
).inc()
REQUEST_LATENCY.labels(
method=method,
endpoint=path
).observe(time.time() - start_time)
await send(message)
await self.app(scope, receive, send_wrapper)
# В main.py
from app.core.metrics import PrometheusMiddleware, generate_latest
app = FastAPI()
app.add_middleware(PrometheusMiddleware)
@app.get("/metrics")
async def metrics():
return Response(generate_latest())
CI/CD пайплайн для автоматического деплоя
Автоматизируем процесс деплоя FastAPI приложения с помощью GitHub Actions.
# .github/workflows/deploy.yml
name: Deploy FastAPI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-asyncio httpx
- name: Run tests
run: |
pytest -v --cov=app --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
yourusername/fastapi-app:latest
yourusername/fastapi-app:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/fastapi-app
docker-compose pull
docker-compose up -d
docker system prune -f
Лучшие практики безопасности
- Секреты в .env файлах: Никогда не коммитьте .env файлы. Используйте .env.example как шаблон
- Обновления безопасности: Регулярно обновляйте базовые образы и зависимости
- Минимальные привилегии: Запускайте контейнеры от непривилегированных пользователей
- Сканирование уязвимостей: Используйте Trivy или Docker Scout для сканирования образов
- Rate limiting: Настройте ограничение запросов на уровне Nginx или в приложении
- CORS: Точно настройте CORS политики, не используйте
allow_origins=["*"]в продакшене
Сравнение методов деплоя
| Метод | Плюсы | Минусы | Когда использовать |
|---|---|---|---|
| Docker Compose | Простота, локальное тестирование, все в одном месте | Нет автомасштабирования, single point of failure | Стартапы, пет-проекты, staging окружение |
| Kubernetes | Автомасштабирование, отказоустойчивость, продвинутый мониторинг | Сложность настройки, overhead | Крупные проекты, микросервисы, высокие нагрузки |
| Serverless (AWS Lambda) | Автомасштабирование, pay-per-use, минимальный оверхеад | Cold starts, ограничения времени выполнения | API с переменной нагрузкой, event-driven архитектура |
| PaaS (Heroku, Render) | Максимальная простота, managed БД, автоматический деплой | Дорого на масштабе, меньше контроля | Прототипы, MVP, небольшие проекты |
FAQ: Частые вопросы по деплою FastAPI
Сколько воркеров Gunicorn нужно для моего приложения?
Общее правило: (2 * количество ядер) + 1. Для 4-ядерного сервера: 9 воркеров. Но тестируй под нагрузкой! I/O-bound приложения могут требовать больше воркеров, CPU-bound — меньше. Начинай с формулы и корректируй по метрикам.
Как обрабатывать статические файлы в FastAPI?
В продакшене никогда не используй StaticFiles из FastAPI. Настрой Nginx на раздачу статики: это в 100+ раз эффективнее. Храни статику в volume и монтируй в оба контейнера (app и nginx).
Как деплоить несколько FastAPI сервисов на одном сервере?
Используй Docker Compose с разными портами для каждого сервиса и настрой Nginx как reverse proxy с location блоками. Или лучше — переходи на Kubernetes с Ingress контроллером для proper routing.
Мой деплой работает, но приложение медленное. Что проверять?
1) Логи Nginx на 5xx ошибки 2) Мониторинг CPU/памяти контейнеров 3) Задержки в БД (slow queries) 4) Сетевую задержку между сервисами 5) Gunicorn timeout значения (увеличить если нужно). Используй APM инструменты вроде Sentry или Datadog.
Как сделать zero-downtime деплой?
1) Используй health checks в Docker Compose/Kubernetes 2) Настройте Gunicorn graceful timeout (30-60 секунд) 3) При деплое: запускай новый контейнер, дождись health check, затем останавливай старый 4) Используй blue-green deployment если критично.
Чеклист успешного деплоя
- ✓ HTTPS работает (проверь SSL Labs rating)
- ✓ Health check endpoint возвращает 200
- ✓ Статические файлы отдаются через Nginx
- ✓ Логи пишутся в stdout и собираются
- ✓ Мониторинг настроен (метрики, алерты)
- ✓ Резервные копии БД настроены
- ✓ CI/CD пайплайн тестирует и деплоит автоматически
- ✓ Документация API доступна по /docs и /redoc
Поздравляю! Теперь у тебя есть полное понимание, как делать профессиональный fastapi deploy. Помни: продакшен — это не просто "работает на сервере". Это надежность, безопасность, мониторинг и возможность масштабироваться. Начинай с Docker Compose, добавляй мониторинг, затем автоматизируй деплой. Каждый следующий проект будет деплоиться быстрее и надежнее!