Come servire le applicazioni Flask con Gunicorn e Nginx su Ubuntu 22.04

A previous version of this tutorial was written by Kathleen Juell.

Introduzione

In questa guida, costruirai un’applicazione Python utilizzando il microframework Flask su Ubuntu 22.04. La maggior parte di questo tutorial sarà su come configurare il server dell’applicazione Gunicorn e su come avviare l’applicazione e configurare Nginx per agire come un proxy inverso front-end.

Prerequisiti

Prima di iniziare questa guida, dovresti avere:

  • Un server con Ubuntu 22.04 installato e un utente non root con privilegi sudo. Segui la nostra guida all’impostazione iniziale del server per orientamento.

  • Nginx installato, seguendo i Passaggi 1 e 2 di Come Installare Nginx su Ubuntu 22.04.

  • Un nome di dominio configurato per puntare al tuo server. Puoi acquistarne uno su Namecheap o ottenerne uno gratuitamente su Freenom. Puoi imparare come puntare i domini su DigitalOcean seguendo la relativa documentazione su domini e DNS. Assicurati di creare i seguenti record DNS:

    • Un record A con il_tuo_dominio puntato all’indirizzo IP pubblico del tuo server.
    • Un record A con www.il_tuo_dominio puntato all’indirizzo IP pubblico del tuo server.
  • Familiarità con la specifica WSGI, che il server Gunicorn utilizzerà per comunicare con la tua applicazione Flask. Questa discussione approfondisce ulteriormente il WSGI.

Passaggio 1 — Installazione dei Componenti dai Repository di Ubuntu

Il primo passaggio sarà quello di installare tutti i componenti richiesti dai repository di Ubuntu. Questo include pip, il gestore dei pacchetti Python, che gestirà i componenti Python. Otterrai anche i file di sviluppo di Python necessari per compilare alcuni dei componenti di Gunicorn.

Prima di tutto, aggiorna l’indice dei pacchetti locali e installa i pacchetti che ti consentiranno di creare il tuo ambiente Python. Questi includeranno python3-pip, insieme a alcuni altri pacchetti e strumenti di sviluppo necessari per un ambiente di programmazione robusto:

  1. sudo apt update
  2. sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Con questi pacchetti in posizione, il passo successivo è creare un ambiente virtuale per il tuo progetto.

Passaggio 2 — Creazione di un Ambiente Virtuale Python

Successivamente, configurerai un ambiente virtuale per isolare l’applicazione Flask dagli altri file Python sul tuo sistema.

Inizia installando il pacchetto python3-venv, che installerà il modulo venv:

  1. sudo apt install python3-venv

Successivo, crea una directory principale per il tuo progetto Flask. Spostati nella directory con il comando cd dopo averla creata:

  1. mkdir ~/myproject
  2. cd ~/myproject

Crea un ambiente virtuale per memorizzare i requisiti Python del tuo progetto Flask digitando:

  1. python3 -m venv myprojectenv

Questo installerà una copia locale di Python e pip in una directory chiamata myprojectenv all’interno della tua directory del progetto.

Prima di installare le applicazioni all’interno dell’ambiente virtuale, devi attivarlo. Fallo digitando:

  1. source myprojectenv/bin/activate

Il prompt cambierà per indicare che stai ora operando all’interno dell’ambiente virtuale. Apparirà qualcosa del genere: (myprojectenv)user@host:~/myproject$.

Passaggio 3 — Configurazione di un’applicazione Flask

Ora che sei nel tuo ambiente virtuale, puoi installare Flask e Gunicorn e iniziare a progettare la tua applicazione.

Prima, installa wheel con l’istanza locale di pip per garantire che i tuoi pacchetti si installino anche se mancano archivi wheel:

  1. pip install wheel

Nota

Indipendentemente dalla versione di Python che stai utilizzando, quando l’ambiente virtuale è attivato, dovresti usare il comando pip (non pip3).

Successivamente, installa Flask e Gunicorn:

  1. pip install gunicorn flask

Creazione di un’applicazione di esempio

Ora che hai Flask disponibile, puoi creare un’applicazione semplice. Flask è un microframework. Non include molti degli strumenti che potrebbero essere presenti in framework più completi e esiste principalmente come un modulo che puoi importare nei tuoi progetti per aiutarti nell’inizializzazione di un’applicazione web.

