Community
240
HostiServer
2026-02-23 14:53:00

Що таке WSGI: простий гайд для Python веб-проєктів

⏱️ Час читання: ~8 хвилин | 📅 Оновлено: 23 лютого 2026

Чому налаштування сервера критичне для Python-застосунків

Django та Flask — одні з найпопулярніших фреймворків для веб-розробки. Instagram, Pinterest, Spotify, Dropbox побудовані на Django. Netflix, LinkedIn, Reddit використовують Flask для мікросервісів та API. Але навіть ідеально написаний код буде гальмувати на погано налаштованому сервері.

Неправильна кількість workers, відсутність connection pooling, запуск під root, DEBUG=True в production — типові помилки, які перетворюють швидкий застосунок на повільний і вразливий. Ми бачили проєкти, де час відповіді падав з 1.2 секунди до 150мс просто після правильного налаштування бази даних.

У цьому гайді розберемо production-ready налаштування: від вибору між Gunicorn та uWSGI до systemd-сервісів з автоперезапуском, від connection pooling до моніторингу. Всі рекомендації базуються на реальному досвіді деплою сотень Python-проєктів.

Django vs Flask: коли що обрати

Django — "batteries included" фреймворк з вбудованою адмінкою, ORM, автентифікацією, формами, middleware, системою міграцій. Ідеальний для великих проєктів: eCommerce, CRM, SaaS-платформи, контент-менеджмент системи, соціальні мережі. Більше коду "з коробки", менше рішень приймати самому, швидший старт для типових задач.

Flask — мінімалістичний мікрофреймворк. Дає повну свободу у виборі компонентів: ORM (SQLAlchemy), форми (WTForms), автентифікація (Flask-Login), адмінка (Flask-Admin). Підходить для API, мікросервісів, невеликих застосунків, прототипів, Machine Learning сервісів. Легший у вивченні, але потребує більше ручної роботи для великих проєктів.

Обидва фреймворки production-ready і використовуються у великих компаніях. Різниця в підході: Django — конвенція над конфігурацією (Django Way), Flask — свобода вибору (micro framework).

Gunicorn vs uWSGI: що обрати

Обидва — WSGI-сервери для запуску Python-застосунків. Вони виступають посередником між веб-сервером (Nginx) та вашим кодом (Django/Flask). Але на практиці ми в 90% випадків обираємо Gunicorn.

Чому Gunicorn

  • Простіший у налаштуванні — менше параметрів, зрозуміліша документація, швидший старт
  • Стандарт для Docker — більшість туторіалів, Dockerfile прикладів і best practices написані під нього
  • "Чистіший" код — легше дебажити проблеми, менше "магії"
  • Достатньо функціоналу — для 90% проєктів не потрібно більше
  • Активна спільнота — швидкі відповіді на питання, регулярні оновлення

Коли uWSGI

uWSGI потужніший: має власний бінарний протокол (uwsgi), вбудоване кешування, emperor mode для керування кількома застосунками, підтримку WebSocket з коробки. Але конфігурація надто громіздка для типових проєктів — десятки параметрів, складний синтаксис. Обирайте uWSGI тільки якщо:

  • Потрібен emperor mode для керування багатьма застосунками на одному сервері
  • Використовуєте специфічні фічі: вбудоване кешування, spooler для черг
  • Вже маєте досвід роботи з uWSGI

Кількість workers

Класична формула (2 × CPU cores) + 1 — гарна відправна точка, але не аксіома. Детальніше про вибір кількості ядер та потоків для сервера:

Тип задач Рекомендація Чому
CPU-bound ≈ кількість ядер Обробка зображень, важкі обчислення — workers конкурують за CPU
I/O-bound (2-4) × ядра Багато запитів до БД, зовнішні API — workers часто чекають
Змішані (2 × ядра) + 1 Типовий веб-застосунок — баланс

Async workers (gevent, eventlet)

Для I/O-bound застосунків з великою кількістю одночасних запитів можна використовувати асинхронні workers:

# Встановлення
pip install gunicorn gevent
# Запуск з gevent workers
gunicorn myproject.wsgi:application \
    --worker-class gevent \
    --workers 4 \
    --worker-connections 1000 \
    --bind unix:/run/gunicorn.sock

