Docker per Principianti: Una Guida Pratica ai Contenitori

Quando ho iniziato a usare Docker, ho rapidamente realizzato quanto fosse potente. Immagina di configurare il tuo ambiente di sviluppo in pochi minuti invece che in ore o di eseguire applicazioni su macchine diverse senza il classico problema “funziona sulla mia macchina”.

Docker semplifica il modo in cui costruiamo, distribuiamo ed eseguiamo applicazioni imballandole in contenitori leggeri e portatili. Che tu sia uno sviluppatore, un data scientist o un amministratore di sistema, padroneggiare Docker può farti risparmiare mal di testa e rendere i tuoi flussi di lavoro più efficienti.

In questo tutorial, ti guiderò attraverso le basi: installare Docker, comprendere i concetti chiave e eseguire la tua prima applicazione containerizzata. Alla fine, non solo saprai come funziona Docker, ma avrai anche esperienza pratica nell’utilizzarlo, creando una solida base per argomenti più avanzati. Iniziamo!

Che cos’è Docker?

Docker è una piattaforma di containerizzazione open-source che semplifica il deployment delle applicazioni imballando il software e le sue dipendenze in un’unità standardizzata chiamata contenitore. Rispetto alle tradizionali macchine virtuali, i contenitori Docker condividono il kernel del sistema operativo host, rendendoli più efficienti e leggeri.

I contenitori garantiscono che un’applicazione funzioni allo stesso modo negli ambienti di sviluppo, test e produzione. Questo riduce i problemi di compatibilità e migliora la portabilità su varie piattaforme. Grazie alla sua flessibilità e scalabilità, Docker è diventato uno strumento cruciale nei moderni flussi di lavoro DevOps e di sviluppo cloud-native.

Logo ufficiale di Docker.

Installare Docker

Docker può essere installato su vari sistemi operativi, tra cui Windows, macOS e Linux. Mentre la funzionalità principale rimane la stessa su tutte le piattaforme, il processo di installazione varia leggermente a seconda del sistema. Di seguito troverai istruzioni passo dopo passo per installare Docker sul tuo sistema operativo preferito.

Installare Docker su Windows

  1. Scarica Docker Desktop per Windows.

Scarica l’installer di Docker Desktop per Windows

  1. Esegui l’installatore e segui le istruzioni di configurazione.

Installazione di Docker Desktop per Windows

  1. Abilita l’integrazione WSL 2 se richiesto.
  2. Verifica l’installazione eseguendo docker –version in PowerShell.

Controllo della versione di Docker dopo l’installazione tramite Powershell

5. Avvia l’app Docker Desktop dal menu di esecuzione.

Avvio dell’applicazione Docker Desktop su Windows

Installazione di Docker su macOS

  1. Scarica Docker Desktop per Mac.

Scarica l’installatore di Docker Desktop per Mac

  1. Apri il file .dmg scaricato e trascina Docker nella cartella Applicazioni.
  2. Avvia Docker e completa la configurazione.
  3. Verifica l’installazione utilizzando docker –version nel terminale.

Installazione di Docker su Linux (Ubuntu)

  1. Aggiornare le liste dei pacchetti: sudo apt update 
  2. Installare le dipendenze: sudo apt install apt-transport-https ca-certificates curl software-properties-common 
  3. Aggiungere la chiave GPG ufficiale di Docker: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 
  4. Aggiungere il repository di Docker: sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 
  5. Installare Docker: sudo apt install docker-ce  
  6. Verificare l’installazione: docker –version

Concetti di base di Docker

Ora che hai Docker installato, potresti essere impaziente di iniziare subito ad eseguire container. Ma prima di farlo, è importante comprendere alcuni concetti chiave che costituiscono le fondamenta del lavoro di Docker. Questi concetti ti aiuteranno a navigare in Docker in modo più efficace e a evitare errori comuni dei principianti.

Al centro di Docker ci sono immagini, che fungono da progetti per i container; container, che sono le istanze in esecuzione di queste immagini; e Docker Hub, un repository centralizzato per condividere e gestire le immagini.

Esploriamo ciascuno di questi concetti in dettaglio.

Immagini Docker