Anche se la tua applicazione potrebbe essere più complessa, creeremo la nostra app Flask in un singolo file, chiamato myproject.py:

  1. nano ~/myproject/myproject.py

Il codice dell’applicazione risiederà in questo file. Importerà Flask e istanzierà un oggetto Flask. Puoi usare questo per definire le funzioni che devono essere eseguite quando viene richiesta una specifica route:

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Questo definisce fondamentalmente quale contenuto presentare quando viene accesso il dominio radice. Salva e chiudi il file quando hai finito.

Se hai seguito la guida iniziale alla configurazione del server, dovresti avere un firewall UFW abilitato. Per testare l’applicazione, devi consentire l’accesso alla porta 5000:

  1. sudo ufw allow 5000

Ora puoi testare la tua app Flask digitando:

  1. python myproject.py

Vedrai un output simile al seguente, incluso un avviso utile che ti ricorda di non utilizzare questa configurazione del server in produzione:

Output
* Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Visita l’indirizzo IP del tuo server seguito da :5000 nel tuo browser web:

http://your_server_ip:5000

Dovresti vedere qualcosa del genere:

Quando hai finito, premi CTRL-C nella finestra del terminale per interrompere il server di sviluppo Flask.

Creazione del Punto di Accesso WSGI

Successivamente, crea un file che fungerà da punto di accesso per la tua applicazione. Questo dirà al server Gunicorn come interagire con l’applicazione.

Chiamalo wsgi.py:

  1. nano ~/myproject/wsgi.py

In questo file, importa l’istanza Flask dalla nostra applicazione e poi avviala:

~/myproject/wsgi.py
from myproject import app

if __name__ == "__main__":
    app.run()

Salva e chiudi il file quando hai finito.

Passaggio 4 — Configurazione di Gunicorn

Ora la tua applicazione è stata scritta con un punto di accesso stabilito. Ora puoi passare alla configurazione di Gunicorn.

Prima di procedere, verifica che Gunicorn possa servire correttamente l’applicazione.

Puoi farlo passandogli il nome del punto di accesso dell’applicazione. Questo è costruito come il nome del modulo (senza l’estensione .py), più il nome della chiamata all’interno dell’applicazione. In questo caso, è wsgi:app.

Specifica anche l’interfaccia e la porta a cui collegarsi usando l’argomento 0.0.0.0:5000 in modo che l’applicazione venga avviata su un’interfaccia disponibile pubblicamente:

  1. cd ~/myproject
  2. gunicorn --bind 0.0.0.0:5000 wsgi:app

Dovresti vedere un output simile al seguente:

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Visita di nuovo l’indirizzo IP del tuo server con :5000 aggiunto alla fine nel tuo browser web:

http://your_server_ip:5000

Dovresti vedere l’output dell’applicazione:

Una volta confermato che funziona correttamente, premi CTRL-C nella finestra del terminale.

Quando hai finito di usare l’ambiente virtuale, puoi disattivarlo:

  1. deactivate

Qualsiasi comando Python userà nuovamente l’ambiente Python di sistema.

Successivamente, crea il file dell’unità di servizio systemd. Creare un file dell’unità di servizio permetterà al sistema init di Ubuntu di avviare automaticamente Gunicorn e servire l’applicazione Flask ogni volta che il server si avvia.

Crea un file di unità che termini con .service all’interno della directory /etc/systemd/system per iniziare:

  1. sudo nano /etc/systemd/system/myproject.service

All’interno, inizia con la sezione [Unit], che viene utilizzata per specificare metadati e dipendenze. Aggiungi una descrizione del tuo servizio qui e indica al sistema init di avviare questo solo dopo che il target di rete è stato raggiunto:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

