Container e servizi Docker: capire Docker con gli hamburger

Che cosa significa davvero “un menu hamburger, per favore”?

L’ultima volta abbiamo usato un Dockerfile per creare la nostra “immagine Docker (Image)”. È come una “ricetta che non cambia mai”, in cui è scritto l’ordine esatto con cui mettere il patty sul pane e aggiungere la lattuga sopra.

Ora è il momento di usare quella ricetta per avviare l’attività reale, cioè il servizio. Ma quando si inizia a usare Docker compaiono termini che confondono. Container, Service, Stack… sembrano tutti simili, quindi qual è esattamente la differenza?

Se non si afferrano bene questi concetti, più avanti, scrivendo script di deployment, ci si ritrova a vagare chiedendosi: “Perché ho avviato il container ma il servizio non è salito?” Per mettere ordine oggi in questa genealogia, ho portato il linguaggio universale del mondo: l’analogia dell’hamburger shop.

Prepari un hamburger, il container, seguendo la ricetta, l’immagine, gestisci lo stesso menu come servizio e lo vendi in combinazione come stack.

1. Container: l’hamburger preparato dopo l’ordine

  • Definizione: è l’“entità concreta” che si ottiene eseguendo un’immagine e caricandola in memoria.
  • Caratteristiche: al cliente non si può servire il foglio della ricetta (l’immagine). Bisogna dargli un “hamburger (container)” bello caldo, preparato seguendo la ricetta.
  • Comando: docker run my-burger

Se avvio un’istanza server, significa che ho eseguito “un container”.

2. Service: la gestione delle scorte dello stesso menu

Arriva l’ora di pranzo e i clienti si riversano dentro. Con la velocità di cuocere un hamburger per volta non si riesce più a stare dietro agli ordini. Così decidiamo di aumentare il personale in cucina e di preparare in anticipo tre “cheeseburger”. A quel punto non sto più gestendo “un cheeseburger”, ma l’intera “linea cheeseburger”, cioè il Service.

  • Definizione: un “insieme di container” creati dalla stessa immagine.
  • Caratteristiche: puoi assegnargli una “policy” come “tieni sempre 3 pezzi di scorta di questo menu (Replicas=3)”. Se un hamburger cade per terra (errore), ne cuoce subito uno nuovo da solo.
  • Scopo: la “scalabilità orizzontale (Scale-out)”. Distribuzione del traffico e stabilità.

3. Stack: il menu combinato, cioè il pacchetto completo

Un hamburger da solo non basta. Servono anche le patatine, la DB, e una cola, Redis. Solo quando tutte queste cose si uniscono si ottiene finalmente “un pasto completo”, cioè l’applicazione.

  • Definizione: il “gruppo di livello più alto” che riunisce servizi diversi tra loro (web, DB, cache).
  • Caratteristiche: hamburger e cola sono più comodi da mangiare se stanno sullo stesso vassoio (connessione di rete). Una volta raggruppati in uno stack, si crea uno spazio dedicato (Network) in cui possono comunicare tra loro.

Il problema: mi manca il fiato solo a fare l’ordine

Il concetto è chiaro, ma quando bisogna metterlo davvero in produzione viene il mal di testa. Il servizio web, cioè lo stack, che vogliamo costruire di solito ha bisogno di componenti come questi.

  1. Spring Boot (Burger): 2 piatti principali (ridondanza)
  2. PostgreSQL (Fries): 1 contorno
  3. Redis (Coke): 1 bevanda

Avviare tutto questo con comandi Docker grezzi, docker run, è come fare un ordine al banco in questo modo.

“Scusi, vorrei un ‘cheeseburger (Spring)’, però il patty lo cuocia medium-well, senza cetriolini e con molta salsa. Ah, e mi dia anche delle ‘patatine (DB)’, ma senza sale e con tre bustine di ketchup. Infine, per la ‘cola (Redis)’, la voglio zero e con solo metà ghiaccio. E per favore metta tutte e tre queste cose su ‘un unico vassoio (Network)’.”

# In realta bisogna digitare questi comandi ogni volta
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...

Riusciresti davvero a recitare questo lungo ordine senza un errore ogni volta che fai il deployment? E se sbagliassi la sequenza e le patatine arrivassero dopo l’hamburger? Il cliente, cioè l’utente, si arrabbierebbe.

Il salvatore: Docker Compose – “Il solito, grazie!”

Per questo gli sviluppatori hanno pensato: “E se scrivessimo in anticipo questo ordine complicato su un ‘menu, documento’, così da poter dire semplicemente: ‘Mi dia il menu numero 1’?”

Così è nato “Docker Compose”. Se definiamo il nostro “menu combinato personalizzato” in un file chiamato docker-compose.yml, Docker si occupa di mandare l’ordine in cucina e disporre tutto in ordine sul vassoio.

Anche un ordine personalizzato complesso si risolve con una sola frase, “prendo questo”, se è già scritto sul menu.

[Action] Crea il tuo menu combinato

Non fermiamoci alle parole: avviamo davvero, in un colpo solo, il server Spring e la DB presenti in locale. Tutto quello che devi fare è creare un file docker-compose.yml nella root del progetto e incollare il contenuto qui sotto.

version: '3.8'

services:
  # 1. Patatine (DB)
  my-database:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    ports:
      - "5432:5432"

  # 2. Cheeseburger (Web)
  my-web-server:
    image: my-spring-app:latest
    ports:
      - "8080:8080"
    depends_on:
      - my-database # "Le patatine devono essere pronte prima che esca il burger" (ordine garantito)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

Ora pronuncia la formula magica nel terminale.

# "Un menu 1 (docker-compose.yml), per favore!"
$ docker-compose up -d

Risultato: Docker legge quel file-menu, avvia prima la DB e poi, quando la DB è pronta, avvia il server Spring. Questo è esattamente l’inizio del concetto di IaC, Infrastructure as Code, cioè gestire l’infrastruttura tramite codice. D’ora in poi, anche una configurazione server complessa si risolve condividendo un solo “file-menu”.

In chiusura: Works on My Machine? Works on ANY Machine!

La scusa con cui è iniziata questa serie, “ma sul mio computer funziona…”, non regge più. Abbiamo capito Linux, l’OS, isolato gli ambienti con Docker, il Container, e definito servizi complessi in un unico file con Docker Compose.

Ora, purché abbia il file docker-compose.yml che ho scritto, posso preparare esattamente lo stesso menu hamburger sul mio computer, su quello del mio team lead o su un server AWS in appena un minuto.

Non siamo più “semplici coder”. Siamo diventati “ingegneri” capaci di progettare non solo il codice che scriviamo, ma anche il mondo, l’ambiente, in cui quel codice verrà eseguito. Con questo si chiude la serie [Works on My Machine].

Dalla prossima serie in poi entreremo nel mondo di [The Pipeline: automazione CI/CD], dove affideremo alle macchine il noioso processo di deployment sopra questa infrastruttura solida.

Lascia un commento