Le immagini Docker sono i mattoni fondamentali dei contenitori. Sono immutabili, modelli di sola lettura che contengono tutto il necessario per eseguire un’applicazione, inclusi il sistema operativo, il codice dell’applicazione, il runtime e le dipendenze.

Le immagini vengono costruite utilizzando un Dockerfile, che definisce le istruzioni per creare un’immagine strato per strato.

Le immagini possono essere memorizzate e recuperate da registri di contenitori come Docker Hub.

Ecco alcuni comandi di esempio per lavorare con le immagini:

  • docker pull nginx: Recupera l’ultima immagine Nginx da Docker Hub.
  • docker images: Elenca tutte le immagini disponibili sulla macchina locale.
  • docker rmi nginx: Rimuove un’immagine dalla macchina locale.

Contenitori Docker

Un contenitore Docker è un’istanza in esecuzione di un’immagine Docker. I contenitori forniscono un ambiente di runtime isolato in cui le applicazioni possono essere eseguite senza interferire l’una con l’altra o con il sistema host. Ogni contenitore ha il proprio filesystem, rete e spazio di processo, ma condivide il kernel dell’host.

I contenitori seguono un ciclo di vita semplice che prevede creazione, avvio, arresto e eliminazione. Ecco una panoramica dei comandi comuni di gestione dei contenitori:

  1. Creare un contenitore: docker create o docker run
  2. Avviare un contenitore: docker start
  3. Fermare un contenitore: docker stop
  4. Riavviare un contenitore: docker restart
  5. Cancellare un contenitore: docker rm

Vediamo un esempio pratico. Il seguente comando esegue un contenitore Nginx in modalità staccata (in esecuzione in background), mappando la porta 80 all’interno del contenitore alla porta 8080 sulla macchina host:

docker run -d -p 8080:80 nginx

Dopo aver eseguito questo comando, Docker scaricherà l’immagine Nginx (se non già disponibile), creerà un contenitore e lo avvierà.

Per controllare tutti i contenitori in esecuzione e fermi:

docker ps -a

Questo mostrerà un elenco di tutti i contenitori e dettagli come il loro stato e le porte assegnate.

Docker Hub

Docker Hub è un servizio di registro basato su cloud per trovare, memorizzare e distribuire immagini dei contenitori. Gli utenti possono caricare immagini personalizzate su Docker Hub e condividerle pubblicamente o privatamente.

Ecco alcuni comandi per interagire con Docker Hub:

  • docker login: Autenticarsi su Docker Hub.
  • docker push my-image: Caricare un’immagine personalizzata su Docker Hub.
  • docker search ubuntu: Cercare immagini ufficiali e della community.
  • docker pull ubuntu: Scaricare un’immagine di Ubuntu da Docker Hub.

Se sei nuovo alla containerizzazione, ottieni una solida base con il corso Concetti di Containerizzazione e Virtualizzazione.

Esecuzione del tuo primo contenitore Docker

Ora che abbiamo coperto i concetti principali di Docker, è il momento di metterli in pratica! Cominciamo eseguendo il nostro primo contenitore per assicurarci che Docker sia installato correttamente e funzioni come previsto.

Per testare la tua installazione di Docker, apri PowerShell (Windows) o Terminal (Mac e Linux) e esegui:

docker run hello-world

Questo scarica l’immagine hello-world da DockerHub e la esegue in un contenitore.

Esempio di immagine hello-world di Docker

Ora, andiamo un passo avanti ed eseguiamo un’applicazione del mondo reale: un server web Nginx. Esegui il seguente comando:

docker run -d -p 8080:80 nginx

Il comando sopra esegue le seguenti azioni:

  • Il flag -d esegue il container in modalità staccata, il che significa che viene eseguito in background.
  • Il flag -p 8080:80 mappa la porta 80 all’interno del container alla porta 8080 sul tuo computer locale, consentendoti di accedere al server web.

Una volta che il comando viene eseguito con successo, apri un browser e visita: http://localhost:8080

Accesso al server web su localhost:8080

Dovresti vedere la pagina di benvenuto predefinita di Nginx, confermando che il tuo server web è in esecuzione all’interno di un container!