Successivamente, aggiungi una sezione [Service]. Questo specifica l’utente e il gruppo sotto cui desideri eseguire il processo. Assegna il possesso del processo al tuo account utente normale poiché possiede tutti i file rilevanti. Assegna anche il possesso al gruppo www-data in modo che Nginx possa comunicare facilmente con i processi Gunicorn. Ricorda di sostituire il nome utente qui con il tuo nome utente:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Successivamente, mappa la directory di lavoro e imposta la variabile ambientale PATH in modo che il sistema di inizializzazione sappia che gli eseguibili del processo sono situati all’interno del nostro ambiente virtuale. Specifica anche il comando per avviare il servizio. Questo comando farà quanto segue:

  • Avvia 3 processi worker (anche se dovresti adattare questo se necessario)
  • Crea e collegati a un file socket Unix, myproject.sock, all’interno della nostra directory di progetto. Imposteremo un valore di umask di 007 in modo che il file socket sia creato dando accesso al proprietario e al gruppo, mentre si limita l’accesso ad altri
  • Specifica il nome del file di ingresso WSGI, insieme alla funzione Python all’interno di tale file (wsgi:app)

Systemd richiede che tu fornisca il percorso completo dell’eseguibile Gunicorn, che è installato all’interno del tuo ambiente virtuale.

Ricorda di sostituire il nome utente e i percorsi del progetto con le tue informazioni:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Infine, aggiungi una sezione [Install]. Questo dirà a systemd a cosa collegare questo servizio se lo abiliti per l’avvio automatico. Vorrai che questo servizio si avvii quando il sistema multi-utente regolare è attivo e in esecuzione:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Con questo, il file di servizio di systemd è completo. Salvalo e chiudilo ora.

Ora puoi avviare il servizio Gunicorn che hai creato e abilitarlo in modo che si avvii all’avvio:

  1. sudo systemctl start myproject
  2. sudo systemctl enable myproject

Controlliamo lo stato:

  1. sudo systemctl status myproject

Dovresti vedere un output simile a questo:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2022-05-10 19:40:41 UTC; 9s ago Main PID: 17300 (gunicorn) Tasks: 4 (limit: 2327) Memory: 56.0M CPU: 514ms CGroup: /system.slice/myproject.service ├─17300 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17301 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17302 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─17303 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app May 10 19:40:41 r systemd[1]: Started Gunicorn instance to serve myproject. . . .

Se visualizzi degli errori, assicurati di risolverli prima di continuare con il tutorial.

Passaggio 5 — Configurazione di Nginx per Instradare le Richieste

Il server dell’applicazione Gunicorn dovrebbe essere ora attivo e in esecuzione, in attesa di richieste sul file socket nella directory del progetto. Ora puoi configurare Nginx per inoltrare le richieste web a quel socket apportando alcune piccole modifiche al suo file di configurazione.

Inizia creando un nuovo file di configurazione del blocco server nella directory sites-available di Nginx. Chiamalo myproject per mantenerti in linea con il resto della guida:

  1. sudo nano /etc/nginx/sites-available/myproject

Apri un blocco server e indica a Nginx di ascoltare sulla porta predefinita 80. Indica anche di utilizzare questo blocco per le richieste del nome di dominio del nostro server:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

In seguito, aggiungi un blocco di posizione che corrisponda a ogni richiesta. All’interno di questo blocco, includerai il file proxy_params che specifica alcuni parametri di inoltro generale che devono essere impostati. Successivamente, passerai le richieste al socket che hai definito utilizzando la direttiva proxy_pass:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Salva e chiudi il file quando hai finito.

Per abilitare la configurazione del blocco server di Nginx che hai appena creato, collega il file alla directory sites-enabled:

  1. sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Con il file in quella directory, puoi testare eventuali errori di sintassi:

  1. sudo nginx -t

Se questo ritorna senza indicare problemi, riavvia il processo Nginx per leggere la nuova configurazione:

  1. sudo systemctl restart nginx

Finalmente, aggiusta nuovamente il firewall. Non è più necessario l’accesso tramite la porta 5000, quindi puoi rimuovere quella regola. Puoi quindi consentire l’accesso completo al server Nginx:

  1. sudo ufw delete allow 5000
  2. sudo ufw allow 'Nginx Full'

Ora dovresti essere in grado di navigare sul nome di dominio del tuo server nel browser web:

http://your_domain

Dovresti vedere l’output della tua applicazione:

Nota: Riceverai un errore di gateway HTTP 502 se Nginx non può accedere al file socket di Gunicorn. Di solito questo accade perché la directory home dell’utente non permette ad altri utenti di accedere ai file al suo interno.

