Linux & Administration

Gunicorn / WSGI

WSGI — стандарт связи Python-приложения с веб-сервером, Gunicorn — самый популярный WSGI-сервер для production.

Что такое WSGI

WSGI (Web Server Gateway Interface, произносится «виз-ги») — стандарт, описанный в PEP 3333: как веб-сервер должен передавать HTTP-запрос Python-приложению и получать ответ.

До WSGI каждый фреймворк договаривался с каждым сервером по-своему, и связки не переставлялись. Стандарт свёл всё к одной функции:

def application(environ, start_response):
    """environ — словарь с данными запроса, start_response — колбэк для статуса и заголовков."""
    start_response('200 OK', [('Content-Type', 'text/plain; charset=utf-8')])
    return [b'Hello, WSGI']

Всё. Django, Flask и любой другой WSGI-фреймворк — это, если снять слои абстракций, такая же функция. Поэтому любое WSGI-приложение работает с любым WSGI-сервером: Gunicorn, uWSGI, Waitress меняются одной строкой.

Что такое Gunicorn

Gunicorn (Green Unicorn) — WSGI-сервер для production. Он занимается тем, чего не умеет manage.py runserver: держит пул рабочих процессов, распределяет между ними запросы, перезапускает зависшие и пережившие утечку памяти.

Модель — pre-fork: мастер-процесс порождает несколько воркеров, каждый обрабатывает запросы независимо. Мастер сам запросы не трогает, только следит за воркерами.

gunicorn --workers 5 --bind 127.0.0.1:8000 --timeout 60 \
         --access-logfile - myproject.wsgi:application

myproject.wsgi:application — модуль и имя WSGI-объекта в нём.

Сколько воркеров ставить

Документация Gunicorn рекомендует формулу 2 × число ядер + 1. Для 4-ядерной машины — 9 воркеров.

Каждый воркер — отдельный процесс со своей копией приложения в памяти. Считайте: приложение на 200 МБ × 9 воркеров = 1.8 ГБ, и если VPS даёт 2 ГБ, OOM killer придёт за вами под нагрузкой. Формула — отправная точка, а не догма: смотрите на реальное потребление.

Класс воркера Когда брать
sync (по умолчанию) обычные приложения, быстрые ответы
gthread много ожидания ввода-вывода, память дорога
gevent тысячи одновременных медленных соединений
uvicorn.workers.UvicornWorker ASGI-приложения (FastAPI, Django async)

WSGI или ASGI

У WSGI есть врождённое ограничение: он синхронный. Воркер занят одним запросом целиком и не может, ожидая ответа базы, взяться за следующий. Для WebSocket, server-sent events и streaming-ответов это не подходит принципиально.

Ответ — ASGI, асинхронный преемник. Его сервер — uvicorn; на нём работают FastAPI и Django в async-режиме. Для LLM-сервисов, где ответ течёт токен за токеном десятки секунд, WSGI не годится: воркер будет держать соединение и простаивать. Здесь берут ASGI или готовые серверы инференса вроде vLLM.

Практично сочетать оба — Gunicorn как мастер-процесс, uvicorn-воркеры внутри: gunicorn -k uvicorn.workers.UvicornWorker -w 4 app.main:app.

Зачем Nginx перед Gunicorn

Gunicorn — сервер приложений, а не веб-сервер. Он не отдаёт статику эффективно, не терминирует TLS и уязвим к медленным клиентам: воркер, ждущий байты от клиента на плохом мобильном интернете, занят и никого не обслуживает. Разработчики Gunicorn прямо рекомендуют ставить его за Nginx.

upstream app { server unix:/run/myapp/gunicorn.sock; }

server {
    listen 80;
    location /static/ { alias /srv/myapp/static/; expires 30d; }
    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Unix-сокет вместо TCP-порта чуть быстрее и не светит приложение наружу.

Запуск как службы

В production Gunicorn запускают юнитом systemd с Restart=always — он поднимет процесс после перезагрузки и после падения. В контейнерах роль надзирателя берёт на себя Docker или Kubernetes, и Gunicorn запускают одной командой в CMD.

Связанные термины

Готовы запустить GPU-задачу?

Запустить GPU-сервер