Vedrai anche un container in esecuzione nella tua Docker Desktop:

Container Nginx in esecuzione sulla porta 8080

Costruzione della tua prima immagine Docker

Fino ad ora, abbiamo utilizzato immagini pre-costruite da Docker Hub. Ma cosa succede se hai bisogno di un ambiente personalizzato adattato alla tua applicazione? Ecco dove entra in gioco la creazione della propria immagine Docker.

Creare un’immagine Docker implica la scrittura di un Dockerfile, uno script che automatizza la creazione dell’immagine. Questo garantisce coerenza e portabilità tra diversi ambienti. Una volta che un’immagine è stata costruita, può essere eseguita come un container per eseguire applicazioni in un ambiente isolato.

In questa sezione, impareremo i fondamenti della scrittura di un Dockerfile, della costruzione di un’immagine personalizzata e della sua esecuzione come container.

Principi base del Dockerfile

Un Dockerfile è uno script contenente una serie di istruzioni che definiscono come viene costruita un’immagine Docker. Automatizza il processo di creazione dell’immagine, garantendo coerenza tra gli ambienti. Ogni istruzione in un Dockerfile crea uno nuovo strato nell’immagine. Ecco una scomposizione di un esempio di Dockerfile per una semplice app Python Flask:

# Immagine di base contenente l'ambiente di esecuzione Python FROM python:3.9 # Imposta la directory di lavoro all'interno del container WORKDIR /app # Copia i file dell'applicazione dall'host al container COPY . /app # Installa le dipendenze elencate in requirements.txt RUN pip install -r requirements.txt # Definisce il comando per eseguire l'app Flask quando il container parte CMD ["python", "app.py"]

Nel comando precedente:

  • -v my-volume:/app/data monta lo storage my-volume nella directory /app/data all’interno del container.
  • Qualsiasi dato memorizzato in /app/data persiste anche se il container si arresta o viene rimosso.

Scomponendo il Dockerfile sopra:

  • FROM python:3.9: Specifica l’immagine di base con Python 3.9 pre-installato.
  • WORKDIR /app: Imposta /app come directory di lavoro all’interno del container.
  • COPY . /app: Copia tutti i file dalla directory corrente dell’host a /app nel container.
  • RUN pip install -r requirements.txt: Installa tutte le dipendenze richieste all’interno del container.
  • CMD ["python", "app.py"]: Definisce il comando da eseguire quando il container viene avviato.

Costruzione ed esecuzione dell’immagine

Una volta definito il Dockerfile, puoi creare ed eseguire l’immagine usando i seguenti comandi:

Passo 1: Creare l’immagine

docker build -t my-flask-app .

Il comando sopra:

  • Utilizza la directory corrente (.) come contesto di creazione.
  • Legge il Dockerfile ed esegue le sue istruzioni.
  • Etichetta (-t) l’immagine risultante come my-flask-app.

Passo 2: Esegui l’immagine come un container

docker run -d -p 5000:5000 my-flask-app

Il comando sopra:

  • Esegue il contenitore in modalità staccata (-d).
  • Mappa la porta 5000 all’interno del contenitore alla porta 5000 sull’host (-p 5000:5000).

Una volta in esecuzione, puoi accedere all’applicazione Flask navigando su http://localhost:5000 in un browser.

Volumi Docker e Persistenza

Per impostazione predefinita, i dati all’interno di un contenitore Docker sono temporanei—una volta che il contenitore si ferma o viene rimosso, i dati scompaiono. Per mantenere i dati tra i riavvii dei contenitori e condividerli tra più contenitori, Docker fornisce volumi, un meccanismo integrato per gestire in modo efficiente lo storage persistente.

Rispetto all’archiviazione dei dati all’interno del filesystem del contenitore, i volumi sono gestiti separatamente da Docker, rendendoli più efficienti, flessibili e più facili da eseguire il backup.

Nella prossima sezione, esploreremo come creare e utilizzare i volumi Docker per garantire la persistenza dei dati nei tuoi contenitori.

Creare e utilizzare i volumi Docker

Passo 1: Creare un volume

Prima di utilizzare un volume, dobbiamo crearne uno. Esegui il seguente comando:

