Docker per Sviluppatori: Guida Rapida 2025

THEJORD Team6 min di lettura
dockerdevopscontainersdevelopment

Docker per sviluppatori: container, Dockerfile, Docker Compose. Guida pratica 2025 per containerizzare applicazioni e standardizzare ambienti di sviluppo.

Docker per Sviluppatori: Guida Rapida 2025

Hai mai sentito "sulla mia macchina funziona"? Docker risolve esattamente questo problema. Invece di installare dipendenze diverse su ogni computer, impacchetti la tua applicazione con tutto ciò che serve in un container che gira identico ovunque: sul tuo laptop, sul server di staging, in produzione. Vediamo come iniziare con Docker da sviluppatore nel 2025.

Concetti fondamentali

Prima di scrivere codice, chiariamo i termini:

  • Dockerfile: un file di testo con istruzioni per costruire un'immagine
  • Immagine: il "template" immutabile che contiene app + dipendenze
  • Container: un'istanza in esecuzione di un'immagine
  • Registry: dove si archiviano le immagini (Docker Hub, GitHub Container Registry)

Pensa al Dockerfile come a una ricetta, all'immagine come al piatto preparato, e al container come al piatto servito al cliente.

Installazione e primi passi

Installare Docker

# macOS/Windows: scarica Docker Desktop
# https://www.docker.com/products/docker-desktop

# Linux (Ubuntu/Debian)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Verifica installazione
docker --version
docker run hello-world

Comandi essenziali

# Scaricare un'immagine
docker pull node:20-alpine

# Vedere immagini locali
docker images

# Eseguire un container
docker run -it node:20-alpine sh

# Vedere container in esecuzione
docker ps

# Vedere tutti i container (anche fermi)
docker ps -a

# Fermare un container
docker stop container_id

# Rimuovere un container
docker rm container_id

# Rimuovere un'immagine
docker rmi image_id

Il tuo primo Dockerfile

Creiamo un Dockerfile per un'applicazione Node.js:

# Usa un'immagine base leggera
FROM node:20-alpine

# Crea directory di lavoro
WORKDIR /app

# Copia prima package.json per sfruttare la cache
COPY package*.json ./

# Installa dipendenze
RUN npm ci --only=production

# Copia il resto del codice
COPY . .

# Esponi la porta
EXPOSE 3000

# Comando di avvio
CMD ["node", "server.js"]

Build e run

# Costruisci l'immagine
docker build -t myapp:1.0 .

# Esegui il container
docker run -p 3000:3000 myapp:1.0

# In background (detached)
docker run -d -p 3000:3000 myapp:1.0

Best practice 2025

1. Usa immagini base leggere

# Evita (900MB+)
FROM node:20

# Preferisci (150MB)
FROM node:20-alpine

# Ancora meglio per produzione (50MB)
FROM node:20-alpine AS builder
# ... build steps ...

FROM gcr.io/distroless/nodejs20
COPY --from=builder /app /app
CMD ["server.js"]

Le immagini alpine e distroless riducono la superficie di attacco e i tempi di deploy.

2. Multi-stage build

Separa build e runtime per immagini più piccole e sicure:

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

Risultato: immagini 50-85% più piccole rispetto a build single-stage.

3. Non eseguire come root

FROM node:20-alpine

# Crea utente non-root
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app
COPY --chown=nodejs:nodejs . .

# Passa all'utente non-root
USER nodejs

CMD ["node", "server.js"]

Il 58% dei container in produzione gira ancora come root: non fare questo errore.

4. Usa .dockerignore

# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
*.md
Dockerfile
.dockerignore
coverage
.nyc_output
tests

Riduce dimensione immagine ed evita di esporre file sensibili.

5. Ordine layer per cache

# Prima le istruzioni che cambiano meno
FROM node:20-alpine
WORKDIR /app

# Package.json cambia raramente
COPY package*.json ./
RUN npm ci

# Codice cambia spesso - mettilo per ultimo
COPY . .

Docker riusa i layer cachati se non sono cambiati. Ordine corretto = build 70% più veloci.

6. Health check

FROM node:20-alpine
WORKDIR /app
COPY . .

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "server.js"]

Senza healthcheck, Docker non sa se l'applicazione è davvero funzionante.

Docker Compose per sviluppo locale

Docker Compose orchestra più container con un solo file:

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

volumes:
  postgres_data:

Comandi Compose

# Avvia tutti i servizi
docker compose up

# In background
docker compose up -d

# Rebuild e avvia
docker compose up --build

# Ferma tutto
docker compose down

# Ferma e rimuovi volumi
docker compose down -v

# Logs di un servizio
docker compose logs -f app

Pattern comuni per sviluppatori

Hot reload in sviluppo

# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app           # Monta codice sorgente
      - /app/node_modules # Esclude node_modules
    command: npm run dev

Dockerfile per sviluppo vs produzione

# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install  # Include devDependencies
COPY . .
CMD ["npm", "run", "dev"]

# Dockerfile (produzione)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]

Database locale con persistenza

services:
  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: localdev
    volumes:
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Debugging container

# Entra in un container in esecuzione
docker exec -it container_name sh

# Vedi logs
docker logs container_name
docker logs -f container_name  # follow

# Ispeziona container
docker inspect container_name

# Statistiche risorse
docker stats

# Vedi processi nel container
docker top container_name

Errori comuni da evitare

  • Immagini enormi: usa alpine, multi-stage build, e .dockerignore
  • Root in container: crea e usa un utente non-root
  • Secrets nel Dockerfile: mai ENV con password, usa secrets o variabili a runtime
  • Tag latest in produzione: usa sempre tag specifici (node:20.10.0-alpine)
  • Un processo per container: non mettere app + database nello stesso container
  • Ignorare healthcheck: aggiungi sempre controlli di salute

Conclusione

Docker ha rivoluzionato il modo in cui sviluppiamo e distribuiamo software. Nel 2025 non è più opzionale: è uno standard. I punti chiave per iniziare:

  • Parti da immagini alpine o distroless
  • Usa multi-stage build per produzione
  • Non eseguire mai come root
  • Sfrutta Docker Compose per sviluppo locale
  • Aggiungi sempre .dockerignore e healthcheck
  • Ordina le istruzioni Dockerfile per massimizzare la cache

Una volta che hai dockerizzato la tua app, il deploy diventa semplice: la stessa immagine gira identica su qualsiasi piattaforma che supporta container.