Назад да блога
Жнівень 19, 2026Кіраўніцтва

Як запускаць Python-праграмы праз Gunicorn і Nginx на VPS

Выкладка Flask або FastAPI на Ubuntu: віртуальнае асяроддзе, служба systemd для Gunicorn, зваротны проксі Nginx, TLS, файл зменных, часопісы і разбор 502 і ліку воркераў.

Як запускаць Python-праграмы праз Gunicorn і Nginx на VPS

Убудаваны сервер Flask і uvicorn --reload — для распрацоўкі. На публічным VPS патрэбны менеджар працэсаў, які перазапускае воркеры, слухае localhost і стаіць за зваротным проксі з TLS і павольнымі кліентамі. Gunicorn — звычайны WSGI-выбар для Flask і Django. FastAPI можна ганяць пад Gunicorn з класам воркера Uvicorn. Nginx (або Caddy) завяршае HTTPS і перасылае на 127.0.0.1:8000.

Ніжэй схема, якая перажывае перазагрузку: праект у /srv/app, віртуальнае асяроддзе, .env з сакрэтамі, unit gunicorn.service, віртуальны хост Nginx, Let's Encrypt і часопісы, якія можна grep’аць. Разбяром лік воркераў, таймаўты і чаму 502 Bad Gateway амаль ніколі не значыць «зламаўся Nginx»: звычайна Gunicorn не запушчаны, слухае не той сокет або падае на імпарце. Прыклад на Flask, для FastAPI — асобныя заўвагі па камандзе.

Навошта гэты стэк

Gunicorn загадзя паднімае працэсы-воркеры. Кожны sync-воркер у адзін момант апрацоўвае адзін запыт, калі не змяніць клас воркера. Nginx буферызуе павольных кліентаў, каб воркеры не захрасалі на адпраўцы ў мабільную сетку. systemd паднімае праграму пасля падзення. Разам гэта нудна — а апоўначы менавіта гэта патрэбна.

  • Gunicorn: прадказальная мадэль WSGI/ASGI
  • systemd: старт пры загрузцы, перазапуск пры падзенні, часопісы journald
  • Nginx: TLS, статыка, ліміт памеру запыту, gzip
  • venv: сістэмны Python не засмечваецца
  • Прывязка да localhost: да праграмы не дастукацца ў абход Nginx

Што патрэбна

Python 3.10+ на Ubuntu 22.04/24.04 дастаткова. Не стаўце pip ад root у сістэмныя site-packages. Для TLS патрэбны дамен. Калі спераду хочаце Caddy, unit Gunicorn з гэтага артыкула не мяняецца — мяняецца толькі фронт (гл. артыкул пра Caddy).

  • VPS Ubuntu 22.04 або 24.04
  • Праграма з requirements.txt або аналагам
  • Кропка ўваходу WSGI (для Flask: app:app) або ASGI (FastAPI: app:app і воркеры uvicorn)
  • A-запіс дамена для HTTPS

Крок 1. Пакеты, карыстальнік і каталог праекта

Сістэмны карыстальнік без інтэрактыўнага ўваходу валодае кодам і запускае Gunicorn. python3-venv і пакеты зборкі ратуюць pip, калі яшчэ трапляюцца пашырэнні на C.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y upgrade
apt -y install python3 python3-venv python3-pip python3-dev build-essential nginx curl

adduser --system --group --home /srv/app appuser
mkdir -p /srv/app
chown appuser:appuser /srv/app

# Copy your code (example):
# rsync -a --delete ./myproject/ appuser@YOUR_VPS_IP:/srv/app/

Крок 2. Віртуальнае асяроддзе і залежнасці

Стварайце venv ад appuser, каб уладальнікі файлаў супалі. У баі версіі пакетаў фіксуйце. Пасля ўсталёўкі пераканайцеся, што праграма імпартуецца — тыя самыя памылкі потым ператворацца ў 502.

bash
sudo -u appuser -H bash -lc '
cd /srv/app
python3 -m venv /srv/app/venv
/srv/app/venv/bin/pip install --upgrade pip
/srv/app/venv/bin/pip install -r /srv/app/requirements.txt gunicorn
'

# Flask example check:
sudo -u appuser -H /srv/app/venv/bin/python -c "from app import app; print('import ok')"

Крок 3. Файл зменных асяроддзя

Не зашывайце SECRET_KEY і URL базы ў unit так, каб гэта выцекла ў git з правамі на чытанне ўсім. Выкарыстоўвайце EnvironmentFile. chmod 640, уладальнік root, група appuser (або ўладальнік appuser, калі так зручней).

bash
cat >/srv/app/.env <<'EOF'
FLASK_ENV=production
SECRET_KEY=replace-with-openssl-rand-hex-32
DATABASE_URL=postgresql://app:password@127.0.0.1:5432/app
EOF
chown appuser:appuser /srv/app/.env
chmod 600 /srv/app/.env

Крок 4. Служба systemd для Gunicorn

Слухайце 127.0.0.1:8000, не 0.0.0.0, калі няма прычыны абходзіць Nginx. Лік sync-воркераў часта (2 × CPU) + 1; на 2 vCPU гэта 5, і гэтага можа быць шмат, калі кожны воркер грузіць цяжкую мадэль — тады 2–3. Для FastAPI укажыце --worker-class uvicorn.workers.UvicornWorker і пастаўце uvicorn. Таймаўт павінен быць большы за найдаўжэйшы сумленны запыт, а не 30 секунд, калі ў вас выгрузкі па дзве хвіліны.