docker volume create my-volume

Questo crea un volume nominato chiamato my-volume, che Docker gestirà separatamente da qualsiasi contenitore specifico.Passo 2: Utilizzare il volume in un contenitore

Ora, iniziamo creando un contenitore e montiamo il volume al suo interno:

docker run -d -v my-volume:/app/data my-app

Nel comando sopra:

  • -v my-volume:/app/data monta lo storage my-volume nella directory /app/data all’interno del contenitore.
  • Tutti i dati memorizzati in /app/data persistono anche se il contenitore si interrompe o viene rimosso.

Docker Compose per Applicazioni Multi-Contenitore

Fino ad ora, abbiamo lavorato con applicazioni a singolo contenitore, ma molte applicazioni del mondo reale richiedono più contenitori che lavorino insieme. Ad esempio, un’applicazione web potrebbe avere bisogno di un server backend, di un database e di uno strato di caching, ognuno in esecuzione nel proprio contenitore. Gestire manualmente questi contenitori con comandi docker run separati può diventare rapidamente noioso.

Ecco dove entra in gioco Docker Compose.

Cos’è Docker Compose?

Docker Compose è uno strumento che semplifica la gestione delle applicazioni multi-container. Invece di eseguire più comandi docker run, è possibile definire l’intero stack dell’applicazione utilizzando un file docker-compose.yml e distribuirlo con un singolo comando.

Scrittura di un file Docker Compose

Adesso, creiamo un esempio concreto: una semplice applicazione Node.js che si collega a un database MongoDB. Invece di gestire i due container separatamente, li definiremo in un file docker-compose.yml.

Ecco come definiamo la nostra configurazione multi-container in Docker Compose:

version: '3' services: web: build: . ports: - "3000:3000" depends_on: - database database: image: mongo volumes: - db-data:/data/db volumes: db-data:

Scomponendo il file sopra:

  • version: '3': Specifica la versione di Docker Compose.
  • services:: Definisce i servizi individuali (container).
  • web:: Definisce l’applicazione web Node.js.
  • database:: Definisce il container del database MongoDB.
  • volumes:: Crea un volume nominato (db-data) per la persistenza dei dati di MongoDB.

Esecuzione di applicazioni multi-container

Una volta che il file docker-compose.yml è pronto, possiamo avviare l’intero stack dell’applicazione con un singolo comando:

docker-compose up -d

Il comando precedente avvia sia i container web che database in modalità distaccata (-d).

Per arrestare tutti i servizi, utilizzare:

docker-compose down

Questo ferma e rimuove tutti i container preservando volumi e impostazioni di rete.

Fondamenti della rete Docker

Fino ad ora ci siamo concentrati sull’avvio dei container e sulla gestione dello storage, ma cosa succede quando i container devono comunicare tra loro? Nella maggior parte delle applicazioni reali, i container non operano in isolamento – devono scambiarsi dati, che si tratti di un server web che parla con un database o di microservizi che interagiscono tra loro.

Docker fornisce una serie di opzioni di rete per adattarsi a diversi casi d’uso, dalle reti interne isolate a configurazioni accessibili esternamente.

Pronto a migliorare le tue competenze Docker? Iscriviti a Docker Intermedio per esplorare le build multistadio, le reti avanzate e altro ancora!

Cos’è la rete Docker?

La rete Docker è una funzionalità integrata che consente ai container di comunicare tra loro, sia sullo stesso host che su più host in un ambiente distribuito. Fornisce isolamento di rete, segmentazione e opzioni di connettività adatte a diversi scenari di distribuzione.

