Cos'è una pipeline CI/CD (e perché il deploy manuale costa più di quanto sembra) - init.d
EN

# Cos'è una pipeline CI/CD (e perché il deploy manuale costa più di quanto sembra)

Alessandro Corbelli~8 min
Table of Contents

In molte aziende il rilascio di un sito o di un’applicazione è ancora un gesto manuale. Una persona apre il terminale, lancia una sequenza di comandi che ha imparato a memoria, aspetta, ricarica la pagina e incrocia le dita. Se il sito si vede, si passa ad altro. Se non si vede, comincia la caccia: cosa è cambiato, chi ha toccato cosa, com’era prima.

Il problema non è la competenza di chi lancia i comandi. Il problema è che un rilascio fatto a mano è una procedura non scritta: leggermente diversa ogni volta, impossibile da controllare prima, e legata a una persona sola. Quando quella persona è in ferie, ha fretta o cambia lavoro, il rilascio diventa un rischio.

Una pipeline CI/CD è la risposta a questo problema. È l’idea di scrivere una volta per tutte cosa deve succedere quando il codice cambia, e lasciare che sia una macchina a eseguirlo, sempre allo stesso modo, verificando prima di pubblicare.

Integrazione continua: il codice viene controllato da qualcun altro

La prima metà della sigla è CI, continuous integration, integrazione continua. L’idea è semplice: ogni volta che qualcuno salva il proprio lavoro nel repository condiviso, una macchina neutra scarica il progetto da zero, installa tutto quello che serve e esegue i controlli.

Quel “da zero” è la parte che conta. Sul computer di chi sviluppa il progetto funziona anche grazie a cose installate mesi prima e dimenticate: una libreria di sistema, una variabile d’ambiente, un file di configurazione mai versionato. La macchina della CI non ha quella memoria. Se il progetto funziona lì, funziona davvero, e la frase più cara al settore, “sul mio computer funziona”, smette di essere un’opinione.

Il vantaggio pratico è il momento in cui scopri il problema. Un test che fallisce due minuti dopo il salvataggio costa cinque minuti di lavoro, perché hai ancora in testa cosa stavi facendo. Lo stesso problema scoperto in produzione tre settimane dopo costa una giornata, più la telefonata del cliente.

Consegna continua: dal codice verificato al sito online

La seconda metà è CD, e vale la pena distinguere due significati che spesso si confondono.

La continuous delivery, consegna continua, significa che il codice verificato è sempre pronto per andare in produzione, e la pubblicazione avviene quando qualcuno preme un pulsante. Il continuous deployment, distribuzione continua, va oltre: se tutti i controlli passano, la pubblicazione avviene da sola, senza intervento umano.

Non sono livelli di bravura, sono scelte diverse: un e-commerce che rilascia venti volte al giorno guadagna dalla distribuzione automatica, uno studio che aggiorna il gestionale di un cliente una volta al mese vive benissimo con il pulsante. Quando si parte conviene il pulsante: si ottiene quasi tutto il valore, e si mantiene il controllo su quando le cose cambiano.

Com’è fatta una pipeline, in concreto

Una pipeline è un file di testo, versionato insieme al codice, che descrive i passi da eseguire. Ecco la versione minima con GitHub Actions, uno dei sistemi più diffusi:

name: CI
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test

Letto in italiano: a ogni salvataggio (on: push), accendi una macchina Linux pulita, scarica il codice, installa Node nella versione 22, installa le dipendenze esattamente come sono scritte nel file di blocco (npm ci, non npm install) ed esegui i test. Se un comando fallisce, la pipeline diventa rossa e chi ha fatto la modifica riceve una notifica.

Fissare la versione di Node non è pignoleria: è la stessa ragione per cui conviene dichiarare la versione del runtime nel progetto. Una pipeline che usa “l’ultima versione disponibile” un giorno si rompe da sola, senza che nessuno abbia toccato niente.

Da qui si cresce aggiungendo la pubblicazione, che avviene solo se i test sono passati e solo dal ramo principale:

deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Le due righe importanti sono needs: test, che impedisce di pubblicare codice non verificato, e if:, che limita la pubblicazione al ramo principale: il lavoro su un ramo secondario viene controllato ma non va online.

Lo stesso concetto, sistemi diversi

Il modo di scrivere cambia, l’idea no. La stessa pipeline su GitLab CI si scrive così:

stages:
- test
- deploy
test:
image: node:22
script:
- npm ci
- npm test
deploy:
stage: deploy
only:
- main
when: manual
script:
- ./deploy.sh

Qui when: manual è la consegna con pulsante di cui sopra: il lavoro viene preparato in automatico, la pubblicazione la decide una persona. Esistono anche Forgejo e Gitea, che ospitano il codice in casa con una sintassi quasi identica a quella di GitHub Actions, e sistemi storici come Jenkins che fanno la stessa cosa da vent’anni. La scelta dello strumento conta molto meno della decisione di avere una procedura scritta.

