Skip to main content
  1. Concetti/

Docker Image

·6 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

Cosa fa
#

Template immutabile da cui nascono i container. Contiene filesystem, dipendenze e istruzioni di avvio. L'immagine non cambia mai — ogni container che nasce da essa e' una copia fresca.


Analogia — la planimetria dell'appartamento
#

Un'immagine e' come la planimetria di un appartamento:

  • La planimetria (immagine) descrive com'e' fatto l'appartamento
  • Puoi costruire 10 appartamenti identici dalla stessa planimetria
  • Ogni appartamento (container) e' indipendente — se uno brucia, gli altri restano
  • La planimetria non cambia quando ci abiti dentro
graph LR
    Image[Immagine chatwoot:latest] -->|docker run| C1[Container 1 in esecuzione]
    Image -->|docker run| C2[Container 2 in esecuzione]
    Image -->|docker run| C3[Container 3 in esecuzione]
    C1 -->|modifica filesystem| FS1[Layer R/W temporaneo]
    C2 --> FS2[Layer R/W temporaneo]
    C3 --> FS3[Layer R/W temporaneo]

Layer — come e' costruita un'immagine
#

Un'immagine e' un stack di layer sovrapposti. Ogni istruzione nel Dockerfile aggiunge un layer. I layer sono condivisi tra immagini — se due immagini usano ubuntu:22.04 come base, quel layer esiste una volta sola su disco.

graph TD
    L1[Layer 1: ubuntu:22.04 base]
    L2[Layer 2: apt install nodejs]
    L3[Layer 3: COPY package.json]
    L4[Layer 4: npm install]
    L5[Layer 5: COPY app/]
    RW[Layer R/W: modifiche del container - temporaneo]

    L1 --> L2 --> L3 --> L4 --> L5 --> RW

I layer sotto sono read-only — immutabili. Solo il layer in cima e' scrivibile, e appartiene al container, non all'immagine. Quando il container viene eliminato, quel layer sparisce.


Comandi essenziali
#

docker images                          # lista immagini locali
docker pull chatwoot/chatwoot:latest   # scarica immagine dal registry
docker rmi chatwoot/chatwoot:latest    # rimuove immagine locale
docker image inspect chatwoot/chatwoot # dettagli completi
docker history chatwoot/chatwoot       # mostra i layer dell'immagine
docker image prune                     # elimina immagini non usate

Tag — versioni dell'immagine
#

# il tag e' la versione — dopo i due punti
chatwoot/chatwoot:latest    # ultima versione stabile
chatwoot/chatwoot:v3.11.0   # versione specifica
postgres:16                 # postgres versione 16
postgres:16-alpine          # postgres 16 su Alpine Linux (piu' leggero)

# latest non e' sempre sicuro in produzione:
# oggi latest = v3.11, domani latest = v4.0 con breaking changes
# in produzione: usa sempre un tag specifico
Warning

In produzione non usare mai :latest — fissa sempre una versione specifica. Un docker compose pull su :latest potrebbe portare una versione incompatibile e rompere tutto.


Aggiornamenti disponibili
#

docker ps mostra una U a fianco del nome container quando l'immagine ha un aggiornamento disponibile nel registry.

Per sapere quale immagine sta usando un container in esecuzione:

# dettaglio completo
docker inspect nome-container | grep Image

# leggibile: nome container → immagine corrente
docker ps --format "{{.Names}}: {{.Image}}"

Per scaricare le versioni aggiornate e riavviare:

docker compose pull   # aggiorna tutte le immagini del compose
docker compose up -d  # riavvia i container con le nuove immagini

Esplorazione interattiva
#

Prima di scrivere un Dockerfile, conviene esplorare l'immagine base in modo interattivo per capire cosa è già installato e quale configurazione serve:

# avvia un container temporaneo con shell interattiva
docker run -it --rm nome-immagine /bin/bash
Tip

Questo approccio riduce i tentativi nel Dockerfile: prima capisci cosa funziona in modo interattivo, poi lo trascrivi come istruzione RUN. Il container viene eliminato all'uscita (--rm).

Docker Entrypoint Override
#

A volte il comando sopra fallisce o non apre una shell. Questo succede quando l'immagine ha un ENTRYPOINT definito (es: ENTRYPOINT ["/entrypoint.sh", "app"]). In questo caso, /bin/bash viene passato come argomento allo script, non eseguito come comando.

Per "zittire" lo script e forzare l'accesso al sistema:

# Sovrascrive l'entrypoint originale
docker run -it --rm --entrypoint /bin/bash nome-immagine

# Per immagini Alpine Linux (che spesso non hanno bash)
docker run -it --rm --entrypoint /bin/sh nome-immagine