Docker supporta diversi tipi di reti, ognuno dei quali serve a scopi diversi:

  • Ponte (predefinito): I container sullo stesso host comunicano attraverso una rete virtuale interna. Ogni container ottiene il proprio indirizzo IP privato all’interno della rete bridge e possono raggiungersi tramite i nomi dei container.
    • Esempio: docker network create my-bridge-network
    • Ideale per eseguire più contenitori su un singolo host che devono comunicare in modo sicuro senza esporre servizi esternamente.
  • Host: I contenitori condividono lo stack di rete dell’host e utilizzano direttamente l’indirizzo IP e le porte dell’host.
    • Esempio: docker run --network host nginx
    • Utile quando hai bisogno di alte prestazioni e non richiedi isolamento di rete, come l’esecuzione di agenti di monitoraggio o applicazioni a bassa latenza.
  • Sovrapposizione: Consente la comunicazione dei contenitori su host diversi creando una rete distribuita.
    • Esempio: docker network create --driver overlay my-overlay-network
    • Progettato per distribuzioni orchestrate come Docker Swarm, dove i servizi si estendono su più nodi.
  • Macvlan: Assegna un indirizzo MAC univoco a ciascun contenitore, facendolo apparire come un dispositivo fisico nella rete.
    • Esempio: docker network create -d macvlan --subnet=192.168.1.0/24 my-macvlan
    • Utilizzato quando i container necessitano di accesso diretto alla rete, ad esempio quando si integrano sistemi legacy o si interagisce con reti fisiche.

Esecuzione di container su reti personalizzate

Scopriamo come configurare e utilizzare una rete bridge personalizzata per la comunicazione tra container.

Passo 1: Creare una rete personalizzata

Prima di eseguire i container, è necessario creare una rete dedicata:

docker network create my-custom-network

Questo comando crea una rete isolata a cui i contenitori possono unirsi per la comunicazione tra contenitori.

Passaggio 2: Esegui i contenitori sulla rete

Ora, avviamo due contenitori e li collegiamo alla nostra rete appena creata:

docker run -d --network my-custom-network --name app1 my-app docker run -d --network my-custom-network --name app2 my-app
  • Il flag --network my-custom-network collega il contenitore alla rete specificata.
  • Il flag --name assegna un nome univoco al contenitore, rendendo più facile il riferimento.

Sia app11 che app2 possono ora comunicare utilizzando i loro nomi di contenitore. Puoi testare la connettività utilizzando il comando ping all’interno di uno dei contenitori:

docker exec -it app1 ping app2

Se tutto è configurato correttamente, vedrai una risposta che conferma che i container possono comunicare.

Ispezione delle reti Docker

Per verificare le configurazioni della rete e i container connessi, utilizza:

docker network inspect my-custom-network

Questo comando fornisce dettagli sulla rete, inclusi gli intervalli di IP, i container connessi e le configurazioni.

Esposizione e pubblicazione delle porte

Quando si eseguono container che devono essere accessibili esternamente, è possibile esporre porte specifiche.

Ad esempio, per eseguire un server web Nginx ed esporlo sulla porta 8080 della tua macchina locale, utilizza:

docker run -d -p 8080:80 nginx

Questa mappa la porta 80 all’interno del container alla porta 8080 sull’host, rendendo il servizio accessibile tramite http://localhost:8080.

Linee guida per la rete Docker

  • Utilizzare reti personalizzate: Evitare di utilizzare la rete bridge predefinita per distribuzioni in produzione al fine di ridurre l’accesso non intenzionale tra i container.
  • Sfrutta la scoperta basata su DNS: Invece di codificare gli indirizzi IP, utilizza i nomi dei container per abilitare la scoperta dinamica dei servizi.
  • Limita l’esposizione esterna: Utilizza firewall o policy di rete per controllare l’accesso ai servizi.
  • Monitora il traffico: Utilizza strumenti come docker network inspect, Wireshark o Prometheus per analizzare il traffico di rete e rilevare anomalie.
  • Ottimizzare reti overlay: Se si sta effettuando un deployment in un ambiente distribuito, ottimizzare le reti overlay per ridurre la latenza sfruttando le opzioni di routing locale dell’host.

Pratiche consigliate di Docker e prossimi passi

Ora che hai appreso i fondamenti di Docker, è il momento di migliorare le tue competenze e adottare le migliori pratiche che ti aiuteranno a costruire applicazioni containerizzate sicure, efficienti e manutenibili.

