Edizione 2026. Un corso audio completo su Docker, che copre le basi dei container, le immagini, i Dockerfile, il networking, Compose, la CI/CD e le ultime funzionalità IA come l'MCP Toolkit, le Docker Sandboxes e Docker Agent.
Scopri perché Docker ha cambiato radicalmente lo sviluppo del software. Questo episodio copre la proposta di valore fondamentale di separare le applicazioni dall'infrastruttura e ottenere una perfetta parità tra gli ambienti di sviluppo e produzione.
3m 30s
2
Container vs Virtual Machines
Comprendi le differenze architetturali tra container e VM. Scopri come i container ottengono l'isolamento condividendo il kernel dell'host, rendendoli incredibilmente leggeri rispetto agli hypervisor tradizionali.
3m 58s
3
L'anatomia di un'immagine Docker
Esplora cos'è realmente un'immagine Docker. Questo episodio spiega i principi dell'immutabilità delle immagini e della composizione dei layer, mostrando come le modifiche al file system vengono sovrapposte per creare un template di container.
3m 38s
4
Il blueprint del Dockerfile
Impara a scrivere un Dockerfile per creare immagini personalizzate. Trattiamo istruzioni essenziali come FROM, RUN e CMD, e spieghiamo la differenza cruciale tra le forme shell ed exec.
3m 30s
5
Padroneggiare la Build Cache
Ottimizza le build delle tue immagini utilizzando la build cache di Docker. Scopri perché l'ordine delle istruzioni nel tuo Dockerfile è fondamentale per evitare installazioni non necessarie di dipendenze.
3m 44s
6
Multi-Stage Builds
Mantieni le tue immagini di produzione snelle e sicure. Questo episodio introduce le multi-stage builds, dimostrando come separare il tuo pesante ambiente di compilazione dal tuo ambiente di runtime minimale.
4m 16s
7
Esecuzione e interazione
Impara i meccanismi pratici per eseguire i container. Trattiamo le modalità detached e interactive, il port publishing di base e come eseguire comandi shell all'interno di un container in esecuzione.
4m 22s
8
Le basi della persistenza dei dati
Previeni la perdita catastrofica di dati quando i container vengono eliminati. Questo episodio confronta i Bind Mounts per l'hot-reloading nello sviluppo locale con i Docker Volumes per la persistenza sicura dei database.
3m 32s
9
Il networking dei container
Comprendi come Docker gestisce il traffico di rete. Scopri le basi del port publishing verso l'host e come i container comunicano in modo sicuro tra loro su reti bridge isolate.
3m 51s
10
Introduzione a Docker Compose
Vai oltre i comandi per un singolo container. Scopri come Docker Compose utilizza un file YAML dichiarativo per definire, collegare in rete e orchestrare più servizi contemporaneamente.
4m 05s
11
Docker nella pipeline CI/CD
Elimina i test instabili con ambienti di build containerizzati. Questo episodio spiega come utilizzare Docker nelle pipeline di Continuous Integration per garantire test automatizzati perfettamente riproducibili.
3m 51s
12
Immagini multipiattaforma
Risolvi l'incompatibilità tra Apple Silicon e Cloud Server. Scopri come Docker Buildx ti consente di cross-compilare e pacchettizzare applicazioni per entrambe le architetture ARM e AMD64 contemporaneamente.
3m 58s
13
Il Docker MCP Toolkit
Connetti in modo sicuro i tuoi agenti IA agli strumenti locali. Questo episodio introduce il Docker Model Context Protocol (MCP) Toolkit, spiegando come gestire i server MCP containerizzati utilizzando cataloghi e profili.
3m 37s
14
Dynamic MCP Auto-Discovery
Esplora Dynamic MCP, una funzionalità sperimentale che consente ai client IA di cercare nel Docker MCP Catalog e installare dinamicamente nuovi server di strumenti durante una conversazione senza configurazione manuale.
4m 14s
15
Docker Sandboxes per l'IA
Comprendi l'architettura delle Docker Sandboxes. Scopri perché gli agenti di programmazione IA autonomi richiedono microVM isolate con demoni Docker dedicati invece dei namespace standard dei container.
3m 58s
16
Creare team di agenti IA
Smetti di fare affidamento su un singolo modello IA per compiti complessi. Questo episodio introduce il framework Docker Agent, mostrando come comporre team specializzati di agenti definiti in YAML.
3m 49s
17
Toolset e workflow degli agenti
Rendi i tuoi agenti IA realmente utili dando loro i giusti vincoli. Scopri come configurare i toolset del filesystem e imporre workflow di sviluppo strutturati in Docker Agent.
3m 42s
18
Modelli IA in Compose
Tratta i tuoi LLM locali proprio come qualsiasi altra dipendenza dell'applicazione. Scopri come dichiarare, configurare e collegare i modelli IA direttamente all'interno del tuo file YAML di Docker Compose.
3m 18s
Episodi
1
La promessa Dev uguale Prod
3m 30s
Scopri perché Docker ha cambiato radicalmente lo sviluppo del software. Questo episodio copre la proposta di valore fondamentale di separare le applicazioni dall'infrastruttura e ottenere una perfetta parità tra gli ambienti di sviluppo e produzione.
Ciao, sono Alex di DEV STORIES DOT EU. Masterclass su Docker, episodio 1 di 18. Hai appena passato tre giorni a cercare un errore che si verifica solo sul server di staging. Il codice funziona perfettamente sul tuo laptop, ma nel momento in cui entra nella deployment pipeline, si rompe. Il colpevole è quasi sempre una system library incompatibile, una versione diversa del runtime o una environment variable mancante. Questo è esattamente il problema che Docker è stato creato per eliminare, mantenendo la promessa del dev equals prod.
Docker è una open platform per lo sviluppo, lo shipping e l'esecuzione di applicazioni. Il suo scopo principale è separare le tue applicazioni dalla tua infrastruttura. Storicamente, gli sviluppatori scrivevano il codice e i team di operations si occupavano del provisioning dei server. Gli sviluppatori consegnavano l'applicazione e il team di operations impiegava ore o giorni per configurare la host machine in modo che soddisfacesse i requisiti del software. Questo allineamento manuale degli ambienti è fragile e lento.
Docker risolve questo problema facendo il packaging dell'applicazione, insieme alle sue dipendenze, ai system tool, alle librerie e al runtime, in un'unità standardizzata chiamata container. Potresti associare questo concetto alle tradizionali virtual machine. Sebbene condividano l'obiettivo di isolare le applicazioni, i container sono molto più leggeri perché non richiedono un guest operating system completo. Approfondiremo questa differenza architetturale nel prossimo episodio. Ora, però, ci concentriamo su ciò che questo tipo di packaging permette di ottenere.
Ecco il punto chiave. Dato che il container contiene sia il codice che l'ambiente preciso necessario per eseguirlo, la host machine sottostante diventa sostanzialmente irrilevante. Docker garantisce che se un container gira sul tuo laptop di sviluppo locale, funzionerà esattamente allo stesso modo su un server di quality assurance, ed esattamente allo stesso modo in un data center di produzione. Elimini la frase it works on my machine, perché la tua macchina e la macchina di produzione ora forniscono l'esatto stesso execution environment.
Il workflow è questo. Uno sviluppatore scrive il codice in locale e definisce l'ambiente richiesto in un file di configurazione plain text. Docker legge quel file e crea un artefatto statico chiamato image. Quella singola image immutabile è ciò che viene testato. Quando i test passano, quella stessa identica image viene deployata in produzione. Non stai copiando codice su un server per poi eseguire un setup script. Stai spostando l'intero ambiente di lavoro come un'unica unità sigillata.
Questa portabilità cambia il modo in cui i sistemi scalano. Dato che i container sono standardizzati e leggeri, tirare su nuove instance di un'applicazione in risposta a un picco di traffico richiede pochi secondi. Puoi spostare facilmente i workload tra ambienti diversi, passando un'applicazione da un server di testing locale a un cloud provider senza modificare una singola riga di codice o riconfigurare l'host.
Il concetto fondamentale è che Docker trasforma l'infrastruttura in una commodity prevedibile, creando un confine rigido in cui gli sviluppatori possiedono l'intero ambiente all'interno del container, e le operations forniscono semplicemente il compute power per eseguirlo. Se vuoi supportare lo show, cerca DevStoriesEU su Patreon. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
2
Container vs Virtual Machines
3m 58s
Comprendi le differenze architetturali tra container e VM. Scopri come i container ottengono l'isolamento condividendo il kernel dell'host, rendendoli incredibilmente leggeri rispetto agli hypervisor tradizionali.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 2 di 18. Non hai bisogno di avviare un intero sistema operativo solo per eseguire un singolo script Python. Eppure, per anni, gli sviluppatori hanno accettato un elevato overhead e tempi di boot lenti per mantenere le proprie applicazioni isolate l'una dall'altra. Oggi risolviamo questo problema analizzando Container contro Virtual Machine.
Immagina di far girare uno stack complesso in locale. Hai bisogno di un frontend React, un'API Python e un database PostgreSQL, tutti in esecuzione simultaneamente. Se li installi direttamente sulla tua macchina host, rischi di creare conflitti di dipendenze. L'API potrebbe richiedere una versione specifica di una libreria di sistema che va in conflitto con quella che serve al tuo database.
Un container risolve questo problema agendo come un processo in sandbox. Ecco il punto chiave. Un container non è un computer in miniatura. È strettamente un processo in esecuzione nativa sulla tua macchina host. La magia sta nell'isolamento. Grazie alle feature integrate nel sistema operativo, a questo processo viene assegnato un filesystem privato, il suo networking stack e una visione isolata del sistema. Per l'API Python in esecuzione all'interno, sembra di essere l'unico software presente sulla macchina. Per il tuo sistema operativo host, è semplicemente un altro processo standard, proprio come il tuo web browser o il tuo text editor.
Dato che un container è solo un processo, condivide il kernel del sistema operativo host. Quando il database PostgreSQL all'interno di un container deve allocare memoria o scrivere un record su disco, parla direttamente con il kernel host. Non c'è nessun intermediario e nessun sistema operativo secondario che fa il boot in background. Ecco perché avviare un container è quasi istantaneo. Ci mette esattamente lo stesso tempo che serve per avviare l'applicazione stessa.
Ora, confrontalo direttamente con una Virtual Machine. Una Virtual Machine affronta l'isolamento simulando l'hardware. Si basa su un hypervisor, ovvero un software che ritaglia una CPU virtuale, della memoria virtuale e un disco virtuale. Sopra questo hardware finto, devi installare un sistema operativo guest completo.
Se vuoi far girare quella stessa API Python in una VM isolata, devi fare il boot di un'intera distribuzione Linux. La VM carica il suo kernel separato, inizializza i device driver e avvia i servizi di sistema in background prima ancora di pensare a eseguire il tuo codice Python. Ogni volta che l'applicazione deve leggere un file, la richiesta passa attraverso il sistema operativo guest, scende all'hypervisor e infine arriva all'hardware host. Questo garantisce un isolamento di sicurezza incredibilmente forte, ma ha un costo elevato in termini di cicli di CPU, utilizzo di memoria e tempi di boot.
A causa di queste differenze, un malinteso comune è che tu debba scegliere l'uno o l'altro. In realtà, container e Virtual Machine non si escludono a vicenda. Nei moderni ambienti cloud, vengono quasi sempre usati insieme. Quando fai il provisioning di un'istanza cloud, stai noleggiando una Virtual Machine. Quella Virtual Machine fornisce un forte confine a livello hardware che separa il tuo workload dagli altri clienti sullo stesso server fisico. A quel punto installi un container runtime all'interno di quella Virtual Machine per far girare il tuo frontend React, la tua API e il tuo database. La Virtual Machine isola l'infrastruttura, mentre i container isolano le singole applicazioni.
Alla fine, la distinzione si riduce ai confini. Le Virtual Machine virtualizzano l'hardware per far girare più sistemi operativi, mentre i container virtualizzano il sistema operativo per far girare più processi isolati.
Grazie per aver passato qualche minuto con me. Alla prossima, stammi bene.
3
L'anatomia di un'immagine Docker
3m 38s
Esplora cos'è realmente un'immagine Docker. Questo episodio spiega i principi dell'immutabilità delle immagini e della composizione dei layer, mostrando come le modifiche al file system vengono sovrapposte per creare un template di container.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 3 di 18. Fai il deploy di un'applicazione e funziona perfettamente. Due settimane dopo, riavvii la stessa identica applicazione sulla stessa macchina e va in crash perché una libreria di sistema si è aggiornata in background. Questa deriva silenziosa dell'ambiente è esattamente ciò che eliminiamo capendo l'anatomia di un'immagine Docker.
Innanzitutto, chiariamo il punto di confusione più comune. Un'immagine non è un container. Un'immagine è un template statico. Contiene il tuo codice applicativo, le tue librerie, i tuoi tool di sistema e il tuo runtime. Un container è semplicemente un'istanza in esecuzione di quell'immagine. Puoi avviare migliaia di container da una singola immagine, ma l'immagine stessa rimane sul disco, in attesa di essere letta.
La caratteristica distintiva di un'immagine Docker è l'immutabilità. Una volta creata, un'immagine non viene mai modificata. Non puoi modificare un file di configurazione all'interno di un'immagine esistente. Se vuoi modificare un'immagine, devi fare la build di una completamente nuova. Questa immutabilità garantisce che un'immagine testata sul tuo laptop si comporti in modo identico in produzione. Il template non può variare nel tempo.
Se non puoi modificare un'immagine, devi costruirne di nuove. Un'immagine Docker non è un singolo file enorme. È una composizione di più layer indipendenti impilati uno sull'altro. Ogni layer rappresenta uno specifico set di modifiche al filesystem, il che significa aggiungere, modificare o rimuovere file.
Prendi ad esempio un'applicazione Node.js. Raramente fai la build del sistema operativo da zero. Invece, parti da una base image. Questa base image contiene una distribuzione Linux minimale e il runtime di Node. Questa base è in realtà composta dai suoi layer, ma per te funge da fondamenta.
Quando aggiungi la tua applicazione a queste fondamenta, Docker registra le tue modifiche come nuovi layer impilati sopra. Per prima cosa, importi il tuo file di configurazione delle dipendenze. Questo crea un nuovo layer. Successivamente, dici al sistema di scaricare e installare le tue dipendenze. Tutte quelle librerie scaricate vengono impacchettate nel layer successivo. Infine, copi il codice sorgente della tua applicazione. Questo forma il layer superiore.
Quando avvii un container, Docker impila questi layer usando un union filesystem. Questo fa sembrare tutti i layer indipendenti come un'unica struttura di directory standard. Se l'esatto stesso path del file esiste in due layer, la versione nel layer superiore nasconde la versione nel layer inferiore.
Ecco il punto chiave. Dato che i layer sono immutabili, vengono pesantemente messi in cache e condivisi. Se aggiorni il codice sorgente della tua applicazione e fai la build di una nuova immagine, Docker calcola cosa è cambiato. Vede che la base image di Linux, il runtime di Node e il tuo layer delle dipendenze sono identici alla build precedente. Riutilizza all'istante quei layer esistenti e crea un nuovo layer solo per il tuo codice aggiornato. Questo trasforma un deploy che potrebbe richiedere minuti in un'operazione da pochi millisecondi.
Questa architettura fa anche risparmiare spazio su disco e banda di rete. Quando fai il push della tua nuova immagine su un server, trasmetti solo il singolo layer che contiene il nuovo codice sorgente. Il server ha già i layer di base. Imponendo layer immutabili, Docker garantisce che l'ambiente della tua applicazione non possa cambiare silenziosamente, assicurando al contempo che tu trasmetta o memorizzi solo i byte esatti che hai modificato.
Per questo episodio è tutto. Alla prossima!
4
Il blueprint del Dockerfile
3m 30s
Impara a scrivere un Dockerfile per creare immagini personalizzate. Trattiamo istruzioni essenziali come FROM, RUN e CMD, e spieghiamo la differenza cruciale tra le forme shell ed exec.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 4 di 18. L'intero ambiente operativo per la tua applicazione complessa può essere espresso in sole dieci righe di testo semplice. Passi un singolo file a un sistema di build, e ottieni un sistema perfettamente configurato e pronto per girare ovunque. Questo è il Dockerfile Blueprint.
Un Dockerfile è un documento di testo che contiene tutti i comandi che un utente potrebbe lanciare da riga di comando per assemblare un'image. Il formato è semplice. Ogni riga inizia con un'istruzione, scritta in maiuscolo per convenzione, seguita dagli argomenti per quell'istruzione. Docker legge questo file riga per riga, dall'alto verso il basso.
Ogni Dockerfile valido deve iniziare con una base. La definisci usando l'istruzione FROM. Se scrivi FROM ubuntu, Docker fa il pull dell'image ufficiale di Ubuntu e la usa come punto di partenza. Ogni riga successiva nel tuo file modificherà questo ambiente di base.
Una volta impostata la tua base, di solito devi installare le dipendenze. Lo fai usando l'istruzione RUN. L'istruzione RUN esegue qualsiasi comando all'interno dell'image corrente e fa il commit del risultato. Se scrivi RUN apt-get update seguito dai tuoi comandi di installazione dei pacchetti, Docker avvia l'ambiente, esegue il package manager, installa il software e salva il nuovo stato.
A questo punto, ti serve la tua vera e propria applicazione. Un sistema operativo con le dipendenze installate è inutile senza il tuo codice. L'istruzione COPY gestisce questa parte. Fornisci un path di origine dal tuo workspace locale e un path di destinazione all'interno dell'image. Docker prende i tuoi file e li copia direttamente nel filesystem del container.
Fare la build dell'image è solo la prima fase. Devi anche dire a Docker quale applicazione lanciare quando si avvia un container. Definisci questo comportamento di default usando l'istruzione CMD o ENTRYPOINT.
Ecco il punto chiave. Ci sono due modi distinti per formattare queste istruzioni di esecuzione, e confonderli causa dei bug subdoli.
Il primo approccio è la forma shell. Scrivi l'istruzione seguita dal comando esattamente come lo digiteresti in un terminale. Quando Docker vede questo, racchiude il tuo comando in una shell, eseguendolo tramite bin slash sh. Questo è comodo perché le variabili d'ambiente vengono espanse automaticamente. Tuttavia, il processo della shell si piazza tra Docker e la tua applicazione. Se Docker invia un segnale per fermare il container in modo graceful, la shell lo intercetta e la tua applicazione non lo riceve mai, portando a terminazioni forzate.
Il secondo approccio è la forma exec. Questa viene scritta come un array JSON. La formatti con le parentesi quadre, fornendo l'eseguibile come prima string e i suoi argomenti come string successive. Quando usi la forma exec, Docker bypassa completamente la shell. Esegue direttamente il tuo eseguibile. La tua applicazione diventa il process ID uno all'interno del container. Questo garantisce che i segnali di sistema passino direttamente alla tua applicazione, assicurando shutdown fluidi e prevedibili.
Se vuoi un container di produzione stabile, usa sempre la forma exec per i tuoi comandi finali, in modo che la tua applicazione controlli il proprio lifecycle.
Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
5
Padroneggiare la Build Cache
3m 44s
Ottimizza le build delle tue immagini utilizzando la build cache di Docker. Scopri perché l'ordine delle istruzioni nel tuo Dockerfile è fondamentale per evitare installazioni non necessarie di dipendenze.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 5 di 18. Se la tua build Docker impiega dieci minuti ogni volta che modifichi una singola riga di source code, stai sbagliando qualcosa. La soluzione sta interamente in come strutturi il tuo file, e questo significa padroneggiare la build cache.
Quando avvii una build Docker, il builder elabora il tuo Dockerfile in sequenza, dall'alto verso il basso. Ogni singola istruzione, che si tratti di copiare una directory o di eseguire uno script, genera un layer separato nell'immagine risultante. Dato che l'esecuzione di questi step richiede tempo di calcolo e banda di rete, Docker salva automaticamente l'output di ogni step in una build cache locale. Nelle build successive, l'engine tenta di riutilizzare questi layer salvati per saltare il lavoro ridondante.
Per determinare se è possibile un cache hit, Docker valuta ogni istruzione rispetto alla cronologia della cache esistente. Per le istruzioni che eseguono comandi, verifica se la command string stessa è identica a quella usata nella build precedente. Per le istruzioni che copiano file dalla tua macchina host all'immagine, Docker fa un passo in più. Calcola un checksum per i contenuti e i metadata di ogni file che viene copiato. Poi confronta questo nuovo checksum con il checksum dei file nel layer precedentemente salvato in cache. Se i checksum corrispondono perfettamente, Docker riutilizza il layer in cache e procede alla riga successiva. Se anche un solo byte è diverso, la cache viene invalidata.
Fai attenzione a questa parte. La cache invalidation è una reazione a catena rigorosa. Nell'istante in cui Docker rileva una modifica e invalida un layer, smette di guardare la cache per il resto della build. Ogni singola istruzione che viene dopo il layer invalidato è costretta a essere eseguita da zero. Questo succede perché ogni layer si basa sullo stato esatto del layer precedente.
Questa reazione a catena detta come devi organizzare il tuo Dockerfile. Prendi un'applicazione Node in cui gestisci dipendenze esterne. Un errore frequente è usare una singola istruzione per copiare l'intera cartella del tuo progetto nell'immagine, seguita da un'istruzione per eseguire il comando di installazione dei package. Se modifichi una singola riga in un file di testo da qualche parte nel tuo source code, il checksum per l'istruzione di copia cambia. La cache si rompe in quello step. Di conseguenza, l'istruzione successiva è costretta a essere eseguita. Aspetti che vengano scaricati di nuovo centinaia di megabyte di dipendenze, anche se la tua lista effettiva di dipendenze è rimasta completamente intatta.
L'approccio ottimale isola le dipendenze dal codice dell'applicazione. Primo, aggiungi un'istruzione per copiare nell'immagine solo il tuo file di configurazione delle dipendenze, nello specifico il tuo package manifest. Secondo, esegui il comando per scaricare le dipendenze. Terzo, aggiungi un'istruzione separata per copiare il resto del tuo source code generale.
Ora, quando modifichi quello stesso file di testo e fai un rebuild, Docker valuta la prima istruzione. Il dependency manifest non è cambiato, quindi viene usata la cache. Passa allo step di installazione. Dato che il layer precedente è stato un cache hit e la command string è identica, la cache viene usata di nuovo anche qui, saltando il download massiccio. La cache si rompe solo all'istruzione finale, dove il builder copia i tuoi source file aggiornati. Un'attesa di dieci minuti diventa un aggiornamento di due secondi.
Il modo più efficace per velocizzare la tua pipeline è ordinare le tue istruzioni rigorosamente da quella con meno probabilità di cambiare a quella con più probabilità di cambiare.
Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
6
Multi-Stage Builds
4m 16s
Mantieni le tue immagini di produzione snelle e sicure. Questo episodio introduce le multi-stage builds, dimostrando come separare il tuo pesante ambiente di compilazione dal tuo ambiente di runtime minimale.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 6 di 18. Portare il tuo compilatore in produzione è un enorme rischio per la sicurezza e fa lievitare le dimensioni del tuo container di gigabyte. Scrivi codice pulito, ma il tuo artifact finale viene appesantito da tutti i tool pesanti necessari solo per farne la build. La soluzione a questo problema sono le build multi-stage.
Quando fai la build di applicazioni in linguaggi compilati come Java, Go o C++, il processo di compilazione richiede build tool, software development kit e codice sorgente grezzo. Storicamente, gli sviluppatori usavano un approccio standard in cui installavano tutte queste dipendenze nel container, compilavano il codice e poi eseguivano l'applicazione. Il problema è che tutti quei build tool rimangono nell'immagine di produzione finale. Finisci per fare il deploy del tuo compilatore, del tuo package manager e dei file intermedi insieme alla tua applicazione vera e propria. Questo rende il tuo container enorme. I container di grandi dimensioni richiedono più tempo per il pull sulla rete e consumano più storage. Peggio ancora, questo crea un'enorme superficie di attacco. Se un attaccante viola il tuo container, si ritrova improvvisamente con un ambiente di sviluppo completo a sua disposizione.
Un malinteso comune è pensare che per risolvere questo problema serva mantenere due file separati: un file per fare la build del software e uno script per estrarre il risultato e passarlo a un secondo file per il deploy. Non è così. Le build multi-stage gestiscono l'intera separation of concerns all'interno di un singolo file.
Ecco il punto chiave. Una build multi-stage ti permette di definire più ambienti distinti, o stage, in sequenza. Ogni stage inizia definendo la propria base image. Inizi il primo stage con una base image pesante che contiene tutti i tuoi tool di sviluppo. Assegni a questo stage un nome, ad esempio builder. All'interno di questo stage builder, copi il tuo codice sorgente dalla tua macchina locale ed esegui i tuoi comandi di compilazione. Lo stage builder fa il lavoro sporco, generando il file eseguibile finale.
Poi, più giù in quello stesso identico file, definisci una seconda base image. Questo avvia un nuovo stage. Per questo stage, scegli una runtime image minimale. Questo ambiente contiene solo le dipendenze esatte necessarie per far girare l'applicazione, con zero build tool.
Invece di copiare di nuovo i file dalla tua macchina locale, usi un'istruzione copy specializzata. Questa istruzione dice al build engine di tornare allo stage builder, prendere solo l'artifact compilato e finito, e inserirlo nel tuo nuovo stage minimale. Quando il build engine finisce, produce un container basato esclusivamente sullo stage finale. Tutto ciò che proviene dal primo stage — il compilatore, i package scaricati, il codice sorgente — viene completamente scartato. Non arriva mai nella tua immagine di produzione.
Considera uno scenario concreto che coinvolge un'applicazione Java Spring Boot. Nel tuo file, il tuo primo stage usa un'immagine Maven pesante. All'interno di questo stage, esegui il comando Maven per fare il package della tua applicazione. Maven scarica tutte le dipendenze di progetto necessarie, compila il codice Java e lo pacchettizza in un file JAR finito.
Successivamente, avvii il secondo stage usando una base image Java Runtime Environment leggera. Non installi Maven in questo ambiente. Non copi i tuoi file sorgente Java qui. Invece, istruisci l'engine a copiare solo il file JAR compilato direttamente dallo stage Maven in questo runtime environment minimale. Infine, imposti il comando di default per eseguire quel file JAR.
Separando rigorosamente il build environment dal runtime environment, garantisci che il tuo container di produzione sia completamente isolato dai tuoi build tool. L'immagine finale vede solo l'artifact compilato e il runtime minimo indispensabile, mantenendo il tuo deploy veloce, snello e altamente sicuro.
Per questo episodio è tutto. Alla prossima!
7
Esecuzione e interazione
4m 22s
Impara i meccanismi pratici per eseguire i container. Trattiamo le modalità detached e interactive, il port publishing di base e come eseguire comandi shell all'interno di un container in esecuzione.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 7 di 18. Avviare un processo in background è esattamente ciò che vuoi per un web server in produzione. Ma quando quel server si rifiuta di caricare la tua pagina, hai bisogno di un modo per rompere il vetro, entrare nell'environment e vedere cosa non funziona effettivamente. Questo episodio tratta l'esecuzione e l'interazione con i container.
Il comando principale per avviare qualsiasi container è docker run. Di default, se esegui un container, questo collega il suo output direttamente allo schermo del tuo terminale. Prende il controllo del tuo prompt e, se premi control C, il container termina. Per un servizio che deve girare a lungo come un web server Nginx, questo è del tutto impraticabile. Vuoi che il server giri in background. Ottieni questo risultato usando il flag detached mode, che digiti come un singolo trattino d. Quando passi trattino d, Docker avvia il container, stampa un lungo container ID univoco sul tuo schermo e ti restituisce immediatamente il prompt del terminale. Il container continua a girare silenziosamente in background.
Tuttavia, quel container in esecuzione è isolato. Anche se Nginx sta servendo attivamente traffico sulla porta 80 all'interno del container, la tua macchina host non può vederlo. Devi fare esplicitamente un buco attraverso quell'isolamento di rete. Lo fai con il flag publish, digitato come trattino p. Questo ti permette di mappare una porta specifica sul tuo laptop host a una porta specifica all'interno del container. Se specifichi trattino p 8080 due punti 80, Docker intercetta qualsiasi traffico web che arriva al tuo laptop sulla porta 8080 e lo instrada direttamente alla porta 80 all'interno del container. Ora hai un web server in detached mode che puoi raggiungere con successo dal tuo browser locale.
Ma cosa succede quando carichi la pagina e vedi un errore di configurazione? Il tuo server Nginx sta girando in background, ma devi leggere i file di configurazione sul suo filesystem. Ecco il punto chiave. Non devi fermare un container per guardarci dentro. Invece, usi il comando docker exec. Mentre docker run crea un container nuovo di zecca, docker exec esegue un nuovo comando all'interno di un container già esistente e in esecuzione.
Per ottenere un terminale utile e funzionante, devi combinare due flag specifici in trattino i t. La i sta per interactive. Questo mantiene aperto il canale di standard input, permettendoti di digitare effettivamente comandi nel container. La t alloca uno pseudo-TTY. Questo inganna il container facendogli credere di essere connesso a un terminale fisico, il che è necessario per far visualizzare correttamente i command prompt e la formattazione del testo.
Se esegui docker exec trattino i t, seguito dal nome del container e dal comando slash bin slash bash, entri istantaneamente in un command prompt all'interno del container Nginx in esecuzione. Ora sei dentro il box. Puoi leggere i file di configurazione, controllare gli error log e ispezionare il filesystem esattamente come faresti su un server Linux standard. Quando hai finito, digitare exit chiude la tua sessione shell temporanea. Il container Nginx stesso rimane completamente inalterato, e continua a girare in background.
Prima o poi, dovrai fare un po' di pulizia. Eseguire docker stop con il nome del container invia un segnale di terminazione, dando all'applicazione il tempo di spegnersi in modo pulito. Tuttavia, fermare un container non lo elimina dal tuo sistema. Il container fermato rimane sul tuo disco fisso, conservando i suoi log e qualsiasi modifica interna al filesystem. Per eliminarlo permanentemente e liberare quello spazio su disco, esegui il comando docker rm.
La distinzione più critica da memorizzare è la differenza tra run ed exec. Docker run avvia un sistema isolato nuovo di zecca, mentre docker exec ti permette di entrare in un sistema che sta già respirando.
Grazie per l'ascolto. Stammi bene, a presto.
8
Le basi della persistenza dei dati
3m 32s
Previeni la perdita catastrofica di dati quando i container vengono eliminati. Questo episodio confronta i Bind Mounts per l'hot-reloading nello sviluppo locale con i Docker Volumes per la persistenza sicura dei database.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 8 di 18. Fai il deploy di un database all'interno di un container, scrivi migliaia di righe e tutto funziona perfettamente. Poi elimini il container per aggiornare l'immagine, e l'intero database scompare per sempre. Di default, lo storage dei container è strettamente temporaneo. Per evitare la perdita di dati, ci servono le basi della persistenza dei dati.
Quando un container si avvia, crea un layer scrivibile sopra la sua immagine di base. Tutti i file che il container crea o modifica vengono salvati in questo specifico layer. Se il container viene distrutto, anche quel layer viene distrutto insieme a lui. I dati sono completamente effimeri. Non esistono al di fuori del ciclo di vita del container. Per tenere i dati al sicuro, devi portarli fuori dal container, sulla macchina host. Docker offre due meccanismi principali per farlo: i bind mount e i managed volume.
Un bind mount mappa un path specifico ed esplicito sulla tua macchina host direttamente a un path all'interno del container. Dici a Docker esattamente quale cartella del tuo laptop deve apparire all'interno dell'ambiente del container. Questo dipende fortemente dal tuo sistema operativo host e dalla struttura dei tuoi file locali. La macchina host mantiene il pieno controllo sui file.
Questo approccio è perfetto per lo sviluppo in locale. Fai il bind mount della directory del tuo codice sorgente locale nel path della web app del container. Quando modifichi e salvi uno script sul tuo laptop, il container legge immediatamente il file aggiornato. Ottieni un hot-reloading istantaneo senza dover fare il rebuild dell'immagine del container ogni volta che cambi una riga di codice.
Il secondo meccanismo è un managed volume. Invece di puntare a un path specifico che controlli sul tuo disco fisso, chiedi a Docker di creare un'entità di storage. Docker alloca lo spazio sulla macchina host e lo gestisce completamente. Non hai bisogno di sapere dove Docker mette fisicamente i file sul tuo sistema host. Ti basta dare un nome al volume e dire al container dove montarlo internamente.
I volumi sono la soluzione standard per la persistenza del database. Quando fai girare PostgreSQL, crei un volume lanciando un semplice comando e dandogli un identificatore, come db-data. Poi, quando avvii il tuo container, passi un flag di configurazione che collega quel volume db-data al path interno in cui Postgres scrive i record delle sue tabelle. Se fermi ed elimini il container del database, Docker lascia il volume completamente intatto. Quando in seguito tiri su un nuovo container, ti basta attaccare quel volume esistente, e tutti i tuoi record saranno intatti.
Ecco il punto chiave. La scelta tra questi due metodi dipende da chi ha bisogno di accedere ai file. Usa i bind mount quando la tua macchina host deve interagire attivamente con i dati, come un developer che modifica il codice sorgente. Usa i managed volume quando il container è il proprietario dei dati, come un database engine che scrive record, e vuoi semplicemente che Docker tenga quei file al sicuro tra i vari riavvii del container.
I container effimeri sono una scelta di design, non un difetto, perché ti costringono a disaccoppiare i tuoi dati dal tuo compute a runtime. Dai sempre per scontato che il tuo container verrà distrutto immediatamente, e mappa esplicitamente il tuo stato persistente al di fuori di esso. Se trovi utili questi episodi, puoi supportare lo show cercando DevStoriesEU su Patreon. Per questo episodio è tutto. Grazie per l'ascolto, e continua a costruire!
9
Il networking dei container
3m 51s
Comprendi come Docker gestisce il traffico di rete. Scopri le basi del port publishing verso l'host e come i container comunicano in modo sicuro tra loro su reti bridge isolate.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 9 di 18. Di default, un container in esecuzione è completamente isolato dal mondo esterno. Si trova in una bolla privata e, se vuoi che Internet lo raggiunga, devi creare intenzionalmente delle aperture in questo isolamento. Gestire queste aperture e le connessioni tra i container è il compito del Container Networking.
Quando un container si avvia, Docker gli assegna un indirizzo IP interno. Di solito il container può accedere a Internet per scaricare aggiornamenti o fare chiamate di rete, ma nulla al di fuori della macchina host può raggiungerlo. Per accettare il traffico in entrata, usi il port publishing. Il publishing prende una porta sulla tua macchina host fisica e la mappa direttamente su una porta all'interno del container. Se hai un container web server in ascolto internamente sulla porta ottanta, puoi pubblicarlo sulla porta ottanta ottanta del tuo host. Quando un utente invia una request alla tua macchina host sulla porta ottanta ottanta, Docker la intercetta e la inoltra direttamente attraverso il firewall al container sulla porta ottanta. Configuri questo mapping all'avvio usando il flag publish. Senza quel flag, il container rimane inaccessibile alla rete esterna.
Questo copre il traffico esterno. Ora, la seconda parte riguarda la comunicazione interna. Le applicazioni raramente girano come un singolo processo isolato. Di solito hai più container che devono condividere dati. Di default, Docker collega ogni nuovo container a un network integrato chiamato default bridge. Un bridge è uno switch di rete software che gira sulla tua macchina host. Connette i container in modo che possano scambiarsi pacchetti, isolandoli allo stesso tempo dai network esterni.
Ecco il punto chiave. Il default bridge permette ai container di comunicare usando i loro indirizzi IP interni, ma gli indirizzi IP dei container cambiano ogni volta che un container si riavvia o si aggiorna. Fare l'hardcoding di un indirizzo IP nella configurazione della tua applicazione romperà il tuo sistema quasi immediatamente. Per risolvere questo problema, crei un bridge network user-defined.
Quando colleghi più container a un bridge custom user-defined, Docker fornisce una DNS resolution interna automatica. Questo significa che i container possono trovarsi a vicenda usando esattamente i loro nomi container. Immagina uno scenario in cui hai un container con un'applicazione backend e un container database. Crei un singolo bridge network custom e ci colleghi entrambi i container. All'interno del codice della tua applicazione backend, non scrivi una connection string al database usando un indirizzo IP fragile. Usi semplicemente il nome del container database come indirizzo host. Docker intercetta la DNS query, trova il container database su quello specifico bridge e instrada il traffico dinamicamente all'indirizzo IP interno corretto.
Questo design ti dà il controllo totale sulla sicurezza dell'applicazione. Il backend e il database possono parlarsi liberamente attraverso il bridge custom, ma nessun traffico esterno può raggiungere il database. Per far girare la tua applicazione in modo sicuro, lasci il database nascosto sul bridge interno privato senza porte pubblicate. Poi, pubblichi solo la porta del container backend sulla tua macchina host. Gli utenti esterni chiamano la porta pubblica del backend, e il backend fa delle query in modo sicuro al database attraverso il bridge privato.
L'architettura della tua applicazione detta la tua network topology: usa le porte pubblicate per far entrare gli utenti esterni, e i bridge custom user-defined per far parlare tra loro i tuoi container interni in modo sicuro tramite nome. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
10
Introduzione a Docker Compose
4m 05s
Vai oltre i comandi per un singolo container. Scopri come Docker Compose utilizza un file YAML dichiarativo per definire, collegare in rete e orchestrare più servizi contemporaneamente.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 10 di 18. Non dovresti aver bisogno di un documento di testo pieno di comandi da terminale complessi solo per avviare il tuo ambiente di sviluppo locale. Affidarti alla shell history per ricordare i flag, le porte e i nomi dei network esatti per più container è un modo di lavorare fragile. L'introduzione a Docker Compose risolve questo problema trasformando l'intero stack della tua applicazione in un unico file dichiarativo.
Quando esegui un'applicazione, raramente esiste in isolamento. Di solito hai un web server, un database e magari un layer di caching. Avviarli manualmente richiede l'esecuzione di più comandi separati. Devi creare un network custom, collegarci ogni container, esporre le porte corrette e montare i drive di storage. Se fai un errore di battitura in uno qualsiasi di questi passaggi, i container non riescono a comunicare e l'applicazione va in errore.
Docker Compose sostituisce questo processo imperativo con un file YAML dichiarativo, di solito chiamato compose punto yaml. Invece di dire a Docker esattamente cosa fare passo dopo passo, dichiari lo stato finale desiderato dell'intero sistema. Docker Compose capisce i passaggi necessari per raggiungere quello stato.
Il file YAML è diviso in tre sezioni strutturali principali. La prima e più importante sezione si chiama services. Un service è semplicemente la definizione di uno specifico container nella tua applicazione. Prendi uno scenario in cui stai eseguendo un'applicazione Node insieme a un database MySQL. Sotto la sezione services, definisci due voci. Chiami la prima web, specificando l'immagine Node e le porte locali che vuoi esporre. Chiami la seconda database, specificando l'immagine MySQL e le variabili d'ambiente necessarie, come la password di root.
Ecco il punto chiave. Non hai bisogno di collegare questi container manualmente. Di default, Docker Compose crea automaticamente un singolo network interno per la tua applicazione. Collega tutti i service definiti a questo network e assegna a ciascun container un hostname che corrisponde al nome del suo service. Il codice della tua applicazione Node può connettersi al database semplicemente puntando all'hostname database, e il DNS interno lo risolve nell'IP corretto del container. Puoi definire manualmente dei network custom nella sezione networks del file YAML, ma per la maggior parte dei setup di sviluppo standard, il comportamento di default fa esattamente quello che ti serve.
L'ultimo elemento strutturale è la sezione volumes. I database richiedono uno storage persistente. Se il container MySQL si spegne, non vuoi che i tuoi dati vengano cancellati. In fondo al tuo file YAML, dichiari un named volume. Poi, all'interno della definizione del tuo service database, mappi un percorso specifico all'interno del container a quel named volume. Docker Compose gestisce la creazione e il lifecycle di questo storage per te.
Una volta scritto il tuo file, gestisci l'intero stack con due comandi. Digiti docker compose up. Compose legge il file YAML, crea il network interno, configura i volumi e avvia i container MySQL e Node. Se vuoi continuare a lavorare nel tuo terminale, aggiungi il flag detach per eseguire tutto in background.
Quando hai finito di lavorare, non fermi e rimuovi ogni container singolarmente. Digiti docker compose down. Compose ferma in modo graceful l'app Node, ferma il database e rimuove i container e il network di default, mantenendo il tuo sistema completamente pulito. Lascia intatti i tuoi named volume, il che significa che i record del tuo database ti aspetteranno la prossima volta che tiri su lo stack.
Docker Compose sposta la tua mentalità dalla gestione di singoli container isolati alla gestione di ambienti applicativi completi. Il setup della tua infrastruttura diventa un singolo pezzo di codice che puoi committare sotto version control e condividere all'istante con il tuo team.
Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
11
Docker nella pipeline CI/CD
3m 51s
Elimina i test instabili con ambienti di build containerizzati. Questo episodio spiega come utilizzare Docker nelle pipeline di Continuous Integration per garantire test automatizzati perfettamente riproducibili.
Ciao, sono Alex di DEV STORIES DOT EU. Masterclass su Docker, episodio 11 di 18. Fai il push del tuo codice, la pipeline gira e i test falliscono. Li fai girare in locale e passano perfettamente. Il tuo server CI ha una versione delle dipendenze leggermente più vecchia rispetto al tuo portatile. Questo drift è la causa principale dei test notoriamente flaky, ma containerizzare il tuo ambiente di build rende ogni run perfettamente prevedibile. Oggi parliamo di Docker nella pipeline CI/CD.
Storicamente, la Continuous Integration significava mantenere server di build statici. Col tempo, gli ingegneri si connettono a queste macchine virtuali per installare pacchetti, aggiornare i runtime e modificare le configurazioni di sistema. Questi server diventano delle pet VM. Accumulano stato nascosto e file di cache residui. Quando una pipeline fallisce, perdi tempo a capire se il codice è effettivamente rotto o se il server ha solo bisogno di un aggiornamento software.
Usare Docker come ambiente di build aggira completamente questo problema. Invece di eseguire i tuoi script di test direttamente sul sistema operativo host di un worker CI, il worker tira su un container. Il runner CI fa il pull di una specifica immagine Docker, avvia il container, monta il tuo codice sorgente ed esegue i tuoi step di build all'interno di quell'ambiente isolato.
Ecco il punto chiave. Quando il job finisce, il container viene distrutto. La run successiva della pipeline ottiene un ambiente completamente nuovo e identico. Non ci sono processi in background in conflitto derivanti da run precedenti. L'ambiente è stateless e interamente definito dall'immagine.
Pensa al processo di aggiornamento di un runtime di programmazione. Supponi di dover passare il tuo progetto da Node 18 a Node 20. In un setup tradizionale, qualcuno deve loggarsi nel server di build, aggiornare il software a livello di sistema e sperare che non rompa altri progetti che condividono lo stesso worker. Con Docker come tuo ambiente di build, l'intero processo è solo la modifica di una string. Aggiorni il tag dell'immagine base nella tua configurazione da Node 18 a Node 20. Il runner CI fa il pull della nuova immagine. La tua build gira immediatamente nell'ambiente aggiornato. Se un test fallisce, fai il revert del tag e riprovi più tardi. Gestisci l'infrastruttura direttamente insieme al tuo codice.
C'è un altro livello in tutto questo. Se usi Docker per buildare la tua applicazione, la tua pipeline CI deve avere la capacità di fare build e push delle immagini. Se il tuo job CI sta già girando all'interno di un container, come fai a lanciare i comandi di Docker build? Questo richiede un pattern chiamato Docker-in-Docker.
Docker-in-Docker significa far girare un daemon Docker isolato all'interno del tuo container CI. Il container esterno fornisce l'ambiente controllato per gli step della tua pipeline, mentre il daemon interno processa le build della tua applicazione. Questo permette al tuo job CI di fare il pull delle immagini base, costruire il container della tua applicazione e fare il push dell'artifact finale su un registry, il tutto senza sporcare la macchina host che fa girare il worker CI.
Spostare il tuo ambiente CI in un container trasferisce il controllo del sistema di build allo sviluppatore. La stessa identica immagine che fa la build del tuo codice su un server remoto può essere fatta girare sulla tua macchina locale, garantendo che se un test fallisce in CI, puoi riprodurre quell'esatto fallimento in locale.
Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
12
Immagini multipiattaforma
3m 58s
Risolvi l'incompatibilità tra Apple Silicon e Cloud Server. Scopri come Docker Buildx ti consente di cross-compilare e pacchettizzare applicazioni per entrambe le architetture ARM e AMD64 contemporaneamente.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 12 di 18. L'espressione "funziona sulla mia macchina" assume un significato completamente nuovo quando la tua macchina locale usa un processore ARM, ma il tuo cloud in produzione gira su Intel. Testi il container in locale, fai il push su un registry, fai il pull sul server, e crasha all'istante con un execution format error. Il problema è un mismatch di architettura hardware. Per risolverlo, usi le Multi-Platform Image.
Un'immagine container è fondamentalmente un bundle di binari e file system. Se fai la build di un'immagine su un Mac Apple Silicon, i binari risultanti sono compilati per l'architettura ARM64. Quando fai il deploy di quell'immagine su un server Linux cloud standard con processore AMD64, la CPU host letteralmente non capisce le istruzioni all'interno del container. In passato, dovevi mantenere pipeline di build separate per i diversi target hardware. Docker Buildx elimina questa necessità.
Docker Buildx è un plugin da riga di comando che estende il sistema di build standard di Docker. Usa un engine di backend chiamato BuildKit per eseguire le build in parallelo e gestire task complessi come puntare a piattaforme multiple in un singolo passaggio. Quando fai la build di un'immagine multi-piattaforma usando Buildx, non stai infilando due file system separati in un unico container gigante. Invece, Buildx crea una manifest list dell'immagine. Pensa a questo manifest come a una routing table. Contiene una lista di puntatori a diverse immagini specifiche per architettura, salvate nel tuo registry. Quando una macchina fa il pull della tua immagine, il suo Docker daemon legge questo manifest, identifica l'architettura della propria CPU host e scarica automaticamente solo i layer dell'immagine che corrispondono al suo hardware.
Per fare la cross-compilazione e pacchettizzare un'API di backend per entrambe le architetture simultaneamente, usi il comando docker buildx build. Includi un platform flag, passandogli una lista separata da virgole dei tuoi target. Per esempio, digiti il flag, seguito da linux slash amd64 virgola linux slash arm64. Aggiungi il tuo image tag standard, e poi aggiungi un push flag.
Ecco il punto chiave. Quando fai la build per più piattaforme contemporaneamente, non puoi semplicemente caricare l'immagine multi-piattaforma finale di nuovo nella cache del tuo Docker engine locale. Il daemon locale non è progettato per contenere una manifest list che punta a più architetture. Devi istruire Buildx a fare il push dei risultati direttamente al tuo container registry. Il registry funge da sistema di storage che organizza correttamente la manifest list e le singole immagini per architettura.
Per eseguire fisicamente la build per un processore che non hai, Buildx si affida a un emulatore chiamato QEMU. Docker Desktop lo configura automaticamente. Quando la tua macchina ARM raggiunge uno step che richiede un'istruzione AMD64, l'emulatore la traduce al volo. Questo richiede zero modifiche al tuo Dockerfile. Se hai bisogno di tempi di build più rapidi, puoi anche usare tool di cross-compilazione direttamente all'interno di una multi-stage build, il che salta l'emulazione ma richiede di impostare compiler flag specifici nel tuo codice.
Il vero potere di un multi-platform manifest è che isola completamente il consumer dai dettagli hardware sottostanti. Uno sviluppatore su un Mac e un cluster di produzione che gira su Intel fanno il pull dell'esatto stesso image tag, e il registry fornisce automaticamente a ciascuno il binario corretto senza alcuna configurazione extra.
Grazie per aver passato qualche minuto con me. Alla prossima, stammi bene.
13
Il Docker MCP Toolkit
3m 37s
Connetti in modo sicuro i tuoi agenti IA agli strumenti locali. Questo episodio introduce il Docker Model Context Protocol (MCP) Toolkit, spiegando come gestire i server MCP containerizzati utilizzando cataloghi e profili.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 13 di 18. Dare a un agente AI accesso diretto al tuo database o al file system locale è incredibilmente potente. Ma installare script di integrazione non attendibili direttamente sulla tua macchina host per farlo funzionare è un disastro di sicurezza annunciato. Il Docker MCP Toolkit risolve questo problema spostando queste integrazioni in container isolati.
Il Model Context Protocol, o MCP, è uno standard aperto che permette ai client AI, come la desktop app di Claude o l'editor Cursor, di connettersi a fonti di dati e tool esterni. Per dare alla tua AI una nuova funzionalità, esegui una piccola applicazione chiamata server MCP. Storicamente, questo significava scaricare script Python o Node di terze parti ed eseguirli direttamente sul tuo sistema operativo. Questo introduce una forte frizione operativa con conflitti di dipendenze e, cosa ancora più importante, dà a codice non attendibile un accesso illimitato alla tua macchina. Il Docker MCP Toolkit risolve questo problema racchiudendo questi server in container Docker standard.
Il primo elemento di questo sistema è il Catalog. Un Catalog è un registry di server MCP verificati e containerizzati. Invece di fare il pull di repository casuali da internet, fai il pull di immagini Docker standardizzate. Queste immagini sono pre-pacchettizzate per eseguire i tool necessari senza richiedere alcun runtime di linguaggio locale sulla tua macchina host.
Una volta che hai accesso a questi server, hai bisogno di un modo per organizzarli. Questo viene fatto usando i Profile. Un Profile è un raggruppamento di configurazioni che definisce esattamente quali tool sono necessari per un progetto specifico. Ad esempio, potresti creare un Profile chiamato web-dev. All'interno di questa configurazione, specifichi che questo Profile richiede il server GitHub per leggere i repository di codice e il server Playwright per la browser automation. Imposti le tue chiavi API e le variabili d'ambiente per entrambi i tool una sola volta all'interno della configurazione del Profile.
Ora hai dei tool isolati e un Profile definito. Come fa l'AI a connettersi a questi? È qui che la cosa si fa interessante. La connessione è gestita dall'MCP Gateway. Il Gateway fa da router centrale in esecuzione sul tuo host. Non devi configurare il tuo client AI per avviare i singoli container. Invece, punti Claude o Cursor verso l'MCP Gateway e richiedi il Profile web-dev.
Quando il client si connette, il Gateway legge il Profile, avvia automaticamente in background i container GitHub e Playwright richiesti, e stabilisce la connessione. Il Gateway fa da intermediario per la comunicazione tra il client AI e i container usando il protocollo standard. Il client AI crede di parlare con dei tool locali, ma tutta l'esecuzione avviene in modo sicuro all'interno di Docker. Configuri i tool una sola volta nel Profile, e puoi condividere quell'esatto setup tra un numero qualsiasi di applicazioni AI diverse. Se uno di questi tool si comporta male o viene compromesso, rimane intrappolato in un container, completamente cieco rispetto al resto del tuo sistema.
Il vero valore dell'MCP Toolkit è che separa la configurazione dei tuoi tool AI dai client che li usano, fornendo forti garanzie di isolamento senza sacrificare l'intelligenza dei tuoi workflow. Grazie per l'ascolto. Statemi bene, tutti.
14
Dynamic MCP Auto-Discovery
4m 14s
Esplora Dynamic MCP, una funzionalità sperimentale che consente ai client IA di cercare nel Docker MCP Catalog e installare dinamicamente nuovi server di strumenti durante una conversazione senza configurazione manuale.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 14 di 18. Sei nel bel mezzo di una conversazione con un AI coding agent e gli chiedi di fare una query su un database. Normalmente, se ti sei dimenticato di configurare il tool del database in anticipo, l'agent fallisce e ti chiede di intervenire. Il problema è che la configurazione manuale dei tool interrompe il workflow. Ma cosa succederebbe se l'agent si accorgesse di non avere una funzionalità, cercasse in un catalogo e installasse il server necessario completamente al volo? Questo è esattamente ciò che fa Dynamic MCP Auto-Discovery.
In genere, fornire tool a un Large Language Model significa definirli staticamente in un file di configurazione prima di avviare la sessione. Se il tuo agent dovesse aver bisogno di leggere un repository GitHub, postare un messaggio su Slack e fare una query su un database, devi caricare tutti quei server Model Context Protocol in anticipo. Questo approccio intasa la context window con tool che potrebbero non essere mai utilizzati e ti obbliga a prevedere perfettamente le esigenze dell'agent. Dynamic MCP cambia questo paradigma. Consente all'agent di scoprire e collegare i tool esattamente quando il task lo richiede, senza alcun intervento umano.
Quando abiliti la funzionalità dinamica, il Docker MCP Gateway espone una serie di tool di gestione direttamente all'AI agent. Il gateway, in sostanza, dà all'agent la possibilità di gestire la propria toolchain. I due tool fondamentali forniti dal gateway per questo processo sono mcp-find e mcp-add. L'agent interagisce con questi esattamente come interagisce con qualsiasi function call standard.
Possiamo analizzare il flusso di questa logica con uno scenario concreto. Supponi di chiedere al tuo agent di analizzare le metriche utente memorizzate in un database SQL. L'agent valuta la richiesta, controlla il suo toolkit attuale e si accorge di non avere alcun tool di query sul database caricato. Invece di lanciare un errore, l'agent invoca il tool mcp-find, passando una string di ricerca pertinente come postgres.
Il gateway intercetta questa call e fa una query al catalogo Docker MCP configurato per trovare i server disponibili che corrispondono a quella string. Restituisce all'agent i metadata e le descrizioni dei server corrispondenti. L'agent legge la descrizione, conferma che il server Postgres risolverà il problema e passa allo step successivo.
L'agent quindi invoca il tool mcp-add, passando l'identificativo del server Postgres appena trovato. Ed è qui che la cosa si fa interessante. Il gateway intercetta la richiesta mcp-add, fa il pull dell'immagine necessaria, tira su il server MCP in un container Docker e fa il bind dinamico dei nuovi tool alla connessione attiva. L'agent ha improvvisamente accesso ai tool del database, si connette al tuo database, esegue la query che avevi chiesto inizialmente e restituisce il risultato. L'intero processo avviene in background, senza interrompere minimamente la conversazione.
In questa management suite è disponibile un terzo tool per l'esecuzione di codice sperimentale, ma gestisce un set di problemi completamente diverso, quindi oggi manterremo il focus esclusivamente sulla discovery.
Ecco l'aspetto fondamentale di questo processo. Quando l'agent utilizza mcp-add per caricare un nuovo server, quell'aggiunta ha uno scope strettamente limitato alla sessione corrente. Il gateway non riscrive i tuoi file di configurazione globali e i tool appena aggiunti non persistono dopo i riavvii. Quando chiudi la sessione, il binding temporaneo del tool viene distrutto. Questo garantisce che il tuo ambiente di base rimanga pulito e sicuro, offrendo al contempo all'agent la massima flessibilità per risolvere dinamicamente problemi complessi e multi-step.
Esponendo la ricerca nel catalogo e l'installazione come function call standard, Dynamic MCP elimina il peso della configurazione iniziale e consente all'agent di costruire il proprio ambiente on demand.
Questo è tutto per questo episodio. Grazie per l'ascolto e continua a sviluppare!
15
Docker Sandboxes per l'IA
3m 58s
Comprendi l'architettura delle Docker Sandboxes. Scopri perché gli agenti di programmazione IA autonomi richiedono microVM isolate con demoni Docker dedicati invece dei namespace standard dei container.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 15 di 18. Un coding agent AI autonomo è esattamente il tipo di processo che non vorresti mai far girare con accesso root sul tuo laptop. Gli chiedi di risolvere un bug e all'improvviso inizia a scaricare pacchetti arbitrari, a modificare file di sistema o a tentare di fare il rebuild della tua infrastruttura locale. Hai bisogno di un ambiente in cui l'agente possa agire come un amministratore senza esserlo effettivamente. Questo è esattamente il problema che le Docker Sandbox per l'AI sono progettate per risolvere.
Tradizionalmente, Docker isola i processi usando i namespace e i control group di Linux. Questi container condividono il kernel del sistema operativo host. Per un servizio web prevedibile, questo modello funziona perfettamente. Ma un agente AI è intrinsecamente imprevedibile. Genera codice non verificato, lo esegue e spesso ha bisogno di installare nuovi pacchetti di sistema al volo per testare le proprie soluzioni. Condividere il kernel dell'host con un agente imprevedibile è un rischio per la sicurezza troppo alto. Per risolvere questo problema, le Docker Sandbox abbandonano i namespace standard dei container a favore di microVM isolate.
Quando avvii una sandbox per un agente, questa fa il boot di una virtual machine dedicata e leggera. L'agente ottiene il suo kernel distinto. Non può vedere i processi del tuo host. Di default, non può accedere al network stack del tuo host. Cosa ancora più importante, elimina completamente il rischio delle tradizionali vulnerabilità di container escape. L'agente è strettamente confinato in un box con virtualizzazione hardware.
Questo è importantissimo quando consideri cosa fanno effettivamente gli agenti AI. Immagina che il tuo agente debba scrivere una web app complessa, creare un Dockerfile e testare la build. Per farlo, l'agente deve lanciare dei comandi Docker. Se mappassi semplicemente il Docker socket del tuo sistema host in un container standard, l'agente potrebbe teoricamente lanciare privileged container direttamente sulla tua macchina host. Le Docker Sandbox evitano questo problema facendo girare un Docker daemon completamente isolato all'interno della microVM stessa. L'agente può fare la build di immagini, fare il pull di dipendenze esterne e far girare nested container tutto il giorno. Dato che comunica con il daemon isolato all'interno della microVM, l'ambiente Docker del tuo sistema host rimane completamente ignaro e pulito. Quando il task finisce e la sandbox viene distrutta, il daemon interno e tutte le immagini scaricate spariscono immediatamente.
È qui che la cosa si fa interessante. Se la microVM è completamente isolata, come fai a tirare fuori il codice finito? L'architettura risolve questo problema usando il workspace mounting. Si tratta di un meccanismo sicuro di filesystem passthrough. Quando inizializzi la sandbox, definisci una directory specifica sul tuo host che farà da workspace. Questa singola directory viene montata in modo sicuro nella microVM. Mentre l'agente scrive codice, lancia test o genera asset, li salva in questa directory di workspace. Il passthrough sincronizza questi file specifici di nuovo sul filesystem del tuo host in tempo reale. L'agente consegna l'output richiesto senza mai avere accesso al resto del tuo hard drive. Può tranquillamente rompere tutto all'interno della microVM, ma i tuoi file locali rimangono intatti.
Il concetto chiave è che l'isolamento, in questo contesto, non serve più solo a proteggere l'host da software esterni malevoli. Serve ad abilitare in sicurezza le operazioni di sistema imprevedibili e altamente privilegiate che un agente autonomo deve eseguire per essere davvero utile.
Se ti piacciono questi episodi e vuoi supportare il podcast, puoi cercare DevStoriesEU su Patreon.
Per questo episodio è tutto. Ci sentiamo alla prossima!
16
Creare team di agenti IA
3m 49s
Smetti di fare affidamento su un singolo modello IA per compiti complessi. Questo episodio introduce il framework Docker Agent, mostrando come comporre team specializzati di agenti definiti in YAML.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 16 di 18. Passi un enorme errore dell'applicazione a un singolo modello AI. Lui cerca di tenere a mente l'intera architettura, i log e la sintassi di destinazione tutti in una volta. A metà strada, si confonde e allucina un fix per un file completamente scollegato. Un modello generico che cerca di fare tutto porta a un context overload. Per risolvere problemi complessi in modo affidabile, devi creare dei team di agenti AI.
Il framework Docker Agent ti permette di definire team specializzati di agenti AI usando un semplice file di configurazione YAML. Invece di scrivere un unico system prompt monolitico, suddividi il workflow in ruoli distinti. Lo strutturi come una gerarchia. C'è un root agent che orchestra il workflow, e vari sub-agent che eseguono task specifici. Questo isola il contesto. Ogni sub-agent riceve solo le informazioni di cui ha bisogno per il suo lavoro specifico.
Prendi ad esempio un workflow di debugging. Hai bisogno di un team con due ruoli distinti. Primo, un bug investigator che analizza gli stack trace. Secondo, un fixer che riscrive materialmente il codice rotto. Definisci l'intera composizione di questo team in un file chiamato docker dash agent dot yml.
Inizi configurando il root agent in cima al file. Gli dai un nome, selezioni un modello linguistico di base e fornisci le system instruction. Il root agent fa da manager. La sua responsabilità principale non è risolvere il problema direttamente, ma delegare il lavoro. Istruisci il root agent a coordinare il bug investigator e il fixer in base agli input che riceve.
Poi, definisci i sub-agent all'interno dello stesso file YAML. Dichiari l'agente bug investigator. Gli assegni un modello che eccelle nel ragionamento e nella lettura dei log. Gli dai istruzioni rigide di leggere solo gli stack trace, identificare la funzione che fallisce e produrre in output una breve spiegazione del perché ha fallito. Poi, dichiari l'agente code fixer. Potresti assegnargli un modello ottimizzato specificamente per la code generation. Le sue istruzioni gli dicono rigorosamente di prendere una funzione che fallisce e restituire in output una versione corretta. Nessuna analisi dei log, solo code in e code out.
Quando esegui questo team, l'utente interagisce solo con il root agent. Passi al root agent un enorme dump dei log dell'applicazione. Il root agent valuta la request e legge le descrizioni dei sub-agent disponibili. Determina che il bug investigator è l'agente giusto per il primo step. Il root agent passa il dump dei log all'investigator. L'investigator elabora il rumore, trova una null pointer exception in una funzione specifica e restituisce solo quel dettaglio specifico.
Il root agent prende quell'informazione isolata e la passa all'agente code fixer. Il code fixer scrive la patch e la riconsegna al root manager, che poi ti restituisce il risultato finale e pulito.
Ecco il punto chiave. Il code fixer non vede mai l'enorme stack trace. Vede solo l'esatta funzione che deve fixare. Proteggi la context window del modello di coding filtrando il rumore in anticipo. Assegnando istruzioni ristrette e specifiche ai singoli sub-agent nel file YAML, impedisci ai modelli di andare fuori task. Il root agent gestisce la sequenza, e i sub-agent gestiscono l'esecuzione.
Strutturare gli agenti in modo gerarchico ti costringe a trattare l'AI come un'architettura a microservizi, assegnando limiti precisi a ciò di cui ogni singolo modello può occuparsi.
Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
17
Toolset e workflow degli agenti
3m 42s
Rendi i tuoi agenti IA realmente utili dando loro i giusti vincoli. Scopri come configurare i toolset del filesystem e imporre workflow di sviluppo strutturati in Docker Agent.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 17 di 18. Un agent AI con il modello linguistico più avanzato è praticamente inutile per lo sviluppo se non può leggere il tuo codice sorgente o eseguire la tua test suite. Senza funzionalità esterne, si limita a tirare a indovinare la sintassi. Gli Agent Toolset e i Workflow risolvono questo problema colmando il divario tra un semplice generatore di testo e un software engineer operativo.
Di default, un Docker Agent gira in un container isolato. Non ha idea di quali file esistano nella directory del tuo progetto. Per cambiare questa cosa, devi configurare l'array toolsets nel file YAML del tuo agent. I toolset sono funzionalità pre-confezionate che danno all'agent l'accesso diretto al suo ambiente host. Per un agent di sviluppo, in genere inietti due toolset principali: l'accesso al filesystem e l'accesso alla shell.
Il toolset del filesystem permette all'agent di leggere la tua directory tree, aprire i file sorgente e scrivere il codice su disco. Il toolset della shell permette all'agent di lanciare comandi da terminale. Senza l'array toolsets, il tuo agent è intrappolato in una scatola. Con questo, il tuo agent ha mani e occhi.
Tuttavia, dare mani e occhi a un agent è una ricetta per il caos se gli manca la disciplina. Un agent non strutturato potrebbe modificare un file, dare per scontato che abbia funzionato e riportare un successo senza mai controllare gli errori di sintassi. Puoi controllare questo comportamento usando il blocco instructions nel file YAML. Questo blocco non è il posto per suggerimenti vaghi. È dove definisci un workflow operativo rigoroso.
Il modo più affidabile per strutturare queste instructions è dividere i task dell'agent in quattro fasi obbligatorie: Analyze, Examine, Modify e Validate. Le scrivi direttamente nel blocco instructions, dicendo all'agent che deve completare una fase prima di passare alla successiva.
La prima è Analyze. L'agent legge il prompt dell'utente per capire la feature o il bug fix richiesto. Poi c'è Examine. Qui, istruisci l'agent a usare il suo toolset del filesystem per cercare nella tua codebase, trovare i file rilevanti e leggere il loro contenuto per capire la logica attuale. La terza è Modify. L'agent scrive il codice aggiornato sul disco.
Questa è la parte che conta. La quarta fase è Validate. È qui che obblighi l'agent a dimostrare il suo lavoro usando il toolset della shell. Prendi ad esempio un agent esperto sviluppatore Go. Nella sezione Validate delle tue instructions, imponi esplicitamente che l'agent debba lanciare il comando go test punto slash punto punto punto, seguito da golangci trattino lint run.
Dato che l'agent ha accesso alla shell, esegue esattamente quei comandi. Se il compilatore Go lancia un errore di sintassi, o se un test fallisce, il toolset riporta quell'output del terminale direttamente all'agent. Dato che le tue instructions dicono che il task non è completo finché la validazione non passa, l'agent è costretto a leggere l'errore, tornare in loop alla fase di Modify, sistemare il codice e lanciare di nuovo i test. Ripeterà questo ciclo finché il linter non è felice e i test non passano.
Fornire l'accesso al filesystem e alla shell rende il tuo agent capace di scrivere software. Ma strutturare le sue instructions per richiedere l'esecuzione esplicita dei test rende il tuo agent affidabile. Vincoli i suoi tool a un rigoroso loop di validazione, così non devi mai revisionare codice rotto. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
18
Modelli IA in Compose
3m 18s
Tratta i tuoi LLM locali proprio come qualsiasi altra dipendenza dell'applicazione. Scopri come dichiarare, configurare e collegare i modelli IA direttamente all'interno del tuo file YAML di Docker Compose.
Ciao, sono Alex di DEV STORIES DOT EU. Docker Masterclass, episodio 18 di 18. La tua applicazione dipende da un Large Language Model locale. Probabilmente avvii un inference engine esterno, configuri manualmente la rete e inietti gli URL degli endpoint a mano. Funziona, ma compromette la riproducibilità isolata del tuo ambiente. Il tuo modello AI è solo un'altra dipendenza, e il suo posto è nel tuo file di configurazione, proprio accanto al tuo database. Questo è esattamente ciò che ottieni con l'elemento models di primo livello in Docker Compose.
A partire dalla versione 2.38 di Compose, i models sono un concetto nativo. In precedenza, far girare un modello locale significava scrivere complesse definizioni dei service per un inference engine, esporre manualmente le porte e configurare i network bridge affinché il tuo application container potesse comunicare con esso. Il nuovo blocco models elimina questa complessità trattando il modello AI come un elemento infrastrutturale distinto.
Aggiungi un blocco models al primo livello del tuo file, con la stessa indentazione di services e volumes. Al suo interno, dai un nome al tuo modello. Usiamo ai/smollm2 per una semplice chat app. Sotto questo nome, dichiari l'identificativo effettivo del modello di cui fare il pull. È qui che definisci anche i vincoli hardware e i parametri dell'engine. Puoi impostare la context size per limitare l'uso della memoria. Se l'engine sottostante richiede parametri di avvio specifici, li definisci usando i flag di runtime. La configurazione del modello è isolata e chiara.
Successivamente, fai il binding della tua applicazione al modello. All'interno del blocco services, individui il service della tua chat app e aggiungi un array models. Usando la short syntax, elenchi semplicemente ai/smollm2. Non devi configurare le dipendenze manualmente o impostare alias di rete personalizzati.
Ecco il punto chiave. Quando usi questa short binding syntax, Compose si occupa dell'orchestrazione. Fa il provisioning dell'inference engine corretto dietro le quinte per servire il modello specificato. Ancora più importante, genera automaticamente le variabili d'ambiente standard e le inietta direttamente nel container della tua chat app. Il tuo codice si avvia con variabili come OPENAI_BASE_URL già popolate, che puntano perfettamente all'endpoint interno del modello.
Esegui un singolo comando docker compose up. Compose fa il pull del modello smollm2, configura l'engine, avvia il tuo chat service e stabilisce la connessione. Niente chiavi API manuali, niente indirizzi IP interni da indovinare. Tutto viene instradato correttamente out of the box.
Ti incoraggio a esplorare la documentazione ufficiale e a provare a scrivere uno di questi file tu stesso. Dato che questo conclude la nostra serie di masterclass, sentiti libero di visitare devstories dot eu per suggerire argomenti per qualsiasi cosa tratteremo in seguito. Elevando i modelli AI a elementi nativi nella tua configurazione, la tua infrastruttura diventa completamente dichiarativa, garantendo che la versione esatta del modello che il tuo codice si aspetta sia sempre quella che viene avviata. Per oggi è tutto. Grazie per l'ascolto: vai a creare qualcosa di fantastico.
Tap to start playing
Browsers block autoplay
Share this episode
Episode
—
Copy this episode in another language:
Questo sito non utilizza cookie. Il nostro fornitore di hosting potrebbe registrare il tuo indirizzo IP a fini statistici. Scopri di più.