Один gevent worker може обробляти тисячі одночасних з'єднань завдяки кооперативній багатозадачності. Але є нюанс: весь код повинен бути "gevent-friendly" — блокуючі операції заблокують весь worker.

Повний приклад конфігурації Gunicorn

# /etc/gunicorn/gunicorn.conf.py
# Кількість workers для 4-ядерного сервера
workers = 9  # (2×4)+1
# Bind до Unix socket (швидше за TCP)
bind = 'unix:/run/gunicorn.sock'
# Таймаути
timeout = 30  # Якщо довше — виносьте в Celery
graceful_timeout = 30  # Час на завершення поточного запиту
keepalive = 5  # Keep-alive з'єднання з Nginx
# Перезапуск workers для запобігання витокам пам'яті
max_requests = 1000
max_requests_jitter = 100  # Випадкове відхилення
# Логування
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'warning'
# Процес
daemon = False  # Systemd керує процесом
user = 'www-data'
group = 'www-data'

Важливі параметри:

  • timeout = 30 — якщо запит триває довше 30 секунд, архітектуру треба міняти на черги (Celery). Не збільшуйте таймаут — це приховує проблему
  • graceful_timeout = 30 — час для завершення поточного запиту перед перезавантаженням worker-а
  • max_requests = 1000 — перезапуск worker-а після 1000 запитів запобігає накопиченню витоків пам'яті від погано написаних бібліотек

Production Checklist для Django

Перед виходом в production обов'язково перевірте ці налаштування:

settings.py

# КРИТИЧНО: вимкнути debug mode
DEBUG = False
# Чіткий список дозволених доменів
ALLOWED_HOSTS = ['example.com', 'www.example.com']
# Якщо Django за Nginx/proxy
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Безпека
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True

⚠️ DEBUG = False критично! З увімкненим DEBUG при помилках Django показує повний traceback з кодом, шляхами до файлів, змінними середовища. Це пряма загроза безпеці.

Секрети та змінні середовища

Ніколи не комітьте секрети в Git. Використовуйте:

  • Environment Variables через .env файл (django-environ або python-dotenv)
  • HashiCorp Vault або AWS Secrets Manager для великих проєктів
# .env файл (НЕ комітити в Git!)
SECRET_KEY=your-super-secret-key
DATABASE_URL=postgres://user:pass@localhost/dbname
DEBUG=False

Middleware

Обов'язкові middleware для production:

  • SecurityMiddleware — редіректи на HTTPS, захист від XSS/Clickjacking
  • WhiteNoise — роздача статики без окремого web-сервера (якщо немає CDN)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Після SecurityMiddleware
    # ... інші middleware
]
# Конфігурація WhiteNoise
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Systemd: автозапуск та моніторинг

Systemd забезпечує автоматичний запуск застосунку при старті сервера та перезапуск при збоях.

Типовий файл сервісу

Створіть файл /etc/systemd/system/gunicorn.service:

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
# Шлях до gunicorn у virtualenv
ExecStart=/var/www/myproject/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    myproject.wsgi:application
# Автоперезапуск при збої
Restart=always
RestartSec=5
# Змінні середовища
EnvironmentFile=/var/www/myproject/.env
[Install]
WantedBy=multi-user.target

Керування сервісом

# Перезавантажити конфігурацію systemd
sudo systemctl daemon-reload
# Увімкнути автозапуск
sudo systemctl enable gunicorn
# Запустити сервіс
sudo systemctl start gunicorn
# Перевірити статус
sudo systemctl status gunicorn
# Переглянути логи
sudo journalctl -u gunicorn -f

Параметр Restart=always разом із RestartSec=5 гарантує, що сервіс автоматично підніметься через 5 секунд після будь-якого збою.

Nginx як reverse proxy

Nginx приймає запити від клієнтів і передає їх Gunicorn. Також роздає статичні файли і забезпечує SSL-шифрування.

Конфігурація для Django