Le seguenti migliori pratiche ti aiuteranno a ottimizzare i tuoi flussi di lavoro con Docker ed evitare errori comuni.

  • Utilizzare immagini di base ufficiali: Preferire sempre immagini di base ufficiali e ben mantenute per garantire sicurezza e stabilità. Le immagini ufficiali sono ottimizzate, regolarmente aggiornate e meno probabilmente vulnerabili.
  • Mantenere le immagini leggere: Ridurre le dimensioni dell’immagine scegliendo immagini di base minimali (ad esempio, python:3.9-slim invece di python:3.9). Rimuovere dipendenze e file non necessari per ottimizzare lo spazio di archiviazione e i tempi di estrazione.
  • Utilizzare build a più fasi: Ottimizzare i Dockerfile separando le dipendenze di compilazione e di runtime. Le build a più fasi assicurano che siano inclusi nell’immagine finale solo gli artefatti necessari, riducendo dimensioni e superficie di attacco.
  • Etichettare correttamente le immagini: Utilizzare sempre tag versionati (ad esempio, my-app:v1.0.0) anziché latest per evitare aggiornamenti inaspettati durante il recupero delle immagini.
  • Scansionare le immagini per individuare vulnerabilità: Utilizzare strumenti di scansione della sicurezza come docker scan, Trivy o Clair per identificare e risolvere le vulnerabilità di sicurezza nelle immagini prima del rilascio.
  • Gestire le variabili d’ambiente in modo sicuro: Evitare di memorizzare credenziali sensibili all’interno delle immagini. Utilizzare i segreti di Docker, le variabili d’ambiente o strumenti esterni di gestione dei segreti come AWS Secrets Manager o HashiCorp Vault.
  • Usa file .dockerignore: Escludi file non necessari (ad esempio .git, node_modules, venv) per ridurre le dimensioni del contesto di build e prevenire l’inclusione accidentale di file sensibili nelle immagini.
  • Abilita il logging e il monitoraggio: Utilizza strumenti come Prometheus, Grafana e Fluentd per i log dei container e il monitoraggio. Ispeziona i log utilizzando docker logs e abilita il logging strutturato per una migliore osservabilità.

Dopo aver padroneggiato le basi di Docker, ci sono molteplici argomenti avanzati da esplorare. Ecco alcune aree interessanti da esaminare successivamente:

  • Docker Swarm & Kubernetes: Esplora Docker Swarm (cluster integrato) e Kubernetes (orchestrazione di livello enterprise con auto-scaling e discovery dei servizi) per un’orchestrazione di livello produzione.
  • Best practices per la sicurezza dei container: Per garantire la sicurezza delle applicazioni containerizzate, segui le linee guida del CIS Docker Benchmark e implementa il Controllo degli Accessi Basato sui Ruoli (RBAC).
  • Pipeline CI/CD con Docker: Automatizza la creazione di immagini, scansioni di sicurezza e distribuzioni utilizzando GitHub Actions, GitLab CI o Jenkins.
  • Sviluppo cloud-native: Sfrutta Docker con piattaforme cloud come AWS ECS, Azure Container Instances e Google Cloud Run per distribuzioni scalabili e gestite.
  • Strategie di persistenza dei dati: Per una gestione ottimale dello storage, comprendi le differenze tra volumi Docker, bind mounts e tmpfs.

Conclusioni

Docker ha rivoluzionato il modo in cui i programmatori costruiscono, distribuiscono ed eseguono le applicazioni, diventando uno strumento essenziale per lo sviluppo software moderno.

In questo tutorial, abbiamo coperto:

  • Cos’è Docker e perché è importante
  • Come installare ed eseguire il tuo primo container
  • Concetti chiave come immagini, container e networking
  • Archiviazione persistente con i volumi di Docker
  • Applicazioni multi-container con Docker Compose
  • Best practices per sicurezza, performance e scalabilità

Ma questo è solo l’inizio! Se vuoi approfondire la tua esperienza con Docker, puoi seguire un corso introduttivo Introduzione a Docker. Per una conoscenza più approfondita, puoi seguire un corso Intermedio su Docker che tratta di build multi-stadio, strumenti di networking Docker e Docker Compose. Infine, puoi anche ottenere la certificazione Docker, dai un’occhiata a La Guida Completa alla Certificazione Docker (DCA) per il 2025 se sei interessato!

Source:
https://www.datacamp.com/tutorial/docker-tutorial