bash
cat >/etc/systemd/system/gunicorn.service <<'EOF'
[Unit]
Description=Gunicorn for the web app
After=network.target

[Service]
User=appuser
Group=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 --timeout 60 --access-logfile - --error-logfile - app:app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now gunicorn
systemctl status gunicorn --no-pager
ss -tulpn | grep 8000

Крок 5. Зваротны проксі Nginx і TLS

Nginx слухае 80/443 і проксуе на Gunicorn. client_max_body_size важны для загрузак. proxy_read_timeout не меншы за таймаўт Gunicorn. Калі HTTP запрацуе, выпусціце сертыфікат Certbot (або пастаўце спераду Caddy). Ніжэй блок толькі HTTP для праверкі; Certbot потым можа яго правіць.

bash
cat >/etc/nginx/sites-available/app <<'EOF'
server {
    listen 80;
    server_name app.example.com;
    client_max_body_size 20m;

    location /static/ {
        alias /srv/app/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        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_read_timeout 90s;
    }
}
EOF

ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
nginx -t && systemctl reload nginx

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

apt -y install certbot python3-certbot-nginx
certbot --nginx -d app.example.com

Крок 6. Статыка, правы і адрозненні Flask/FastAPI

Статыку па магчымасці аддае Nginx — гэта хутчэй за Gunicorn. Django collectstatic, для крыхічнага Flask часам send_from_directory, пазней CDN. Дрэва павінен чытаць appuser. Для FastAPI пастаўце uvicorn[standard] у venv і змяніце ExecStart на UvicornWorker. Калі бярыце UNIX-сокет замест TCP, proxy_pass глядзіць у сокет, правы такія, каб www-data мог пісаць.

bash
# FastAPI ExecStart example:
# ExecStart=/srv/app/venv/bin/gunicorn -k uvicorn.workers.UvicornWorker --workers 2 --bind 127.0.0.1:8000 app:app

# Unix socket variant:
# --bind unix:/run/gunicorn/app.sock
# Nginx: proxy_pass http://unix:/run/gunicorn/app.sock:

chown -R appuser:appuser /srv/app

Воркеры, памяць і перазагрузка без лішняй драмы

Сінхронныя воркеры Gunicorn простыя і падыходзяць лёгкім API запыт-адказ. Шмат адначасовых павольных I/O — глядзіце gevent або ASGI, але вымярайце, не гадайце. Кожны воркер загружае праграму; RAM ≈ лік воркераў × RSS праграмы. VPS на 2 ГБ і 8 воркераў па 300 МБ пойдуць у падкачку і будуць «часам тармазіць». systemctl reload gunicorn (HUP) перазапускае воркеры з новым кодам, калі файлы ўжо на месцы; поўны restart зразумелейшы, калі змяніліся залежнасці.

bash
# After git pull / rsync of new code:
sudo -u appuser -H /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
systemctl restart gunicorn
curl -I https://app.example.com/health

Разбор 502 і ціхіх падзенняў

502 значыць, што Nginx не атрымаў нармальны адказ ад upstream. Першая каманда — journalctl -u gunicorn -e. Частыя прычыны: нявернае module:app, няма ключа ў .env, не запушчаны Postgres, слухае 127.0.0.1, а Nginx на іншым хосце, SELinux (рэдка на Ubuntu) або праграма слухае толькі IPv6. 504 — таймаўт. Цыкл 301 — праграма рэдырэктыць на HTTP і ігнаруе X-Forwarded-Proto.

  • systemctl status gunicorn — служба active?
  • journalctl -u gunicorn -n 100 — ImportError, няма зменнай, база
  • curl -v http://127.0.0.1:8000/ з самога VPS — калі тут памылка, Nginx ні пры чым
  • nginx -t і error.log — upstream prematurely closed connection
  • ss -tulpn | grep 8000 — ніхто не слухае
  • Дыск забіты — воркеры падаюць «незразумела чаму»

Абарона

Праграма звонку не слухае. Сакрэты ў .env. venv і АС абнаўляйце. Gunicorn не ад root. Калі ёсць уваход карыстальнікаў, cookie сесіі Secure і SameSite, фрэймворк давярае X-Forwarded-Proto толькі ад Nginx. Ліміт частаты на логін — у Nginx або ў праграме.

  • bind 127.0.0.1 або UNIX-сокет
  • chmod 600 у .env
  • User= не root у systemd
  • TLS праз Certbot або Caddy
  • debug і аўтаперазагрузка ў баі выключаныя

Парады

  • Маршрут /health з праверкай базы для Compose і балансіроўшчыкаў
  • Для статыкі ў Nginx задайце Cache-Control
  • Лісты і цяжкія задачы — асобны воркер або чарга (Redis + systemd)
  • Версіі gunicorn і uvicorn фіксуйце ў requirements.txt
  • Здымак дыска перад першым баявым пераключэннем DNS

Python-праграма на VPS гатовая да бою, калі яна круціцца пад Gunicorn як служба systemd, слухае толькі localhost і даступная праз Nginx або Caddy з TLS. Сакрэты — у EnvironmentFile, лік воркераў — па RAM, а не па чужой формуле з блога, 502 спачатку глядзіце ў часопісе Gunicorn. Калі гэты шлях запісаны для рэпазіторыя, кожная наступная выкладка — rsync або git pull, pip install і systemctl restart gunicorn.