Πίσω στο blog
Αύγουστος 19, 2026Οδηγοί

Πώς να τρέξετε εφαρμογές Python με Gunicorn και Nginx σε ένα VPS

Αναπτύξτε εφαρμογή Flask ή FastAPI σε Ubuntu με virtualenv, υπηρεσία 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, virtualenv, .env με μυστικά, μονάδα gunicorn.service, μπλοκ διακομιστή Nginx, Let's Encrypt και αρχεία καταγραφής που μπορείτε πραγματικά να grepάρετε. Θα μιλήσουμε επίσης για αριθμό εργατών, χρονικά όρια και γιατί το 502 Bad Gateway σχεδόν ποτέ δεν είναι «το Nginx είναι σπασμένο» — σχεδόν πάντα το Gunicorn δεν τρέχει, είναι δεσμευμένο στο λάθος socket ή κρασάρει στην εισαγωγή. Το παράδειγμα χρησιμοποιεί Flask, με σημειώσεις για FastAPI όπου η εντολή διαφέρει.

Γιατί αυτή η στοίβα

Το Gunicorn προδιακλαδώνει διεργασίες εργατών. Κάθε εργάτης χειρίζεται ένα αίτημα τη φορά εκτός αν χρησιμοποιείτε διαφορετική κλάση εργάτη. Το Nginx αποθηκεύει προσωρινά αργούς πελάτες ώστε οι εργάτες να μην κολλάνε στέλνοντας byte σε δίκτυο κινητής. Το systemd επανεκκινεί την εφαρμογή αν πεθάνει. Μαζί είναι βαρετό, που είναι αυτό που θέλετε στις 3 το πρωί.

  • 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 ως διαμεσολαβητή, η μονάδα Gunicorn σε αυτό το άρθρο μένει η ίδια — αλλάζει μόνο η ρύθμιση του μπροστινού (δείτε το άρθρο Caddy).

  • Ubuntu 22.04 ή 24.04 VPS
  • Η εφαρμογή σας με 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: Virtualenv και εξαρτήσεις

Δημιουργήστε το venv ως appuser ώστε η ιδιοκτησία αρχείων να είναι σωστή. Καρφιτσώστε εκδόσεις στην παραγωγή. Μετά την εγκατάσταση επιβεβαιώστε ότι μπορείτε να εισάγετε την εφαρμογή σε εφάπαξ gunicorn --check-config ή εισαγωγή Python. Τα σφάλματα εισαγωγής εδώ είναι τα ίδια σφάλματα που αργότερα γίνονται 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 βάσης στη μονάδα systemd με τρόπο που καταλήγει σε 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. Ο αριθμός εργατών είναι συχνά (2 x CPU) + 1 για συγχρονισμένους εργάτες· σε VPS 2 vCPU αυτό είναι 5, που μπορεί να είναι πάρα πολλοί αν κάθε εργάτης φορτώνει βαρύ μοντέλο ML — τότε χρησιμοποιήστε 2–3. Για FastAPI ορίστε --worker-class uvicorn.workers.UvicornWorker και εγκαταστήστε uvicorn. Τα χρονικά όρια πρέπει να υπερβαίνουν το πιο αργό τίμιο αίτημά σας, όχι 30 δευτερόλεπτα αν έχετε εξαγωγές 2 λεπτών.

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 sockets αντί TCP, κατευθύνετε proxy_pass στο socket και ταιριάξτε δικαιώματα ώστε το 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 αίτησης/απόκρισης ελαφριάς CPU. Αν χρειάζεστε πολλούς ταυτόχρονους αργούς αναμονές I/O, εξετάστε gevent ή εργάτη ASGI — μετρήστε, μην μαντεύετε. Κάθε εργάτης φορτώνει την εφαρμογή σας· RAM ~= εργάτες επί RSS εφαρμογής. Ένα VPS 2 GB με 8 εργάτες εφαρμογής 300 MB θα κάνει swap και θα νιώθει «τυχαία αργό». Το systemctl reload gunicorn (HUP) μπορεί να επανεκκινήσει εργάτες με τον νέο κώδικα αν αναπτύξατε αρχεία στη θέση τους· πλήρης επανεκκίνηση είναι σαφέστερη όταν αλλάζουν εξαρτήσεις.

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 — είναι ενεργό;
  • journalctl -u gunicorn -n 100 — ImportError, λείπει env, βάση δεδομένων
  • curl -v http://127.0.0.1:8000/ από το VPS — αν αυτό αποτύχει, το Nginx είναι αθώο
  • nginx -t και error.log — upstream prematurely closed connection
  • ss -tulpn | grep 8000 — τίποτα δεν ακούει
  • Δίσκος γεμάτος — οι εργάτες κρασάρουν με μυστηριώδεις τρόπους

Ασφάλεια

Η εφαρμογή δεν δένει ποτέ δημόσια. Τα μυστικά μένουν στο .env. Κρατήστε το venv και το OS με ενημερώσεις. Μην τρέχετε Gunicorn ως root. Αν χειρίζεστε συνδέσεις, ορίστε cookies συνεδρίας Secure και SameSite και ρυθμίστε το πλαίσιο να εμπιστεύεται X-Forwarded-Proto μόνο από Nginx. Περιορίστε ρυθμό διαδρομών σύνδεσης στο Nginx ή στην εφαρμογή.

  • bind 127.0.0.1 ή unix socket
  • chmod 600 .env
  • Μη root User= στο systemd
  • TLS μέσω Certbot ή Caddy
  • Απενεργοποιήστε λειτουργία εντοπισμού σφαλμάτων και auto-reload στην παραγωγή

Συμβουλές

  • Προσθέστε διαδρομή /health που ελέγχει συνδεσιμότητα βάσης για Compose και εξισορροπητές φορτίου
  • Στείλτε στατικά στοιχεία με κεφαλίδα cache-control στο Nginx
  • Χρησιμοποιήστε ξεχωριστό εργάτη ή ουρά (Redis + systemd) για email και βαριές εργασίες
  • Καρφιτσώστε εκδόσεις gunicorn και uvicorn στο requirements.txt
  • Πάρτε στιγμιότυπο πριν την πρώτη μετάβαση στην παραγωγή

Μια εφαρμογή Python σε VPS είναι έτοιμη για παραγωγή όταν τρέχει κάτω από Gunicorn ως υπηρεσία systemd, δένει μόνο στο localhost και προσεγγίζεται μέσω Nginx ή Caddy με TLS. Βάλτε μυστικά σε EnvironmentFile, διαστασιολογήστε εργάτες σύμφωνα με τη RAM αντί για τύπο ιστολογίου και διορθώστε 502 πρώτα από το ημερολόγιο Gunicorn. Μόλις αυτή η διαδρομή τεκμηριωθεί για το αποθετήριό σας, κάθε μεταγενέστερη ανάπτυξη είναι rsync ή git pull, pip install και systemctl restart gunicorn.