# /etc/nginx/sites-available/myproject
upstream gunicorn {
    server unix:/run/gunicorn.sock fail_timeout=0;
}
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    
    # SSL сертифікати (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    # Статичні файли
    location /static/ {
        alias /var/www/myproject/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    # Media файли (uploads)
    location /media/ {
        alias /var/www/myproject/media/;
        expires 7d;
    }
    
    # Проксі до Gunicorn
    location / {
        proxy_pass http://gunicorn;
        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 30s;
        proxy_read_timeout 30s;
    }
}
# Активувати конфігурацію
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Конфігурація для Flask

Для Flask конфігурація Nginx майже ідентична. Головна різниця — у Gunicorn команді:

# Django
gunicorn myproject.wsgi:application --workers 3 --bind unix:/run/gunicorn.sock
# Flask
gunicorn app:app --workers 3 --bind unix:/run/gunicorn.sock
# Flask з factory pattern (create_app)
gunicorn "app:create_app()" --workers 3 --bind unix:/run/gunicorn.sock

Де app:app означає: файл app.py, змінна app (Flask instance).

PostgreSQL vs MySQL

Для Django ми на 95% рекомендуємо PostgreSQL. Причини:

  • Найкраща підтримка в Django — JSONB поля, повнотекстовий пошук, ArrayField, робота з типами даних
  • Надійність — краща обробка конкурентних транзакцій, MVCC
  • Розширюваність — PostGIS для геоданих, TimescaleDB для time-series
  • Стандарт індустрії — більшість Python-проєктів використовують саме Postgres

MySQL використовуємо тільки якщо це вимога клієнта або спадщина старого коду.

Connection Pooling

Кожен запит до Django створює з'єднання з базою даних. При великому трафіку це стає серйозним bottleneck — база даних не справляється з кількістю одночасних з'єднань.

На рівні Django (базове рішення):

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'CONN_MAX_AGE': 60,  # Тримати з'єднання 60 секунд
        'CONN_HEALTH_CHECKS': True,  # Django 4.1+
    }
}

CONN_MAX_AGE зазвичай ставимо 60-300 секунд. Це дозволяє перевикористовувати з'єднання між запитами.

При великому трафіку — PgBouncer:

Якщо CONN_MAX_AGE не вистачає або бачите помилки "too many connections", ставимо PgBouncer між Django та базою. Він підтримує пул з'єднань і розподіляє їх між workers.

# Встановлення PgBouncer
sudo apt install pgbouncer
# Конфігурація /etc/pgbouncer/pgbouncer.ini
[databases]
mydb = host=localhost dbname=mydb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20

💡 Реальний кейс: Проєкт на Flask, де кожен запит створював нове підключення до БД. Результат: 504 Gateway Timeout під навантаженням, час відповіді ~1.2 секунди. Після впровадження PgBouncer (connection pooling) час відповіді впав до 150мс — покращення в 8 разів.

Docker на Production

Рекомендуємо однозначно. Docker забезпечує ідентичність середовища розробки та production — "works on my machine" перестає бути проблемою. Деплой стає передбачуваним і повторюваним.

Вибір базового образу

Ми рекомендуємо python:3.12-slim:

  • alpine — часто створює проблеми з компіляцією C-бібліотек (psycopg2, numpy, Pillow, cryptography). Збірка може тривати в рази довше через необхідність компіляції з musl libc
  • slim — ідеальний баланс між розміром (~150MB) та стабільністю. Використовує glibc, всі бібліотеки встановлюються без проблем
  • повний образ — тільки якщо потрібні специфічні системні пакети (ffmpeg, imagemagick тощо)

Приклад Dockerfile

FROM python:3.12-slim
# Змінні середовища для Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Системні залежності
RUN apt-get update && apt-get install -y \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Спочатку копіюємо requirements для кешування шарів
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Потім код застосунку
COPY . .
# Створюємо непривілейованого користувача
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
# Збір статики
RUN python manage.py collectstatic --noinput
# Запуск
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "myproject.wsgi:application"]

Docker Compose для production

version: '3.8'
services:
  web:
    build: .
    restart: always
    env_file: .env
    depends_on:
      - db
      - redis
    networks:
      - backend
    
  db:
    image: postgres:16
    restart: always
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    networks:
      - backend
  
  redis:
    image: redis:7-alpine
    restart: always
    networks:
      - backend
  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./staticfiles:/var/www/static:ro
      - ./certbot/conf:/etc/letsencrypt:ro
    depends_on:
      - web
    networks:
      - backend