Se il tuo file socket si chiama /home/sammy/myproject/myproject.sock, assicurati che /home/sammy abbia almeno le autorizzazioni 0755. Puoi usare uno strumento come chmod per cambiare le autorizzazioni in questo modo:

  1. sudo chmod 755 /home/sammy

Poi ricarica la pagina per vedere se l’errore HTTP 502 scompare.

Se incontri errori, prova a controllare quanto segue:

  • sudo less /var/log/nginx/error.log: controlla i log degli errori di Nginx.
  • sudo less /var/log/nginx/access.log: controlla i log degli accessi di Nginx.
  • sudo journalctl -u nginx: controlla i log del processo Nginx.
  • sudo journalctl -u myproject: controlla i log di Gunicorn della tua applicazione Flask.

Passaggio 6 — Sicurezza dell’Applicazione

Per garantire che il traffico verso il tuo server rimanga sicuro, otteniamo un certificato SSL per il tuo dominio. Ci sono diverse modalità per farlo, tra cui ottenere un certificato gratuito da Let’s Encrypt, generare un certificato autofirmato, o acquistarne uno da un altro fornitore e configurare Nginx per usarlo seguendo i passaggi da 2 a 6 di Come Creare un Certificato SSL Autofirmato per Nginx in Ubuntu 22.04. Utilizzeremo l’opzione uno (Let’s Encrypt) per ragioni di rapidità.

Installa il pacchetto Nginx di Certbot con apt:

  1. sudo apt install python3-certbot-nginx

Certbot fornisce una varietà di modi per ottenere certificati SSL tramite plugin. Il plugin Nginx si occuperà di riconfigurare Nginx e ricaricare la configurazione quando necessario. Per utilizzare questo plugin, digita quanto segue:

  1. sudo certbot --nginx -d your_domain -d www.your_domain

Questo esegue certbot con il plugin --nginx, utilizzando -d per specificare i nomi per cui vogliamo che il certificato sia valido.

Se questa è la prima volta che esegui certbot, ti verrà chiesto di inserire un indirizzo email e accettare i termini del servizio. Dopo averlo fatto, certbot comunicherà con il server Let’s Encrypt, quindi eseguirà una sfida per verificare che tu controlli il dominio per il quale stai richiedendo un certificato.

Se ciò avviene con successo, certbot chiederà come desideri configurare le impostazioni HTTPS:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Seleziona la tua scelta e premi INVIO. La configurazione verrà aggiornata e Nginx si ricaricherà per prendere le nuove impostazioni. certbot concluderà con un messaggio che ti informerà che il processo è stato completato con successo e dove sono memorizzati i tuoi certificati:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Se hai seguito le istruzioni di installazione di Nginx nei prerequisiti, non avrai più bisogno dell’autorizzazione del profilo HTTP ridondante:

  1. sudo ufw delete allow 'Nginx HTTP'

Per verificare la configurazione, naviga nuovamente sul tuo dominio, utilizzando https://:

https://your_domain

Dovresti vedere di nuovo l’output della tua applicazione, insieme all’indicatore di sicurezza del tuo browser, che dovrebbe indicare che il sito è protetto.

Conclusion

In questa guida, hai creato e protetto una semplice applicazione Flask all’interno di un ambiente virtuale Python. Hai creato un punto di ingresso WSGI in modo che qualsiasi server applicativo compatibile con WSGI possa interfacciarsi con esso, e quindi hai configurato il server dell’applicazione Gunicorn per fornire questa funzionalità. Successivamente, hai creato un file di servizio systemd per avviare automaticamente il server dell’applicazione all’avvio. Hai anche creato un blocco server Nginx che instrada il traffico dei client web al server dell’applicazione, inoltrando le richieste esterne e proteggendo il traffico verso il tuo server con Let’s Encrypt.

Flask è un framework molto semplice ma estremamente flessibile pensato per fornire alle tue applicazioni funzionalità senza essere troppo restrittivo per quanto riguarda struttura e design. Puoi utilizzare lo stack generale descritto in questa guida per servire le applicazioni Flask che progetti.

Source:
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04