Kaip paleisti Python programas su Gunicorn ir Nginx VPS serveryje
Įdiekite Flask arba FastAPI programą Ubuntu su virtualenv, Gunicorn systemd paslauga, Nginx atvirkštiniu tarpiniu serveriu, TLS, aplinkos failais, žurnalizavimu ir 502 klaidų bei worker dydžio kontroliniu sąrašu.

Įmontuotas Flask serveris ir uvicorn --reload skirti kūrimui. Viešame VPS norite procesų tvarkyklės, kuri paleidžia workerius iš naujo, susieja su localhost ir sėdi už atvirkštinio tarpinio serverio, kuris tvarko TLS ir lėtus klientus. Gunicorn yra įprastas WSGI pasirinkimas Flask ir Django. FastAPI gali veikti po Gunicorn su Uvicorn worker klase. Nginx (arba Caddy) užbaigia HTTPS ir persiunčia į 127.0.0.1:8000.
Šis vadovas eina per išdėstymą, kuris išgyvena paleidimus iš naujo: projektas /srv/app, virtualenv, .env su paslaptimis, gunicorn.service vienetas, Nginx serverio blokas, Let's Encrypt ir žurnalų failai, kuriuos iš tikrųjų galite grepinti. Taip pat kalbėsime apie worker skaičių, skirtįs ir kodėl 502 Bad Gateway beveik niekada nėra „Nginx sulūžęs“ — beveik visada Gunicorn neveikia, susietas su neteisingu lizdu arba krenta importuojant. Pavyzdys naudoja Flask, su pastabomis FastAPI, kur komanda skiriasi.
Kodėl šis rinkinys
Gunicorn iš anksto sukuria worker procesus. Kiekvienas worker tvarko vieną užklausą vienu metu, nebent naudojate kitą worker klasę. Nginx kaupia lėtus klientus, kad workeriai neužstrigtų siųsdami baitus į mobilųjį tinklą. systemd paleidžia programą iš naujo, jei ji miršta. Kartu tai nuobodu, o tai ir norite trečią valandą nakties.
- Gunicorn: stabilus WSGI/ASGI procesų modelis
- systemd: paleidimas įkrovos metu, paleidimas iš naujo po gedimo, journald žurnalai
- Nginx: TLS, statiniai failai, užklausos dydžio ribos, gzip
- venv: sistemos Python lieka švarus
- localhost susiejimas: programa pasiekiama tik per Nginx
Reikalavimai
Python 3.10+ Ubuntu 22.04/24.04 tinka. Nenaudokite pip kaip root į sistemos site-packages. TLS reikia domeno. Jei tarpiniam serveriui renkatės Caddy, šio straipsnio Gunicorn vienetas lieka tas pats — keičiasi tik priekinės dalies konfigūracija (žr. Caddy straipsnį).
- Ubuntu 22.04 arba 24.04 VPS
- Jūsų programa su requirements.txt arba lygiaverčiu
- WSGI įėjimo taškas (Flask: app:app) arba ASGI (FastAPI: app:app su uvicorn workeriais)
- Domeno A įrašas HTTPS
1 veiksmas: sistemos paketai, naudotojas ir projekto katalogas
Sukurkite sistemos naudotoją, kuris negali prisijungti interaktyviai, valdyti kodą ir paleisti Gunicorn. python3-venv ir kūrimo įrankių diegimas išvengia pip gedimų paketuose, kurie vis dar kompiliuoja C plėtinius.
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 veiksmas: Virtualenv ir priklausomybės
Sukurkite venv kaip appuser, kad failų nuosavybė būtų teisinga. Gamyboje prisegkite versijas. Po diegimo patvirtinkite, kad galite importuoti programą vienkartiniu gunicorn --check-config arba Python importu. Importo klaidos čia yra tos pačios klaidos, kurios vėliau tampa 502.
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 veiksmas: aplinkos failas
Nekoduokite SECRET_KEY ar duomenų bazės URL systemd vienete taip, kad jie patektų į visiems skaitomą git. Naudokite EnvironmentFile. chmod 640, savininkas root, grupė appuser (arba savininkas appuser, jei taip norite).
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/.env4 veiksmas: Gunicorn systemd paslauga
Susiekite su 127.0.0.1:8000, ne 0.0.0.0, nebent turite priežastį praleisti Nginx. Worker skaičius dažnai (2 x CPU) + 1 sinchroniniams workeriams; 2 vCPU VPS tai 5, kas gali būti per daug, jei kiekvienas worker įkelia sunkų ML modelį — tada naudokite 2–3. FastAPI nustatykite --worker-class uvicorn.workers.UvicornWorker ir įdiekite uvicorn. Skirtys turėtų viršyti lėčiausią sąžiningą užklausą, ne 30 sekundžių, jei turite 2 minučių eksportus.
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 80005 veiksmas: Nginx atvirkštinis tarpinis serveris ir TLS
Nginx klauso 80/443 ir perduoda Gunicorn. client_max_body_size svarbus įkėlimams. proxy_read_timeout turėtų sutapti arba viršyti Gunicorn skirtį. Kai serverio blokas veikia HTTP, išduokite sertifikatą su Certbot (arba priekinę dalį pakeiskite į Caddy). Toliau pateiktas fragmentas tik HTTP, kad galėtumėte išbandyti; tada paleiskite Certbot, kuris gali redaguoti failą.
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.com6 veiksmas: statiniai failai, teisės ir Flask/FastAPI ypatumai
Nginx turėtų teikti statinius failus, jei galite; tai greičiau nei Gunicorn. Django collectstatic, Flask send_from_directory mažoms programoms arba CDN vėliau. appuser turi galėti skaityti medį. Jei naudojate FastAPI, įdiekite uvicorn[standard] venv ir pakeiskite ExecStart naudoti UvicornWorker. Jei vietoj TCP naudojate Unix lizdus, nukreipkite proxy_pass į lizdą ir sutapkite teises, kad www-data galėtų ten rašyti.
# 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/appWorkeriai, atmintis ir nulinės prastovos perkrovimai
Sinchroniniai Gunicorn workeriai paprasti ir pakankami CPU lengvoms request/response API. Jei reikia daug lygiagrečių lėtų I/O laukimų, apsvarstykite gevent arba ASGI worker — matuokite, nespėliokite. Kiekvienas worker įkelia jūsų programą; RAM ~= workeriai kart programos RSS. 2 GB VPS su 8 workeriais 300 MB programos keis į diską ir atrodys „atsitiktinai lėtas“. systemctl reload gunicorn (HUP) gali paleisti workerius su nauju kodu, jei diegėte failus vietoje; viso paleidimo iš naujo aiškiau, kai keičiasi priklausomybės.
# 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/health502 ir tylių gedimų trikčių šalinimas
502 reiškia, kad Nginx negavo galiojančio atsakymo iš upstream. journalctl -u gunicorn -e yra pirmoji komanda. Įprastos priežastys: neteisingas module:app vardas, trūkstamas .env raktas, Postgres neveikia, susieta su 127.0.0.1, bet Nginx kitame mazge, SELinux (retai Ubuntu) arba programa klauso tik IPv6. 504 yra skirtis. 301 ciklai vyksta, kai programa nukreipia į HTTP, o X-Forwarded-Proto ignoruojamas.
- systemctl status gunicorn — ar jis aktyvus?
- journalctl -u gunicorn -n 100 — ImportError, trūkstama env, duomenų bazė
- curl -v http://127.0.0.1:8000/ iš VPS — jei tai nepavyksta, Nginx nekaltas
- nginx -t ir error.log — upstream prematurely closed connection
- ss -tulpn | grep 8000 — niekas neklauso
- Diskas pilnas — workeriai krenta paslaptingais būdais
Saugumas
Programa niekada nesiejama viešai. Paslaptys lieka .env. Laikykite venv ir OS su pataisymais. Nenaudokite Gunicorn kaip root. Jei tvarkote prisijungimus, nustatykite seanso slapukus Secure ir SameSite ir sukonfigūruokite karkasą pasitikėti X-Forwarded-Proto tik iš Nginx. Ribokite prisijungimo maršrutų greitį Nginx arba programoje.
- bind 127.0.0.1 arba unix lizdas
- chmod 600 .env
- Neroot User= systemd
- TLS per Certbot arba Caddy
- Išjunkite derinimo režimą ir auto-reload gamyboje
Patarimai
- Pridėkite /health maršrutą, kuris tikrina DB ryšį Compose ir apkrovos balansuotojams
- Siųskite statinius išteklius su cache-control antrašte Nginx
- El. paštui ir sunkiems darbams naudokite atskirą worker arba eilę (Redis + systemd)
- Prisegkite gunicorn ir uvicorn versijas requirements.txt
- Padarykite momentinę kopiją prieš pirmąjį perėjimą į gamybą
Python programa VPS paruošta gamybai, kai veikia po Gunicorn kaip systemd paslauga, susieta tik su localhost ir pasiekiama per Nginx arba Caddy su TLS. Paslaptis dėkite į EnvironmentFile, workerius dydinkite pagal RAM, o ne tinklaraščio formulę, ir 502 pirmiausia šalinkite iš Gunicorn žurnalo. Kai šis kelias dokumentuotas jūsų saugyklai, kiekvienas vėlesnis diegimas yra rsync arba git pull, pip install ir systemctl restart gunicorn.