Un termine da conoscere: la macchina che esegue i passi si chiama runner. Può essere fornita dal servizio, e paghi al minuto, oppure essere una tua macchina se il codice non deve uscire dalla rete aziendale. Nel secondo caso va installata, aggiornata e isolata, perché esegue codice a ogni modifica del progetto.

Cosa conviene metterci dentro

L’ordine giusto dei passi va dal più veloce al più lento, così un errore banale non ti fa aspettare dieci minuti prima di saperlo:

  1. Controlli di forma: formattazione e analisi statica, pochi secondi.
  2. Test automatici: il cuore della verifica.
  3. Compilazione: se il progetto non si compila, è inutile andare oltre.
  4. Controlli specifici del progetto: qui sta la parte che quasi nessuno sfrutta.
  5. Pubblicazione.

Il punto 4 merita un esempio, perché è dove una pipeline smette di essere generica e diventa tua. Un controllo è semplicemente un comando che esce con codice zero se va tutto bene e con un codice diverso se qualcosa non va:

#!/usr/bin/env bash
set -euo pipefail
if grep -rn "console.log" src/; then
echo "Trovate chiamate di debug dimenticate in src/"
exit 1
fi

Con la stessa logica si verificano cose che non sono codice: immagini troppo pesanti caricate per sbaglio, link interni che puntano a pagine inesistenti, file di traduzione disallineati tra due lingue, regole editoriali di un blog. Sono tutti errori che nessuno trova rileggendo, e che una macchina trova sempre.

I segreti, cioè la parte che si sbaglia più spesso

Per pubblicare servono credenziali: un token del provider, una chiave SSH, una password di database. Tre regole, in ordine di importanza.

Mai nel repository. Un token scritto in un file versionato resta nella cronologia anche dopo averlo cancellato: rimuoverlo dal file non lo rimuove dai salvataggi precedenti. Se succede, l’unica risposta seria è cambiare quel token.

Nel sistema CI, con le protezioni attive. Le due impostazioni da cercare sono il mascheramento, che impedisce alla credenziale di comparire nei registri di esecuzione, e la limitazione ai rami protetti. Senza quelle, il token è leggibile da chiunque possa modificare il file della pipeline.

Con il minimo dei privilegi. Il token che pubblica il sito deve poter pubblicare il sito, non amministrare l’intero account.

C’è un ultimo aspetto che si tende a ignorare: la pipeline installa dipendenze, e installare dipendenze significa eseguire codice scritto da altri su una macchina che ha in mano le credenziali di produzione. È il motivo per cui vale la pena sapere cosa succede quando un pacchetto viene compromesso e perché il runner andrebbe isolato dal resto dell’infrastruttura.

Cosa costa, detto onestamente

Una pipeline non è gratis: va scritta, va mantenuta, e ogni tanto si rompe per conto suo perché scade un token o cambia una versione. Su progetti minuscoli, con un solo sviluppatore e un rilascio ogni sei mesi, può non valerne la pena.

C’è poi un rischio culturale, ed è il più serio: una pipeline rossa da settimane non è una pipeline, è un semaforo guasto. Se il rosso diventa normale, le persone imparano a ignorarlo e i controlli non servono più a niente. Il verde deve essere lo stato ordinario: quando un controllo produce troppi falsi allarmi, o lo si sistema o lo si toglie, ma non lo si lascia lampeggiare.

Infine, la pipeline verifica solo ciò che le hai chiesto di verificare. Non sostituisce il ripristino di un backup, che va comunque provato davvero, né la possibilità di tornare indietro in fretta quando qualcosa passa lo stesso.

In sintesi

Una pipeline CI/CD trasforma il rilascio da procedura ricordata a memoria in un documento eseguibile: l’integrazione continua verifica ogni modifica su una macchina pulita, la consegna continua porta in produzione ciò che ha superato i controlli, con o senza un pulsante di mezzo. Si scrive in un file di testo versionato insieme al codice e funziona allo stesso modo su GitHub Actions, GitLab CI o un sistema ospitato in casa. Come per l’infrastruttura descritta in file di testo, il guadagno vero non è la velocità: è smettere di affidare a una persona sola qualcosa che l’azienda fa ogni settimana.

Fonti

Tux versione Gandalf, mascotte del blog init.d

init.d è il team di Alessandro Corbelli, sistemista Linux e sviluppatore backend con oltre vent’anni di esperienza. Progetta e gestisce infrastrutture cloud (Google Cloud, AWS, Azure), server farm e architetture ad alta disponibilità, e sviluppa software su misura in Laravel/PHP e Vue - dalla piattaforma di food delivery Take2Me ai gestionali dei nostri clienti. Su questo blog condividiamo note tecniche su Linux, sistemistica, sviluppo, DevOps ed e-commerce.


Altri articoli