Python и FastAPI: продакшн-деплой
После этой инструкции ваше FastAPI-приложение будет работать под управлением Gunicorn с воркерами Uvicorn, автоматически подниматься при перезагрузке сервера через systemd и обслуживать внешние запросы через nginx — то есть будет готово к реальному трафику, а не только к uvicorn main:app --reload на ноутбуке.
Что понадобится
- VPS mHost на одном из шаблонов ОС: Ubuntu 22.04/24.04, Debian 11/12, AlmaLinux/Rocky Linux 8/9.
- Доступ по SSH под root или пользователем с sudo.
- Домен, указывающий на IP сервера — для
server_nameв nginx. Если домена пока нет, подойдёт и IP-адрес. - Готовое FastAPI-приложение (или минимальный пример из статьи — просто чтобы проверить всю цепочку от venv до nginx).
Шаг 1. Подготовьте пользователя, venv и зависимости
Приложение будет работать не от root, а от отдельного системного пользователя без права входа — это ограничивает ущерб, если в самом приложении найдётся уязвимость.
Установите Python и модуль для виртуальных окружений:
Ubuntu / Debian
sudo apt update
sudo apt install python3-venv python3-pipAlmaLinux / Rocky Linux
sudo dnf install python3 python3-pipСоздайте отдельного системного пользователя для приложения. Флаг --create-home заодно создаст ему домашний каталог /opt/myapp, где будут жить и код, и venv:
sudo useradd --system --create-home --home-dir /opt/myapp --shell /usr/sbin/nologin myappПереключитесь на этого пользователя (у него нет обычного шелла для входа, поэтому запускаем bash явной командой) и создайте виртуальное окружение:
sudo -u myapp -H bash
cd /opt/myapp
python3 -m venv venv
source venv/bin/activateУстановите FastAPI, Uvicorn и Gunicorn:
pip install --upgrade pip
pip install fastapi "uvicorn[standard]" gunicorn uvicorn-workerСоздайте минимальный main.py — если у вас уже есть готовое приложение, пропустите этот шаг и просто скопируйте свой код в /opt/myapp:
cat > /opt/myapp/main.py << 'EOF'
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"status": "ok"}
EOFПроверьте, что приложение вообще запускается — напрямую через Uvicorn, без Gunicorn:
uvicorn main:app --host 127.0.0.1 --port 8000 &
sleep 1
curl http://127.0.0.1:8000/
kill %1Должно вернуться {"status":"ok"}.
Шаг 2. Запустите Gunicorn с воркерами Uvicorn
Gunicorn выступает менеджером процессов: поднимает несколько параллельных воркеров, перезапускает упавшие и умеет обновлять приложение без даунтайма — но сам протокол ASGI, на котором работает FastAPI, Gunicorn понимает только через класс воркера uvicorn_worker.UvicornWorker.
Число воркеров задаётся флагом -w/--workers. Официальная рекомендация Gunicorn — 2–4 воркера на ядро CPU (число ядер можно узнать командой nproc). Для небольшого VPS в 1–2 ядра обычно достаточно 3–4 воркеров.
Проверьте связку прямо сейчас, всё ещё находясь в сессии пользователя myapp с активным venv:
gunicorn main:app \
--workers 3 \
--worker-class uvicorn_worker.UvicornWorker \
--bind 127.0.0.1:8000 &
sleep 1
curl http://127.0.0.1:8000/
kill %1
exitКоманда exit в конце возвращает вас обратно в сессию sudo-пользователя — дальше вся настройка идёт от его имени.
Шаг 3. Настройте systemd-юнит
Создайте юнит /etc/systemd/system/myapp.service:
sudo tee /etc/systemd/system/myapp.service > /dev/null << 'EOF'
[Unit]
Description=Gunicorn (Uvicorn workers) for myapp
After=network.target
[Service]
Type=notify
NotifyAccess=main
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/myapp/venv/bin/gunicorn main:app \
--workers 3 \
--worker-class uvicorn_worker.UvicornWorker \
--bind 127.0.0.1:8000 \
--access-logfile - \
--error-logfile - \
--timeout 60
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
Restart=on-failure
RestartSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOFВключите и запустите сервис:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myappЛоги (в том числе логи запросов, которые пишет Gunicorn) смотрите через journald:
sudo journalctl -u myapp -fШаг 4. Настройте nginx как реверс-прокси
Установите nginx:
Ubuntu / Debian
sudo apt install nginxAlmaLinux / Rocky Linux
sudo dnf install nginx
sudo systemctl enable --now nginxСоздайте конфиг сайта. Путь к файлу отличается в зависимости от дистрибутива — выберите нужную строку CONF=:
# Ubuntu / Debian:
CONF=/etc/nginx/sites-available/myapp
# AlmaLinux / Rocky Linux:
CONF=/etc/nginx/conf.d/myapp.conf
sudo tee "$CONF" > /dev/null << 'EOF'
upstream myapp {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80;
server_name example.com;
client_max_body_size 20m;
location / {
proxy_pass http://myapp;
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;
}
}
EOF
# Ubuntu / Debian only — enable the site with a symlink:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myappЗамените example.com на свой домен (или на IP-адрес сервера, если домена ещё нет). fail_timeout=0 в блоке upstream значит «не считать бэкенд недоступным после одной неудачной попытки, пробовать снова» — это разумно, когда за upstream стоит единственный процесс Gunicorn и переключаться всё равно больше не на что.
Проверьте конфигурацию и перезапустите nginx:
sudo nginx -t
sudo systemctl reload nginxПроверьте снаружи:
curl http://example.com/Должно вернуться {"status":"ok"}.
getsebool httpd_can_network_connect
sudo setsebool -P httpd_can_network_connect onШаг 5. Откройте порт в файрволе
Сам Gunicorn слушает только 127.0.0.1 и снаружи недоступен в принципе — открывать нужно только порт nginx.
Ubuntu / Debian (ufw)
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verboseAlmaLinux / Rocky Linux (firewalld)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-allЧто дальше
- Логичный следующий шаг — выпустить TLS-сертификат и включить HTTPS (например, через Let's Encrypt/certbot) поверх уже настроенного nginx; это отдельная тема за рамками этой статьи.
- Обновлять приложение теперь просто: обновите код в
/opt/myapp(например,git pullот имени пользователяmyapp) и перезапустите сервис командойsudo systemctl restart myapp. - Полный список того, что в VMmanager 6 ставится в один клик — в статье «Готовые рецепты VMmanager 6»; работа с файрволом подробнее разобрана в статье «Настройка firewall (ufw)».