Come costruire e distribuire un’applicazione Python?
Python è un linguaggio di programmazione libero e open-source che ha molte applicazioni. È stato creato da Guido Van Rossum nel 1991 e da allora si è evoluto in uno dei linguaggi di programmazione più popolari.
In questo articolo parleremo di Python, dei suoi vantaggi, svantaggi, casi d’uso e opzioni di distribuzione. Inoltre, dimostreremo come costruire, dockerizzare e distribuire una semplice applicazione Python su Back4app Containers.
Continuate a leggere per saperne di più su come costruire e distribuire un progetto Python.
Contents
Panoramica su Python
Python è un linguaggio di programmazione generale di alto livello. È interpretato, digitato dinamicamente e può essere utilizzato sia per lo scripting che per la programmazione. Gli sviluppatori di solito descrivono Python come un linguaggio con “batterie incluse”, grazie alla sua ampia libreria standard.
Il linguaggio è apparso per la prima volta nel 1991 e da allora è diventato uno dei linguaggi di programmazione più popolari e amati grazie alla sua sintassi semplice e leggibile. I principali principi di progettazione di Python sono illustrati in The Zen of Python, scritto da Tim Peters.
Python ha un’enorme comunità di sviluppatori e pacchetti pronti all’uso. Al giorno d’oggi Python viene utilizzato praticamente ovunque: nell’amministrazione dei server, nel calcolo scientifico, nell’apprendimento automatico, nella creazione di API RESTful e così via. Python può essere considerato una delle competenze essenziali di uno sviluppatore moderno.
Vantaggi di Python
Facile da usare
Python è uno dei linguaggi più facili da usare e da imparare. La sua curva di apprendimento è quasi inesistente perché la sua filosofia progettuale enfatizza la semplicità e la leggibilità del codice. Python è anche favorito nel campo dell’istruzione, perché è un ottimo strumento per l’apprendimento dei concetti di base della programmazione.
Sviluppo rapido
Un altro grande vantaggio di Python è che consente uno sviluppo rapido. È un linguaggio ideale per la creazione di prototipi e per portare a termine il lavoro in modo rapido. Utilizzando Python non ci si deve preoccupare della sintassi complessa, dell’allocazione della memoria e così via, ma ci si può concentrare sulla propria applicazione.
Versatile
Python è uno dei linguaggi di programmazione più versatili. Viene utilizzato in un’ampia gamma di settori e campi. Alcuni dei campi in cui Python viene utilizzato sono la contabilità, la ricerca scientifica, la scienza dei dati, lo sviluppo web, lo sviluppo di giochi e l’automazione.
Portatile
Python è un linguaggio portatile, poiché è interpretato (come Java) e non compilato (come C++). Ciò significa che il codice scritto su una piattaforma può essere facilmente trasferito su un’altra. Questo rende Python una scelta popolare per gli sviluppatori che devono supportare più piattaforme, tra cui Windows, macOS, Linux e Android.
Ampie biblioteche
Il linguaggio di programmazione Python è supportato da un’enorme comunità di sviluppatori. Esiste una libreria per quasi tutto ciò che si può immaginare. Alcune delle librerie più popolari includono:
- Sviluppo web(Django, Flask, FastAPI)
- Apprendimento automatico e scienza dei dati(TensorFlow, PyTorch)
- Visione artificiale(OpenCV, Pillow, Scikit-image)
- Informatica scientifica(NumPy, SciPy)
- Interfaccia grafica(Qt, GTK)
E questo è solo un piccolo assaggio. Se volete vedere un elenco completo di pacchetti Python fantastici, date un’occhiata a awesome-python su GitHub.
Tipizzati dinamicamente
Python è un linguaggio a tipizzazione dinamica, il che significa che non è necessario dichiarare il tipo di dati quando si creano le variabili. Il tipo di variabile viene assegnato solo durante l’esecuzione del codice.
Per quanto sia fantastico, i linguaggi tipizzati dinamicamente possono talvolta essere un’arma a doppio taglio. Di solito sono più soggetti a errori e hanno un livello inferiore di controllo della memoria rispetto ai linguaggi tipizzati staticamente.
Estensibile
Se i requisiti del progetto includono compiti di calcolo pesanti, è possibile scriverli in linguaggi più veloci, come C o C++, e poi richiamarli dal codice Python.
Per saperne di più sull’invocazione di C/C++ da Python, consultate questo ottimo articolo.
Limitazioni di Python
Cattive prestazioni
Python è relativamente lento rispetto ad altri linguaggi di programmazione. I due fattori che ne ostacolano maggiormente le prestazioni sono il fatto che Python è un linguaggio interpretato e digitato dinamicamente. Se i requisiti del vostro progetto prevedono un’elaborazione pesante o il multi-threading, Python potrebbe non essere lo strumento più adatto. È molto meglio utilizzare C, C++ o un altro linguaggio compilato.
Memoria intensiva
I programmi Python, rispetto a quelli scritti in linguaggi a tipizzazione statica, di solito consumano più memoria e forniscono un livello inferiore di controllo sulla memoria. Questo può essere un po’ problematico se le applicazioni devono essere efficienti dal punto di vista della memoria.
Incline agli errori di runtime
Python, essendo un linguaggio interpretato, è più incline agli errori di runtime. Non essendoci un processo di compilazione, i bug non possono essere scoperti in fase di compilazione. Inoltre, Python è tipizzato dinamicamente, il che significa che gli sviluppatori possono cambiare il tipo di una variabile in qualsiasi momento. Questo può talvolta portare a errori e richiede che gli sviluppatori siano attenti ai tipi di variabile.
Blocco dell’interprete globale (GIL)
Il blocco globale dell’interprete (GIL) in Python è un meccanismo che assicura che solo un thread esegua il bytecode Python alla volta. Se da un lato questo semplifica l’implementazione del linguaggio e fornisce alcuni vantaggi in termini di prestazioni per alcuni tipi di programmi, dall’altro limita la possibilità di utilizzare appieno i processori multi-core nelle applicazioni legate alla CPU.
Non ottimizzato per l’accesso al database
Lavorare con i database nelle applicazioni Python può essere più difficile a causa della mancanza di interfacce potenti e facili da usare come la Java Database Connectivity (JDBC). Sebbene Python possa essere utilizzato per operazioni di database che comportano semplici operazioni di lettura e scrittura, potrebbe non essere l’opzione più adatta per le applicazioni che devono lavorare con database grandi e complessi.
Opzioni di distribuzione di Python
Le applicazioni Python possono essere distribuite su diverse piattaforme cloud. In generale, possiamo suddividerle nelle tre categorie seguenti:
- Infrastruttura come servizio (IaaS)
- Piattaforma come servizio (PaaS)
- Contenitori come servizio (CaaS)
IaaS è il meno astratto e CaaS è il più astratto. C’è anche l’hosting tradizionale, ma sono sicuro che lo conoscete già.
Infrastruttura come servizio (IaaS)
L’Infrastructure as a Service o IaaS è un modello di cloud computing in cui un fornitore di terze parti offre risorse informatiche virtualizzate come server, storage, sistemi operativi e networking su Internet. Le risorse fornite possono essere gestite tramite dashboard avanzate o API di alto livello, offrendo ai clienti il pieno controllo sull’intera infrastruttura.
I principali vantaggi dell’IaaS sono la scalabilità, il risparmio sui costi, l’aumento del supporto, delle prestazioni e della sicurezza. La struttura di pagamento adottata dalla maggior parte dei fornitori di IaaS si basa su un sistema pay-as-you-go, che prevede l’addebito delle spese solo per le risorse utilizzate.
L’IaaS è il modello di cloud computing più flessibile e rimane l’opzione più popolare sin dal suo lancio all’inizio degli anni 2010. Il suo principale svantaggio è che il cliente è completamente responsabile delle sue applicazioni, dei suoi sistemi operativi e dei suoi dati.
Piattaforma come servizio (PaaS)
Platform as a Service (PaaS) è un servizio di cloud computing che fornisce agli utenti un ambiente cloud per creare, gestire e distribuire applicazioni. Il PaaS offre strumenti precostituiti per lo sviluppo, la personalizzazione e il test delle applicazioni. Con il PaaS, gli utenti possono concentrarsi sulla propria applicazione, mentre il fornitore di servizi si occupa dell’infrastruttura sottostante, compresi server, sistemi operativi, software, backup e altro ancora.
I vantaggi dei PaaS includono una maggiore velocità di immissione sul mercato, una maggiore sicurezza, l’economicità, la scalabilità, l’alta disponibilità e, in genere, la necessità di un minor numero di codici. Tuttavia, ci sono alcuni aspetti negativi. I tre principali aspetti negativi sono la mancanza di flessibilità, la mancanza di controllo e il rischio di vendor lock-in. Ciononostante, il PaaS consente agli utenti di creare applicazioni più rapidamente e con meno spese di gestione.
Contenitori come servizio (CaaS)
Containers as a Service (CaaS) è un modello di cloud computing che consente alle organizzazioni di caricare, eseguire, scalare e gestire i propri container attraverso l’uso della tecnologia di virtualizzazione dei container. I fornitori di CaaS eliminano gran parte del lavoro necessario, come l’infrastruttura, i sistemi operativi, il software, i motori di containerizzazione e altro ancora.
Il bello dei container è che una volta che la vostra applicazione è containerizzata potete distribuirla praticamente ovunque e il suo comportamento è garantito allo stesso modo. In caso di necessità, è possibile passare da un fornitore CaaS a un altro. I clienti CaaS sono in genere fatturati per container (in base alle specifiche del container).
Il CaaS è generalmente più costoso rispetto allo IaaS e al PaaS, offre un livello inferiore di flessibilità e controllo e richiede un po’ di lavoro iniziale, ad esempio per la dockerizzazione dell’applicazione e così via. Tuttavia, è uno dei modelli di cloud computing più semplici da utilizzare una volta che l’applicazione è stata containerizzata.
Per saperne di più sui container, consultate Che cosa sono i container nel cloud computing?
Processo di distribuzione di Python
In questa sezione del tutorial, dimostreremo come costruire e distribuire una semplice applicazione Python passo dopo passo. L’applicazione web sarà implementata utilizzando il framework Flask e distribuita su Back4app Containers.
Prerequisiti
- Esperienza con Python e Flask
- Conoscenza di base di Docker
- Python versione 3.8 o successiva e Docker Desktop installato
Che cos’è Back4app Containers?
Back4app Containers è una piattaforma gratuita e open-source che consente di distribuire e scalare le applicazioni utilizzando container distribuiti a livello globale.
Con Back4app Containers, potete concentrarvi sulla creazione del vostro software e sulla sua rapida distribuzione, senza dovervi preoccupare di DevOps.
La piattaforma è strettamente integrata con GitHub ed è dotata di un sistema CI/CD integrato. Utilizzando Back4app Containers potrete mettere online la vostra applicazione in pochi minuti.
Perché utilizzare Back4app Containers?
- Si integra bene con GitHub
- Scalabilità e implementazioni zero-downtime
- Estremamente facile da usare
- Assistenza clienti eccellente
Introduzione al progetto
In questo articolo, costruiremo e distribuiremo una semplice API RESTful che servirà come lista di TODO. L’API web consentirà agli utenti di eseguire operazioni CRUD di base, come l’aggiunta di attività, l’eliminazione di attività, il contrassegno di attività completate e così via.
Lavoreremo prima sul codice sorgente e poi lo distribuiremo in un contenitore Flask di Back4app. L’applicazione web sarà implementata utilizzando il framework web Flask.
Potete saltare la sezione “Codice App” e seguire il vostro progetto Python.
Codice App
Progetto Init
Iniziate creando una cartella dedicata all’applicazione Flask e navigando in essa:
$ mkdir flask-todo
$ cd flask-todo
Creare un ambiente virtuale e attivarlo:
$ python3 -m venv venv && source venv/bin/activate
Quindi, installare Flask tramite pip:
$ (venv) pip install Flask==2.2.2
Per verificare che tutto funzioni, sostituiamo il contenuto di app.py con il seguente:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index_view():
return {
'detail': 'Hello world'
}
Questo codice inizializza Flask e crea un semplice endpoint che restituisce un messaggio.
Avviare il server con:
$ (venv) flask run
Infine, navigare all’indirizzo http://localhost:5000/ nel proprio browser preferito. Dovrebbe apparire un messaggio con scritto Hello world
.
Database
Procediamo con la gestione del database.
Invece di eseguire SQL grezzo, useremo Flask-SQLAlchemy, un semplice ORM di Flask. Per installarlo, eseguire:
$ (venv) pip install Flask-SQLAlchemy
Quindi, sostituire il contenuto di app.py con il seguente:
# app.py
from datetime import datetime
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
app = Flask(__name__)
app.config['SECRET_KEY'] = '5b3ef5s80gl3b217c20fb37044fe4k33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
description = db.Column(db.String(256), nullable=False)
is_done = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.now(), nullable=True)
updated_at = db.Column(db.DateTime, default=None, nullable=True)
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
def __repr__(self):
return f'<Task {self.title}>'
@app.route('/')
def index_view():
return {
'name': 'flask-todo',
'description': 'a simple todo app written in flask',
'version': 1,
}
Questo codice configura Flask e definisce un nuovo modello chiamato Task
. Il modello di task ha un nome
, una descrizione
, is_done
e alcune altre variabili, come created_at
e updated_at
, che verranno aggiornate dinamicamente.
Quindi, creare uno script Python chiamato init_db.py che inizializzi e popoli il database SQLite:
# init_db.py
from app import db, app
from app import Task
with app.app_context():
db.create_all()
if Task.query.count() == 0:
tasks = [
Task(name='Backup the database', description='Make sure to backup the database with all the tables.'),
Task(name='Setup 2FA', description='Setup the two factor authentication to secure your account.'),
Task(name='Malware scan', description='Perform a malware scan.'),
]
for task in tasks:
db.session.add(task)
db.session.commit()
Eseguire lo script dalla riga di comando:
$ (venv) python init_db.py
Si noterà che è stata creata una nuova cartella denominata instance. All’interno della cartella, ci sarà default.db, che è il database predefinito.
API
L’applicazione Flask avrà i seguenti URL:
/api/
restituisce l’elenco di tutti i compiti/api/
/ visualizza o cancella un compito specifico/api/create/
crea un nuovo task/api/toggle/
/ alterna la proprietàis_done
di un’attività specifica
Per implementarli, aggiungete quanto segue alla fine di app.py:
# app.py
@app.route('/api/')
def list_view():
json = [task.as_dict() for task in Task.query.all()]
return jsonify(json)
@app.route('/api/<int:task_id>/', methods=['GET', 'DELETE'])
def detail_view(task_id):
task = db.get_or_404(Task, task_id)
if request.method == 'DELETE':
db.session.delete(task)
db.session.commit()
return {
'detail': 'Task has been successfully deleted.'
}
else:
return task.as_dict()
@app.route('/api/create/', methods=['POST'])
def create_view():
name = request.form.get('name')
description = request.form.get('name')
if name is None or description is None:
return {
'detail': 'Please provide the name and the description.'
}, 400
task = Task(name=name, description=description)
db.session.add(task)
db.session.commit()
return task.as_dict()
@app.route('/api/toggle/<int:task_id>/')
def toggle_view(task_id):
task = db.get_or_404(Task, task_id)
if task.is_done:
task.is_done = False
else:
task.is_done = True
task.updated_at = datetime.now()
db.session.commit()
return task.as_dict()
Questo codice è abbastanza autoesplicativo. Abbiamo definito le rotte necessarie e implementato la logica richiesta. Ogni volta che vogliamo apportare modifiche alla base dati, dobbiamo eseguire il commit()
.
Questo è tutto per l’applicazione Flask. Nelle prossime sezioni prepareremo il progetto per la distribuzione.
Gunicorno
Il server web di Flask non è raccomandato in produzione, poiché è progettato per gestire una richiesta alla volta e potrebbe non essere in grado di gestire grandi quantità di traffico. Per questo motivo, sostituiamolo con Gunicorn, un server Python WSGI pronto per la produzione.
Per prima cosa, installatelo tramite pip:
$ (venv) pip install gunicorn==20.1.0
Dopo l’installazione è possibile avviare il server in questo modo:
$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app
[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 3
[INFO] Booting worker with pid: 4
Tenete presente che questo comando funziona solo sui sistemi operativi basati su UNIX.
Questo avvierà due worker Gunicorn ed esporrà la vostra applicazione sulla porta 5000
. Per accedere all’applicazione, aprire il browser web preferito e navigare verso http://localhost:5000.
requisiti.txt
L’ultima cosa da fare prima di Dockerizzare la nostra applicazione è creare un file requirements.txt. Il file requirements.txt serve a specificare le dipendenze del progetto.
Generarlo eseguendo il seguente comando:
$ (venv) pip freeze > requirements.txt
Altri (compresi i contenitori Docker) possono quindi utilizzare il file requirements.txt in questo modo:
$ (venv) pip install -r requirements.txt
App Dockerize
Per dockerizzare l’applicazione useremo un Dockerfile. I file Docker sono usati per definire le istruzioni per la creazione di immagini Docker. Consentono di impostare l’immagine di base, la directory di lavoro, le variabili d’ambiente, l’esecuzione dei comandi e altro ancora.
.dockerignore
Prima di lavorare sul file Docker, creiamo un file .dockerignore. Un file .dockerignore viene usato per specificare quali cartelle e file devono essere omessi dall’immagine.
.git/
.idea/
instance/
__pycache__/
Assicurarsi di modificare il file .dockerignore in base alle proprie esigenze.
Profilo Docker
Creare un nuovo file nella radice del progetto chiamato Dockerfile con i seguenti contenuti:
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine
WORKDIR /app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt
COPY . .
# initialize the database (create DB, tables, populate)
RUN python init_db.py
EXPOSE 5000/tcp
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]
Questo file Docker utilizza python:3.10-alpine
come immagine di base. Quindi imposta la directory di lavoro, installa i requisiti, copia il progetto, inizializza il database e infine avvia il server Gunicorn sulla porta 5000
.
Per saperne di più sui file Docker, consultare ilriferimento ai file Docker .
Test
Per verificare che il file Docker funzioni, possiamo costruirlo ed eseguirlo localmente. Per costruire l’immagine eseguire:
$ docker build -t flask-todo:1.0 .
Se si elencano le immagini, si dovrebbe vedere una nuova immagine:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-todo 1.0 7ege66240eb1 3 hours ago 109MB
Infine, utilizzare l’immagine per avviare un nuovo contenitore Docker:
$ docker run -it -p 5000:5000 flask-todo:1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 3
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 4
È possibile usare
-d
per avviare il contenitore Docker in modalità distaccata. Ciò significa che il contenitore viene eseguito in background nel terminale e non riceve input né visualizza output.
Bene, la vostra applicazione è ora in esecuzione in un contenitore. Navigate su http://localhost:5000 nel vostro browser preferito per vedere la vostra applicazione web in azione.
{
"name": "flask-todo",
"description": "a simple todo app written in flask",
"version": 1
}
Spingere su GitHub
I passaggi seguenti richiedono un account GitHub. Se non ne avete ancora uno, iscrivetevi e fate il login. Inoltre, assicuratevi di avere Git installato e configurato.
Una volta effettuato l’accesso a GitHub, utilizzare il pulsante “più” in alto a destra per aprire il menu a tendina. Quindi, selezionare “Nuovo repository”:
Scegliere un nome personalizzato per il repository. Io sceglierò “flask-todo”, quindi fare clic su “Crea repository”:
Una volta creato il repository, prendere nota dell’URL remoto:
Ora, torniamo al nostro progetto locale e facciamo il push del codice.
Poiché abbiamo alcuni file che non vogliamo inviare a Git, creiamo un file .gitignore nella root del progetto. Vi aggiungerò quanto segue, ma sentitevi liberi di modificarlo in base alle vostre esigenze:
instance/*
!instance/.gitignore
.webassets-cache
.env
__pycache__/
*.py[cod]
*$py.class
Quindi, aprire la riga di comando ed eseguire i seguenti comandi:
$ git init
$ git remote add origin <your_remote_url>
$ git add .
$ git commit -m "init"
$ git push origin master
Questo inizializzerà un nuovo repository Git, aggiungerà l’origine remota, farà il VCS di tutti i file e creerà il commit iniziale. Infine, si spingerà il codice sorgente sul repository GitHub.
Bene, dovrebbe essere tutto a posto. Se ora si naviga nel repository nel browser, si dovrebbe essere in grado di vedere che tutti i file sono stati impegnati.
Distribuire l’applicazione
Per eseguire i passaggi seguenti è necessario disporre di un account Back4app. Se ne avete già uno, effettuate il login, altrimenti procedete con la registrazione dell’account gratuito.
Una volta effettuato l’accesso, si verrà reindirizzati alla dashboard della propria app. Fate clic sul pulsante “Crea nuova app” per avviare il processo di creazione dell’app.
Back4app consente di costruire e distribuire due tipi di applicazioni, Backend as a Service (BaaS) e Containers as a Service (CaaS). Poiché vogliamo distribuire un’applicazione dockerizzata, sceglieremo “Container as a Service”.
Quindi, procedere alla connessione di GitHub con il proprio account Back4app. Assicuratevi di dare a Back4app i permessi per il repository che abbiamo creato nel passaggio precedente. Quindi fare clic sul pulsante verde “Seleziona” per selezionarlo.
Back4app Containers consente di configurare il processo di distribuzione. È possibile impostare il ramo predefinito, la directory principale, abilitare/disabilitare la distribuzione automatica e impostare le variabili d’ambiente. Non abbiamo bisogno di nulla di tutto ciò, quindi diamo un nome alla nostra applicazione e facciamo clic su “Crea applicazione”.
Back4app impiegherà qualche istante per costruire il contenitore, caricarlo nel registro dei contenitori ed eseguirlo. Una volta che il contenitore è pronto, lo stato cambierà in “Pronto” e si potrà vedere un URL verde sul lato sinistro dello schermo.
Facendo clic sull’URL si aprirà l’applicazione web nel browser. Noterete che Back4app ha rilasciato automaticamente un certificato SSL per la vostra applicazione e vi permette di ospitare gratuitamente un’applicazione Python.
Conclusione
In questo articolo avete imparato a conoscere Python, i suoi vantaggi, gli svantaggi e le opzioni di distribuzione. Avete anche distribuito con successo una semplice API RESTful in Python. Ora dovreste essere in grado di creare le vostre semplici API e di distribuirle su Back4app Containers.
Il codice sorgente finale è disponibile su GitHub.
Passi futuri
- Al momento tutti i dati vengono cancellati a ogni riallocazione. Questo perché il database è incluso nell’immagine Docker. Considerare il passaggio a un’istanza di database gestita.
- Esaminate le build in più fasi per accelerare il processo di distribuzione.