networks:
  backend:
volumes:
  postgres_data:

Важливі практики для Docker

  • Multi-stage builds — для зменшення розміру фінального образу
  • Health checks — для автоматичного перезапуску нездорових контейнерів
  • .dockerignore — виключіть .git, __pycache__, .env, venv
  • Не зберігайте дані в контейнері — використовуйте volumes для БД та media файлів

Celery для фонових задач

Якщо запит до Django триває більше 30 секунд — це сигнал винести задачу в Celery. Gunicorn worker блокується на час виконання запиту, і при довгих задачах швидко закінчуються вільні workers.

Типові кандидати для Celery:

  • Відправка email (особливо масові розсилки)
  • Обробка зображень (resize, watermark, оптимізація)
  • Генерація звітів (PDF, Excel)
  • Виклики зовнішніх API (платіжні системи, доставка)
  • Імпорт/експорт даних (CSV, XML)
  • Відео/аудіо конвертація
  • Синхронізація з CRM/ERP

Базове налаштування

# settings.py
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Kyiv'
# Обмеження для запобігання перевантаженню
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
CELERY_TASK_ACKS_LATE = True

Приклад задачі

# tasks.py
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_welcome_email(user_id):
    from users.models import User
    user = User.objects.get(id=user_id)
    send_mail(
        'Вітаємо!',
        'Дякуємо за реєстрацію.',
        'noreply@example.com',
        [user.email],
    )
# Виклик у view
send_welcome_email.delay(user.id)  # Асинхронно

Systemd сервіс для Celery worker

# /etc/systemd/system/celery.service
[Unit]
Description=Celery Worker
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
EnvironmentFile=/var/www/myproject/.env
ExecStart=/var/www/myproject/venv/bin/celery \
    -A myproject worker \
    --loglevel=info \
    --concurrency=4
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

Для періодичних задач (cron-like) додайте Celery Beat як окремий сервіс з celery -A myproject beat.

Моніторинг та логування

Production без моніторингу — це польоти наосліп. Коли щось падає, ви дізнаєтесь про це від користувачів, а не з алертів.

Логування в Django

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/app.log',
            'formatter': 'verbose',
        },
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['file', 'console'],
        'level': 'INFO',
    },
}

Sentry для відстеження помилок

Sentry автоматично збирає exceptions з production і надсилає алерти. Безцінний інструмент для дебагу.

# Встановлення
pip install sentry-sdk
# settings.py
import sentry_sdk
sentry_sdk.init(
    dsn="https://xxx@sentry.io/xxx",
    traces_sample_rate=0.1,  # 10% транзакцій для performance monitoring
    environment="production",
)

Health checks

Endpoint для перевірки стану застосунку — корисно для load balancers та моніторингу:

# Django urls.py
from django.http import JsonResponse
def health_check(request):
    # Перевірка БД
    from django.db import connection
    try:
        connection.ensure_connection()
    except Exception as e:
        return JsonResponse({'status': 'error', 'db': str(e)}, status=500)
    
    return JsonResponse({'status': 'ok'})
urlpatterns = [
    path('health/', health_check),
    # ...
]

Метрики для Prometheus/Grafana

Для серйозного моніторингу використовуйте django-prometheus або statsd:

  • Кількість запитів за секунду
  • Час відповіді (p50, p95, p99)
  • Кількість помилок
  • Використання пам'яті workers

Типові помилки

❌ Запуск Django під root

Ніколи не запускайте застосунок від root. Створіть окремого користувача (www-data або django) з мінімальними правами. Якщо застосунок буде скомпрометований, атакуючий отримає тільки обмежені права, а не повний контроль над сервером.

❌ DEBUG = True в production

При помилках Django показує весь код, шляхи до файлів, значення змінних, SQL-запити. Це пряма загроза безпеці — атакуючий бачить внутрішню структуру застосунку. Завжди перевіряйте DEBUG перед деплоєм.

❌ Відсутність логування

Коли щось падає в production, а в логах пусто — дебаг перетворюється на кошмар. Налаштуйте LOGGING в settings.py, логуйте помилки у файл або централізовану систему. Використовуйте Sentry для відстеження exceptions в реальному часі.

