La maggior parte dei progetti di sviluppo coinvolge un ampio ventaglio di ambienti. Ci sono gli ambienti di produzione, di sviluppo, QA, staging e poi gli ambienti locali di ogni sviluppatore. Mantenere questi ambienti sincronizzati in modo che il progetto venga eseguito allo stesso modo (o che venga semplicemente eseguito) in ogni ambiente può essere una bella sfida.
I motivi di una possibile incompatibilità sono molti, ma l’uso di Docker ti aiuterà a eliminarne la maggior parte e in questo articolo spiegheremo come containerizzare un’applicazione e analizzeremo Docker, i container di applicazioni e come l’uso di questi strumenti possa aiutarti a eseguire la tua applicazione ovunque sia possibile installare Docker, evitando così problemi di compatibilità.
Che cos’è un container di applicazioni?
Un container di applicazioni è un pacchetto leggero e indipendente che include un’applicazione e tutte le sue dipendenze, comprese le librerie, gli ambienti di esecuzione e i file di configurazione. Un container contiene, in un ambiente isolato, tutto ciò che serve all’applicazione per funzionare in modo efficiente. Sono progettati per funzionare su qualsiasi sistema operativo e hardware compatibile, indipendentemente dalle differenze delle distribuzioni dei sistemi operativi (OS), e condividendo il kernel del sistema operativo dell’host. Ciò consente di eseguire più applicazioni o servizi isolati su un singolo host senza la necessità di un sistema operativo completo per ciascuna applicazione.
I container di applicazioni differiscono dalla virtualizzazione tradizionale in termini di l’allocazione delle risorse, sistema operativo, isolamento e scalabilità. I container condividono risorse come CPU, memoria e storage a livello di sistema operativo, consumando meno risorse rispetto alle virtual machine (VM) che richiedono un sistema operativo completo per ogni istanza. Per questo motivo, i container si avviano in pochi secondi, a differenza delle virtual machine che possono richiedere minuti. Questa caratteristica rende i container più scalabili delle virtual machine. Un aspetto in cui le macchine virtuali brillano, tuttavia, è che forniscono un isolamento molto più forte tra le istanze rispetto ai container, perché ogni macchina virtuale esegue il proprio sistema operativo.
Perché containerizzare le applicazioni?
La containerizzazione delle applicazioni offre una grande quantità di vantaggi che rendono più facili da gestire alcune delle sfide dello sviluppo, della distribuzione e delle operazioni del software. Tra i principali motivi per cui è necessario containerizzare un’applicazione vi sono:
- Portabilità: La containerizzazione fornisce un ambiente di runtime coerente mettendo un’applicazione in un pacchetto insieme alle sue dipendenze e alla sua configurazione. Questo garantisce un comportamento coerente sia che il container sia in esecuzione sul computer di uno sviluppatore sia su una piattaforma in cloud.
- Scalabilità: I container consentono di distribuire, applicare patch e scalare rapidamente le applicazioni. Gli orchestratori di container possono eseguire uno scaling intelligente, eseguendo la giusta quantità di istanze applicative necessarie per i carichi applicativi utilizzati, tenendo conto delle risorse disponibili.
- Produttività: I container offrono un ambiente prevedibile e coerente, indipendentemente dal luogo in cui vengono eseguiti. Invece di impostare manualmente ogni ambiente, gli sviluppatori possono di solito eseguire un singolo comando per avviare un container, che costruirà e avvierà l’ambiente per loro.
- Flessibilità: L’efficienza dei container consente di suddividere facilmente un’applicazione in microservizi più piccoli e distribuibili, senza il sovraccarico di risorse dovuto all’esecuzione di più server fisici o di virtual machine.
Introduzione a Docker
Docker è stato lanciato nel 2013 come piattaforma open-source che semplifica il processo di creazione, esecuzione, gestione e distribuzione delle applicazioni grazie alla containerizzazione. Per molti team di sviluppo, si tratta di uno strumento fondamentale nei loro flussi di lavoro legati allo sviluppo e alla distribuzione del software, che consente agli sviluppatori di mettere le applicazioni in pacchetti con le loro dipendenze, utilizzando container leggeri e trasferibili facilmente.
Diamo una rapida occhiata ad alcuni componenti chiave di Docker per avere un’idea del suo funzionamento.
Dockerfile
Un Dockerfile è un file di testo che contiene le istruzioni per costruire un’immagine Docker. Specifica quale immagine Docker utilizzare come base, la posizione del codice sorgente dell’applicazione che verrà fornito con l’immagine, le librerie, i pacchetti e le altre dipendenze necessarie per l’esecuzione dell’applicazione. Docker legge questo file ed esegue le istruzioni in esso contenute per costruire l’immagine Docker.
Immagini Docker
Un’immagine Docker è un modello read-only che contiene il codice sorgente e le dipendenze dell’applicazione e che serve come base per i container Docker. Le immagini Docker vengono create con un Dockerfile e possono essere sottoposte al controllo delle versioni (versioning), archiviate in registri e condivise con altri. Le immagini Docker utilizzano un’architettura a strati, dove ogni strato rappresenta una modifica all’immagine. Poiché la stratificazione è legata direttamente a ogni comando in un file Docker, ogni istruzione può produrre un nuovo strato
Container Docker
Un container Docker è un’istanza in esecuzione di un’immagine Docker. Come altri container, vengono eseguiti in ambienti isolati su un sistema host e sono leggeri, si avviano rapidamente e garantiscono un comportamento coerente indipendentemente dal sistema host su cui vengono eseguiti.
Come containerizzare un’applicazione utilizzando Docker
È possibile containerizzare molti tipi di applicazioni, comprese le applicazioni frontend e backend. Più avanti vedremo come è possibile containerizzare una semplice applicazione web sviluppata con Python Flask. Sebbene esistano altri linguaggi e applicazioni che possono essere organizzati in pacchetti con Docker, per i nostri esempi utilizzeremo Python Flask.
Prerequisiti per la containerizzazione tramite Docker
Poiché la containerizzazione di un’applicazione inserisce tutto il suo codice e le sue dipendenze in un container, non ci sono molti requisiti, a parte i seguenti:
- Devi avere installato Docker. Puoi trovare i pacchetti di installazione per Linux, Mac o Windows qui.
- Crea una cartella di progetto per contenere tutti i file dell’applicazione. Nell’esempio che segue, utilizzeremo solo tre file.
- Potresti voler installare i linguaggi di programmazione e le librerie utilizzate in locale per testare l’applicazione prima di containerizzarla. Tuttavia, ciò non è del tutto necessario, poiché l’installazione avverrà nel container dell’applicazione e Docker si occuperà di questa parte.
Comprendere il codice dell’applicazione e le sue dipendenze
Per il nostro esempio, creeremo una semplice applicazione in Python Flask. Ecco come apparirà la cartella del progetto:
my_flask_app/ - app.py - requirements.txt
La nostra applicazione è composta da due file: app.py, che contiene il codice dell’applicazione web, e requirements.txt, un file che Python usa per installare le dipendenze. Ecco il contenuto di app.py con alcuni commenti che spiegano il ruolo di ogni sua parte:
# Importazione del modulo Python Flask from flask import Flask # Il costruttore di Flask utilizza il nome del modulo corrente app = Flask(__name__) # La funzione route() è un decorator, # Lega un URL a una funzione associata @app.route('/') def hello_world(): return 'Hello World' # Se il file viene eseguito direttamente # Eseguire l'applicazione Flask if __name__ == '__main__': app.run(debug=True, host='0.0.0.0')
Non c’è molto nel filerequirements.txt. Semplicemente elenca Flask come dipendenza:
Flask==2.3.2
Guida passo per passo alla containerizzazione di un’applicazione
Ora che abbiamo un’applicazione da containerizzare, il passo successivo è la creazione di un Dockerfile che contenga le istruzioni per la creazione dell’immagine Docker.
Creare il Dockerfile
Per prima cosa, crea un Dockerfile vuoto nel progetto. Questo file non ha estensione. La cartella del progetto dovrebbe avere questo aspetto:
my_flask_app/ - app.py - Dockerfile - requirements.txt
Ecco il contenuto del file Dockerfile:
FROM python:3.10 COPY . /app WORKDIR /app RUN pip install -r requirements.txt EXPOSE 5000 CMD ["python3", "app.py"]
Ecco cosa significa tutto questo:
- FROM: Definisce il container che useremo come container di base. Puoi trovare un’ampia selezione di container di base con cui iniziare su Docker Hub.
- COPY: Quando il container viene realizzato, i file del nostro progetto, rappresentati dal punto, saranno copiati in una cartella /app all’interno del container.
- WORKDIR: Questo indica a Docker dove eseguire il comando in fondo al nostro file, che è la cartella in cui abbiamo copiato il nostro progetto.
- RUN: L’istruzione RUN indica a Docker un comando da eseguire durante la creazione del container. Questo comando viene eseguito durante la creazione dell’immagine, non all’avvio del container. Qui è dove chiediamo di installare le dipendenze presenti nel file requirements.txt.
- EXPOSE: Questo indica a Docker la porta su cui il container dovrà essere in ascolto, che è la porta su cui verrà eseguita la nostra applicazione Flask.
- CMD: Questo indica a Docker i comandi che verranno eseguiti all’avvio del container e che eseguiranno l’applicazione Flask. L’istruzione CMD fornisce le impostazioni predefinite per l’esecuzione di un container e può essere sovrascritta durante l’esecuzione di Docker.
Creare l’immagine Docker
Ora che abbiamo creato il Dockerfile possiamo creare l’immagine Docker eseguendo questo comando nella nostra cartella del progetto:
docker build -t flask-app .
Nel comando, -t sta per tag e contrassegna l’immagine con un nome, flask-app è il nome che daremo all’immagine e l’opzione . definisce il contesto di compilazione, che sarà la cartella corrente. Una volta eseguito il comando, Docker costruirà la vostra immagine e vedrete un output come questo:
Esecuzione del container Docker
Ora che l’immagine Docker è stata creata, è possibile eseguire il container con il seguente comando nella cartella del progetto:
docker run -it -p 5000:5000 flask-app
In questo comando, -it indica a Docker di eseguire il container in modalità interattiva, il che significa che sarà possibile interagire con la shell del container e assegnare una tty al container, che ti fornirà una console basata su testo. Tuttavia, l’uso di -it quando si esegue l’applicazione Flask non è sempre necessario, poiché non si tratta di uno strumento a riga di comando che richiede un terminale interattivo. Il flag -p specifica la mappatura delle porte del container. Il primo 5000 è la porta del computer host che si vuole mappare sulla porta del container. Il secondo 5000 è la porta del container. E flask-app è il nome dell’immagine che abbiamo appena creato e che vogliamo eseguire.
L’output nel terminale dopo aver eseguito il comando sarà simile a questo:
E quando andrai su http://localhost:5000/ potrai vedere l’applicazione Flask di base.
Best practice per la containerizzazione con Docker
Sebbene la containerizzazione di un’applicazione con Docker offra innumerevoli vantaggi per lo sviluppo e la distribuzione delle applicazioni, è importante seguire le best practice per trarne vantaggio. Ecco alcune regole importanti da seguire:
- Utilizza un’immagine di base minima: Scegli l’immagine di base più leggera possibile, come Alpine Linux, per ridurre le dimensioni dell’immagine finale, diminuire il consumo di risorse e velocizzare la creazione.
- Utilizza costruzioni a più fasi: Costruisci e compila in un’unica fase, quindi copia solo gli artefatti necessari nella fase finale per ridurre le dimensioni dell’immagine finale.
- Riduci al minimo i livelli: Riduci il numero di livelli dell’immagine combinando i comandi in un’unica operazione RUN perché ogni livello aggiunge overhead.
- Rimuovi i file non necessari: Rimuovi i file temporanei, le cache e gli artefatti di compilazione dopo l’installazione delle dipendenze o la creazione dell’applicazione, per ridurre le dimensioni dell’immagine.
- Esegui container stateless: Memorizza i dati persistenti al di fuori del container in database, object storage o in un altro sistema di archiviazione esterno, in modo da poter interrompere il container senza perdere i dati.
- Tagga le tue immagini: I tag aiutano a gestire le versioni delle tue immagini. L’uso di tag unici per le distribuzioni consente di scalare rapidamente un cluster di produzione a molti nodi.
- Applica le patch ai tuoi container: Il patching e l’aggiornamento dei container è un modo proattivo per migliorare la sicurezza e ridurre le minacce.
Docker: Uno strumento essenziale per lo sviluppo moderno
La containerizzazione con Docker ha rivoluzionato lo sviluppo delle applicazioni. Le applicazioni containerizzate offrono notevoli vantaggi, tra cui portabilità, scalabilità, produttività, efficienza e flessibilità. Docker ha reso semplice questo processo fornendo un approccio alla containerizzazione standardizzato e facile da usare.
L’adozione di Docker, se accompagnata dall’uso delle best practice per la containerizzazione, può migliorare lo sviluppo, la distribuzione e la gestione delle applicazioni moderne. Fornendo un ambiente di runtime coerente, i container consentono agli sviluppatori di sviluppare applicazioni una sola volta e di eseguirle ovunque, semplificando il processo in vari ambienti. La scalabilità dei container consente una rapida distribuzione e la possibilità di riduzione delle applicazioni in microservizi più piccoli e distribuibili.
Docker, piattaforma leader per la containerizzazione, semplifica il processo di creazione, esecuzione, gestione e distribuzione di applicazioni containerizzate, imponendosi così come uno strumento essenziale per lo sviluppo di software moderno.