Perché è utile in ambito Security?
#

  1. Ispezione del Filesystem: Verificare se sono rimasti file temporanei, log o segreti (/tmp, /root/.npm).
  2. Verifica Permessi: Controllare con whoami e ls -l se i processi girano come root.
  3. Bypass delle Restrizioni: Se l'entrypoint esegue controlli di licenza o configurazione, l'override permette di analizzare il container "a cuore aperto".

Immagini orfane (dangling)
#

Le immagini orfane sono layer senza tag, prodotti da build successive della stessa immagine. Occupano disco senza essere usate da nessun container.

# lista solo immagini orfane
docker images -f dangling=true

# elimina solo le orfane
docker image prune

# elimina tutte le immagini non usate da container attivi (piu aggressivo)
docker image prune -a

# quanto spazio occupano immagini, container, volumi
docker system df
Warning

docker image prune -a elimina anche immagini valide non usate in questo momento — attenzione se hai container spenti che le usano.

Prima di fare qualsiasi prune, controlla cosa hai davvero in esecuzione e cosa e' fermo:

# mostra tutti i container (attivi e spenti) con la loro immagine
docker ps -a --format "{{.Names}}: {{.Status}}: {{.Image}}"

Cosi' sai esattamente quali immagini sono ancora necessarie per container in stato Exited che vuoi riavviare, e quali invece sono davvero abbandonate.


Docker Layer Caching e Ispezione Immagini
#

1. La Struttura a Layer (Livelli)
#

Ogni immagine Docker è una pila di layer immutabili e read-only. Ogni istruzione nel Dockerfile (RUN, COPY, ADD) crea un nuovo layer.

  • Layer di Contenuto (RUN, COPY): Aggiungono dati fisici e aumentano la dimensione dell'immagine (es. installazione di pacchetti).
  • Layer di Metadati (ENV, ARG, WORKDIR, ENTRYPOINT): Non aggiungono peso (0B), ma modificano il comportamento del runtime.

2. Il Comando docker history
#

È lo strumento di "archeologia digitale" per analizzare come è stata costruita un'immagine.

  • Utilizzo: docker history <nome-immagine>
  • Analisi Security: Permette di vedere se sono state eseguite operazioni pericolose (es. installazione di tool di rete poi rimossi) o se layer pesanti stanno occupando spazio inutilmente.
  • Interpretazione: Se vedi <missing>, significa che Docker sta usando BuildKit: i layer intermedi sono gestiti come differenze logiche e non hanno un ID immagine individuale, rendendo l'immagine finale più efficiente.

3. La Logica della Cache (Determinismo)
#

Docker è "pigro": prima di eseguire un comando, calcola un hash dell'istruzione e del contesto (file o variabili).

  • Se l'hash corrisponde a un layer esistente, Docker usa la Cache (HIT) e non esegue il comando.
  • Il problema del "Latest": Se usi latest o comandi che scaricano dati da internet, Docker non sa se il contenuto remoto è cambiato. Se l'istruzione nel Dockerfile rimane identica, Docker userà la cache, ignorando eventuali aggiornamenti disponibili online.

4. Il Flag --no-cache
#

Forza Docker ad ignorare completamente la cronologia dei layer locali.

  • Quando usarlo: Quando vuoi essere certo di applicare le ultime patch di sicurezza (apk upgrade) o scaricare l'ultima versione di un software CLI senza cambiare il Dockerfile.
  • Effetto: Invalida la cache per tutte le righe del Dockerfile.

5. Strategia: Cache Busting Mirato (Best Practice)
#

Invece di usare sempre --no-cache (lento e inefficiente), si usa un "interruttore" (Cache Buster) tramite gli ARG.

Dockerfile

FROM alpine:3.23
# Layer precedenti (Cache HIT)
RUN apk add --no-cache bash 

# Se cambi questo valore, Docker invalida la cache SOLO da qui in poi
ARG VERSION="1.0.1"
RUN npm install -g mio-tool@${VERSION}

Considerazioni del Security Analyst
#

  • Immutabilità: In produzione, preferire il Version Pinning (es. VERSION=1.0.1) rispetto a --no-cache con latest. Questo garantisce che l'immagine sia riproducibile e che il comportamento del software non cambi inaspettatamente tra una build e l'altra.

  • Pulizia: Ogni riga RUN che aggiunge e poi rimuove file (es. apk add npm && apk del npm) deve essere un'unica catena di comandi (&&). Se lo facessi in due righe separate, npm rimarrebbe per sempre nel layer precedente, occupando spazio e aumentando la superficie d'attacco.


Collegato a
#

  • dockerfile — come si costruisce un'immagine
  • docker-compose — come si usa un'immagine in un servizio
  • system — categoria

Related