❌ Media-файли в папці з кодом

Зберігання uploads у папці з кодом робить неможливим горизонтальне масштабування та ускладнює деплой в Docker. Використовуйте S3-сумісне сховище (AWS S3, MinIO, DigitalOcean Spaces) або окремий volume.

❌ Секрети в коді

SECRET_KEY, паролі БД, API ключі в settings.py або коміти в Git — класична помилка. Історію Git неможливо повністю очистити, секрети залишаться доступними. Використовуйте environment variables з першого дня проєкту.

❌ Відсутність міграцій в production

Запуск python manage.py migrate в production без тестування — ризик втрати даних або downtime. Завжди тестуйте міграції на staging-середовищі, робіть backup бази перед міграцією.

🐍 Готові запустити Python-проєкт?

VPS для старту або Dedicated для високих навантажень — рішення, що масштабуються разом з вами.

💻 Cloud (VPS) Хостинг

  • Від $19.95/міс — Почніть з малого, масштабуйте миттєво
  • KVM віртуалізація — Гарантовані ресурси без overselling
  • Миттєві апгрейди — Без простоїв
  • NVMe сховище — Швидка продуктивність
  • 24/7 підтримка — Відповідь до 10 хв

🖥️ Dedicated Сервери

  • Від $200/міс — Сучасні конфігурації
  • Кастомні конфігурації — Intel або AMD, останні моделі
  • Різні локації — EU + USA
  • 99.9% uptime — Надійність
  • DDoS захист — Включено
  • Безкоштовна міграція — Допоможемо
  • Private Cloud — Proxmox, VMware, OpenStack

💬 Не впевнені, який варіант вам потрібен?
💬 Напишіть нам — допоможемо з усім!

Часті запитання

Gunicorn чи uWSGI для production?

Для 90% проєктів рекомендуємо Gunicorn — простіший у налаштуванні, є стандартом для Docker-контейнерів, має зрозумілу документацію. uWSGI обирайте тільки якщо потрібні його специфічні фічі: власний бінарний протокол uwsgi, вбудоване кешування, emperor mode для керування кількома застосунками.

PostgreSQL чи MySQL для Django?

PostgreSQL на 95%. Django має найкращу підтримку саме Postgres: JSONB поля для зберігання JSON без окремих таблиць, повнотекстовий пошук без Elasticsearch, ArrayField, HStoreField. MySQL тільки якщо це вимога клієнта або міграція існуючого проєкту.

Скільки workers для Gunicorn?

Стартова формула: (2 × CPU cores) + 1. Для 4-ядерного сервера це 9 workers. Для I/O-bound задач (багато запитів до БД) можна збільшувати. Для CPU-bound (обробка зображень) — тримайтесь ближче до кількості ядер. Завжди тестуйте під реальним навантаженням.

Який Docker образ обрати?

python:3.12-slim — оптимальний баланс між розміром (~150MB) та стабільністю. Alpine часто створює проблеми з компіляцією C-бібліотек через musl libc — psycopg2, numpy, Pillow можуть не зібратись або збиратись годинами.

Що робити при 504 Gateway Timeout?

Зазвичай проблема в довгих запитах до БД або зовнішніх сервісів. Перевірте slow query log PostgreSQL, впровадьте connection pooling (PgBouncer), винесіть важкі задачі в Celery, використовуйте Redis для кешування частих запитів.

Як безпечно оновлювати залежності?

Не оновлюйте все одразу. Використовуйте pip-audit для перевірки вразливостей, оновлюйте по одному пакету, тестуйте на staging. Для критичних security-патчів — оновлюйте негайно після тестування.

Як масштабувати Django/Flask застосунок?

Горизонтально: додавайте більше серверів за load balancer. Вертикально: збільшуйте ресурси сервера. Обов'язково: винесіть сесії в Redis, media файли в S3, базу даних на окремий сервер. Використовуйте CDN для статики.

Contents

Поділіться цією статтею

VPS з підтримкою від

$19 95 / міс

Виділені сервери від

$80 / міс

CDN починаючи від

$0 / міс

 

Користуючись цим сайтом, ви погоджуєтеся на використання файлів cookies відповідно до нашої Політики Конфіденційності.