✓Distinguere VM e container spiegando perché i container condividono il kernel dell’host e quali vantaggi e limiti ne derivano
✓Descrivere il funzionamento di Docker: immagini, layer, Dockerfile, registry e i principali comandi della CLI
✓Spiegare il networking dei container Docker: bridge, host e overlay network, e come i container comunicano tra loro
✓Descrivere l’architettura Kubernetes con i concetti di pod, deployment, service e ingress, e il ruolo del control plane
📄
Slides
Data Center — architettura, topologie e Tier
Il problema che i container risolvono
Prima di parlare di container, vale la pena capire il problema che hanno risolto. Immagina uno sviluppatore che crea un’applicazione web sul proprio laptop con Python 3.11 e una libreria specifica. L’applicazione funziona perfettamente in locale. Poi viene deployata sul server di produzione che ha Python 3.8 e versioni diverse delle librerie. L’applicazione si rompe.
Questa situazione era così comune da avere un nome: “Works on my machine” — funziona sulla mia macchina. I container nascono esattamente per eliminare questo problema: l’applicazione viene impacchettata insieme a tutto il suo ambiente di esecuzione — runtime, librerie, file di configurazione — in un’unica unità portabile che si comporta in modo identico ovunque venga eseguita.
VM vs Container — la differenza fondamentale
Macchina Virtuale (VM)
[App A] [App B] [App C]
[OS Guest] [OS Guest] [OS Guest]
[Hypervisor]
[OS Host] + [Hardware]
Ogni VM include un OS completo — kernel, librerie di sistema, servizi — virtualizzato dall’hypervisor. Le VM sono fortemente isolate: kernel separati, spazio di memoria separato.
✅ Isolamento completo — kernel separati
✅ Può eseguire OS diversi (Linux su Windows)
❌ Avvio lento (minuti)
❌ Peso elevato: GB per OS guest
❌ Overhead hypervisor su ogni VM
Container ✅ Leggero e portabile
[App A] [App B] [App C]
[Libs A] [Libs B] [Libs C]
[Container Runtime — Docker]
[OS Host (kernel condiviso)] + [Hardware]
I container condividono il kernel dell’OS host ma hanno il proprio filesystem, librerie e processi isolati. Nessun OS guest — solo le dipendenze dell’applicazione.
✅ Avvio rapidissimo (secondi o meno)
✅ Leggerissimi: MB invece di GB
✅ Densità alta: migliaia su un host
⚠️ Isolamento minore — kernel condiviso
⚠️ Solo Linux su host Linux (o Windows su Windows)
📌 Kernel condiviso — vantaggi e implicazioni di sicurezza
Condividere il kernel è ciò che rende i container così leggeri e veloci — non devono avviare un intero OS. Ma significa anche che una vulnerabilità nel kernel dell’host potrebbe, in teoria, essere sfruttata da un container per “uscire” dal proprio isolamento (container escape). Per questo in ambienti di produzione si usano tecnologie aggiuntive come seccomp (limitazione delle syscall), AppArmor/SELinux (profili di sicurezza) e si evita di eseguire container con privilegi root non necessari.
Docker — il container runtime più diffuso
Docker è la piattaforma che ha reso i container accessibili a tutti, standardizzando la creazione, distribuzione ed esecuzione dei container. Introdotto nel 2013, ha trasformato il modo in cui il software viene sviluppato e deployato.
Immagini Docker — il blueprint del container
Un’immagine Docker è un pacchetto immutabile che contiene tutto il necessario per eseguire un’applicazione: OS base (es. Ubuntu, Alpine), runtime (Python, Node.js, Java), librerie e dipendenze, codice dell’applicazione, configurazione. L’immagine è il template — il container è l’istanza in esecuzione di quell’immagine, esattamente come una classe è il template e l’oggetto è l’istanza in programmazione OOP.
Il sistema a layer — efficienza dello storage
Le immagini Docker sono costruite a strati sovrapposti (layer). Ogni istruzione nel Dockerfile aggiunge un layer immutabile sopra il precedente. Il vantaggio è enorme: i layer identici tra più immagini vengono memorizzati una volta sola sul disco.
Layer di un’immagine Docker — condivisione tra immagini
Immagine app-web (200 MB)
Layer 4: codice app (5 MB)
Layer 3: dipendenze pip (30 MB)
Layer 2: Python 3.11 (50 MB) ← condiviso
Layer 1: Ubuntu 22.04 (115 MB) ← condiviso
Immagine app-api (195 MB)
Layer 4: codice API (3 MB)
Layer 3: dipendenze pip (27 MB) — diverso
Layer 2: Python 3.11 (50 MB) ← condiviso
Layer 1: Ubuntu 22.04 (115 MB) ← condiviso
I layer condivisi (Ubuntu + Python) occupano spazio disco una sola volta. Le due immagini insieme occupano 200 + 195 = 395 MB in teoria, ma solo 200 + 35 = 235 MB effettivi su disco grazie alla condivisione dei layer comuni.
Il Dockerfile — istruzioni per costruire un’immagine
Il Dockerfile è un file di testo che contiene le istruzioni per costruire un’immagine Docker passo per passo. Ogni istruzione crea un nuovo layer.
Esempio Dockerfile — applicazione web Python
# FROM: specifica l'immagine base su cui costruireFROM python:3.11-slim
# WORKDIR: imposta la directory di lavoro dentro il containerWORKDIR /app
# COPY: copia file dall'host nel containerCOPY requirements.txt .
# RUN: esegue un comando durante la BUILD (crea un layer)RUN pip install --no-cache-dir -r requirements.txt
# Copia il codice dell'applicazioneCOPY . .
# EXPOSE: documenta la porta su cui l'app è in ascoltoEXPOSE 8080
# CMD: comando eseguito all'avvio del container (non crea layer)CMD ["python", "app.py"]
Registry — il repository delle immagini
Un registry Docker è un server che memorizza e distribuisce immagini. Il registry pubblico ufficiale è Docker Hub (hub.docker.com) con milioni di immagini già pronte — Ubuntu, Nginx, MySQL, Node.js, Python e qualsiasi altra cosa immaginabile. Le aziende usano registry privati (Harbor, AWS ECR, Azure Container Registry) per ospitare le proprie immagini senza renderle pubbliche.
Comandi Docker essenziali
Comandi Docker — workflow completo
# Costruire un'immagine dal Dockerfile nella directory corrente
docker build -t mia-app:1.0 .
# Scaricare un'immagine dal registry (Docker Hub)
docker pull nginx:latest# Avviare un container in background (-d) con port mapping (-p)# Porta 8080 dell'host → porta 80 del container
docker run -d -p 8080:80 --name web nginx:latest
# Listare i container in esecuzione
docker ps
# Vedere i log del container
docker logs web# Aprire una shell interattiva dentro un container in esecuzione
docker exec -it web /bin/bash
# Fermare e rimuovere un container
docker stop web && docker rm web# Pubblicare l'immagine sul registry
docker push mio-account/mia-app:1.0
Networking dei Container Docker
I container necessitano di comunicare tra loro e con il mondo esterno. Docker gestisce questa comunicazione attraverso reti virtuali software — ogni container ottiene un indirizzo IP virtuale e può comunicare con gli altri secondo le regole della rete a cui è connesso.
Modalità di rete Docker
bridge
Default
La modalità predefinita. Docker crea un bridge virtuale (docker0) sull’host. I container ottengono IP nel range 172.17.0.0/16 e possono comunicare tra loro via IP. Per esporre un servizio all’esterno si usa il port mapping (-p host:container). I container sulla stessa rete bridge si trovano tramite IP o nome container.
Es: container web (172.17.0.2) chiama container db (172.17.0.3) sulla porta 5432
host
Il container condivide direttamente lo stack di rete dell’host — nessuna interfaccia virtuale, nessun NAT. Il container usa le stesse porte e IP dell’host. Massime prestazioni di rete (nessun overhead di traduzione), ma nessun isolamento di rete tra container e host.
Es: container Nginx ascolta direttamente sulla porta 80 dell’host senza port mapping
overlay
Multi-host
Crea una rete distribuita su più host Docker. I container su host fisici diversi vedono la stessa rete logica e comunicano direttamente tramite IP, come se fossero sullo stesso host. Usa VXLAN per incapsulare il traffico L2 dentro UDP su una rete IP esistente. È la modalità usata da Docker Swarm e Kubernetes.
Es: container-A su Host1 chiama container-B su Host2 via IP senza sapere che sono su macchine diverse
none
Il container è completamente isolato dalla rete — nessuna interfaccia, nessuna connettività. Usato per container che elaborano dati locali senza necessità di rete (batch processing, generazione di report, strumenti di analisi offline).
Kubernetes — l’orchestratore di container
Docker risolve il problema di eseguire container su un singolo host. Ma quando si gestiscono centinaia o migliaia di container distribuiti su decine di server, serve uno strumento che risponda automaticamente a domande come: dove avvio questo container? Cosa succede se l’host si guasta? Come distribuisco il traffico tra istanze multiple della stessa app? Come aggiorno l’applicazione senza downtime?
Kubernetes (K8s) è il sistema di orchestrazione di container open source sviluppato da Google e donato alla CNCF (Cloud Native Computing Foundation) nel 2014. È diventato lo standard di fatto per il deployment di applicazioni containerizzate in produzione.
📌 L’origine di Kubernetes
Kubernetes nasce da Borg, il sistema interno con cui Google gestisce tutti i propri servizi (Gmail, YouTube, Ricerca) da oltre un decennio. Borg orchestrava centinaia di migliaia di job su cluster enormi. Kubernetes è essenzialmente Borg riscritto in Go e reso open source — porta a chiunque le stesse tecniche operative usate da Google alla scala più grande del mondo.
Architettura Kubernetes — Control Plane e Worker Node
Cluster Kubernetes — componenti principali
CONTROL PLANE (Master)
API Server: punto di ingresso per tutti i comandi — kubectl vi comunica qui
etcd: database key-value distribuito — memorizza lo stato desiderato del cluster
Scheduler: decide su quale nodo deployare ogni pod in base alle risorse disponibili
Controller Manager: verifica continuamente che lo stato reale corrisponda allo stato desiderato
WORKER NODE (N volte)
kubelet: agente sul nodo che riceve istruzioni dal Control Plane e gestisce i pod
kube-proxy: gestisce le regole di rete per il routing del traffico verso i pod
Container Runtime: Docker, containerd o CRI-O — esegue fisicamente i container
Pod: uno o più container che condividono rete e storage — unità base di K8s
I concetti fondamentali di Kubernetes
📦 Pod
L’unità minima deployabile di Kubernetes. Un pod contiene uno o più container strettamente accoppiati che condividono lo stesso indirizzo IP, la stessa rete e lo stesso volume di storage. I container dentro un pod si comunicano via localhost.
Es: pod con container app + container sidecar per logging
🔁 Deployment
Gestisce il ciclo di vita di un set di pod identici (repliche). Definisce quante repliche del pod devono essere sempre attive. Se un pod muore, il Deployment ne ricrea automaticamente uno nuovo. Permette rolling update senza downtime.
Es: Deployment con replicas: 3 → sempre 3 pod del web server attivi
🔀 Service
I pod hanno IP effimeri — cambiano ogni volta che vengono ricreati. Un Service fornisce un IP stabile e un DNS name che rimane costante. Il Service fa da load balancer tra tutti i pod che corrispondono a una label selettore, distribuendo il traffico automaticamente.
Gestisce l’accesso HTTP/HTTPS esterno al cluster. Funziona come un reverse proxy L7: instrada le richieste verso i Service appropriati in base all’URL o al dominio. Un solo Ingress con un solo IP pubblico può servire decine di applicazioni diverse tramite routing basato su hostname o path.
Con Docker si danno comandi imperativi: “avvia questo container”. Con Kubernetes si dichiara lo stato desiderato: “voglio sempre 3 repliche di questa app”. Il Controller Manager monitora continuamente lo stato reale del cluster e agisce automaticamente per portarlo allo stato desiderato. Se un pod muore, ne viene creato uno nuovo. Se un nodo si guasta, i pod vengono spostati su nodi sani. L’operatore definisce cosa vuole, Kubernetes decide come ottenerlo.
# Esempio YAML di Deployment — stato desiderato dichiarato
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3# voglio sempre 3 pod attivi
selector:
matchLabels:
app: web-app
template:
spec:
containers:
– name: web
image: mia-app:1.0
ports:
– containerPort: 8080
IaaS vs CaaS vs FaaS — tre livelli di astrazione
Aspetto
IaaS VM nel cloud
CaaS Container as a Service
FaaS Function as a Service
Unità deployata
Macchina virtuale
Container / Pod
Funzione / snippet di codice
Gestione OS
A carico dell’utente
Runtime gestito
Completamente astratto
Avvio
Minuti
Secondi
Millisecondi
Scalabilità
Manuale o auto-scaling VM
HPA Kubernetes
Automatica — 0 a ∞
Fatturazione
Per ora di VM accesa
Per risorse container
Per esecuzione singola
Esempi
AWS EC2, Azure VM
GKE, EKS, AKS
AWS Lambda, Azure Functions
📌 FaaS (Serverless) — quando non c’è server da gestire
Con FaaS (es. AWS Lambda) lo sviluppatore scrive solo il codice della funzione — nessun server, nessun container, nessun OS da configurare. La funzione viene eseguita in risposta a un evento (una richiesta HTTP, un messaggio in coda, un file caricato su S3) e viene fatturata al millisecondo di esecuzione. Se non arrivano richieste, la funzione non gira e il costo è zero. Il termine “serverless” è un po’ fuorviante — i server esistono, ma sono completamente nascosti e gestiti dal provider.
📌 Riepilogo — Punti chiave
I container condividono il kernel dell’OS host (VM no) — più leggeri (MB vs GB), avvio in secondi, ma isolamento minore. Il problema “works on my machine” viene eliminato impacchettando app + dipendenze in un’immagine portabile
Docker: immagini a layer condivisi (risparmio disco), Dockerfile per costruirle, Docker Hub per distribuirle. Bridge (default, isolato), host (prestazioni massime), overlay (multi-host con VXLAN), none (isolamento totale)
Kubernetes: Control Plane (API server, etcd, scheduler, controller) + Worker Nodes (kubelet, kube-proxy, container runtime). Principio dichiarativo: si dichiara lo stato desiderato, K8s lo mantiene automaticamente
Pod = unità minima (1+ container con IP condiviso); Deployment = gestisce N repliche; Service = IP stabile + load balancing; Ingress = reverse proxy L7 per routing HTTP/HTTPS esterno
IaaS: gestisci la VM. CaaS: gestisci il container, il runtime è del provider (GKE, EKS). FaaS/Serverless: scrivi solo la funzione, fatturazione al millisecondo di esecuzione
Questo sito Web utilizza i cookie per migliorare la tua esperienza.Supponiamo che tu stia bene con questo, ma puoi rinunciare se lo desideri.
Read More
I cookie sono piccoli file di testo che possono essere utilizzati dai siti Web per rendere più efficiente l'esperienza dell'utente.La legge afferma che possiamo archiviare i cookie sul tuo dispositivo se sono rigorosamente necessari per il funzionamento di questo sito.Per tutti gli altri tipi di cookie, abbiamo bisogno del tuo permesso.Questo sito utilizza diversi tipi di cookie.Alcuni cookie sono collocati da servizi di terze parti che appaiono nelle nostre pagine.
I cookie necessari aiutano a rendere utilizzabile un sito Web consentendo funzioni di base come la navigazione di pagina e l\'accesso alle aree sicure del sito Web.Il sito Web non può funzionare correttamente senza questi cookie.
I cookie di marketing vengono utilizzati per tenere traccia dei visitatori sui siti Web.L\'intenzione è quella di visualizzare annunci pertinenti e coinvolgenti per il singolo utente e quindi più preziosi per gli editori e gli inserzionisti di terze parti.
I cookie di analisi aiutano i proprietari di siti Web a capire come i visitatori interagiscono con i siti Web raccogliendo e segnalando informazioni in modo anonimo.
I cookie di preferenza consentono a un sito Web di ricordare le informazioni che cambiano il modo in cui il sito Web si comporta o sembra, come la tua lingua preferita o la regione in cui ti trovi.
I cookie non classificati sono cookie che stiamo classificando, insieme ai fornitori di singoli cookie.
Cookie Settings
Gestisci Consenso
Per fornire le migliori esperienze, utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Il consenso a queste tecnologie ci permetterà di elaborare dati come il comportamento di navigazione o ID unici su questo sito. Non acconsentire o ritirare il consenso può influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.