Il design di una pipeline comincia prima della prima riga di YAML: comincia dal dove gira il codice. La scelta fra GitHub Actions e Azure Pipelines, e fra compute hosted e self-hosted, determina per anni costo ricorrente, latenza delle build, superficie di attacco e carico operativo del team di piattaforma. Su AZ-400 la domanda non è “come registro un agent”, ma “dato questo vincolo di rete, licenza o budget, quale modello di compute regge”. Diamo per acquisiti i meccanismi base e concentriamoci sulle decisioni di design e sui trade-off.

Prima decisione: GitHub Actions o Azure Pipelines

Il discriminante non è la sintassi del workflow, ormai molto simile. È il modello di consumo, di governance e di rete.

Azure Pipelines ragiona per parallel job: la capacità di esecuzione è una risorsa acquistata a livello di organizzazione e condivisa da tutti i progetti, con tier separati per Microsoft-hosted e self-hosted. Non puoi dedicare capacità a un progetto o a un pool specifico: se un team satura i parallel job, gli altri accodano. Il free tier Microsoft-hosted per progetti privati concede un job che gira fino a 60 minuti per volta, con un tetto mensile di 1.800 minuti; il tier a pagamento porta il limite a 360 minuti per job e rimuove il tetto mensile. Le nuove organizzazioni hanno inoltre un limite iniziale di 25 parallel job Microsoft-hosted, elevabile via supporto.

GitHub Actions ragiona per minuti: i runner hosted sono fatturati al minuto di esecuzione, mentre i runner self-hosted sono gratuiti (paghi solo l’infrastruttura sottostante). I larger runner aggiungono più CPU, RAM e disco, autoscaling, runner group, opzioni GPU e immagini custom, ma sono fatturati al minuto e non rientrano nei minuti inclusi dei repository privati.

Due elementi spostano l’ago della bilancia. Primo: i progetti pubblici di Azure DevOps sono in ritiro e dal 2027 quelli esistenti diventano privati; per l’open source la destinazione indicata è GitHub. Secondo: Azure Pipelines offre ora un pool di GitHub-hosted agent con fatturazione al minuto, sulla stessa infrastruttura dei larger runner di GitHub Actions. Il confine fra i due prodotti si assottiglia sul piano del compute: ragiona su dove vive il codice, quale governance ti serve e quale modello di fatturazione regge il tuo profilo di carico.

Hosted: cosa compri davvero

Con il compute hosted compri manutenzione zero e isolamento per job. Le immagini sono patchate dal provider; ogni job parte su una VM nuova, e su Azure Pipelines la work directory non viene conservata fra le esecuzioni. È simultaneamente il pregio (nessuna contaminazione fra build, nessun agent “sporco” da diagnosticare) e il limite: nessuna cache a livello di macchina, quindi ogni run ripaga il ripristino delle dipendenze.

Il vincolo che più spesso decide il design è però la connettività. I pool Microsoft-hosted hanno per default visibilità verso Azure e verso endpoint pubblici; se le risorse target vivono in una virtual network puoi recuperare gli agent IP range pubblicati e aprire le regole firewall di conseguenza. Ma quando il target è on-premises dietro firewall intermedi la documentazione è esplicita: servono agent self-hosted installati in rete, con visibilità verso i sistemi target e accesso a internet verso il servizio. Analogamente, i GitHub-hosted agent di Azure Pipelines instradano il traffico sulla rete pubblica e non possono usare ExpressRoute o VPN.

Regola di design: se il requisito parla di “line of sight” verso sistemi non raggiungibili da internet, la risposta hosted pura è sbagliata a prescindere dal costo.

Self-hosted: controllo in cambio di responsabilità

Il self-hosted si giustifica su quattro driver, in ordine di frequenza d’esame: connettività privata, software licenziato o pesante da installare a ogni run, cache a livello macchina (su Azure Pipelines le cache e la configurazione persistono da run a run, con guadagni di velocità reali) e hardware o immagini specifiche, incluse immagini hardened secondo benchmark CIS che gli hosted non garantiscono.

Il prezzo è la manutenzione: sei tu responsabile dell’aggiornamento del sistema operativo e di tutto il software installato. Due caratteristiche architetturali vanno però ricordate perché smontano obiezioni comuni.

Il traffico è in uscita. Un runner GitHub self-hosted richiede connessioni HTTPS in uscita sulla porta 443; un agent Azure Pipelines self-hosted richiede accesso al servizio più visibilità verso i target. Non serve aprire porte in ingresso: l’argomento “non possiamo esporre la rete” non è una controindicazione al self-hosted, è esattamente il motivo per cui esiste.

La sicurezza dipende dall’effimero. GitHub è netto: i runner self-hosted non danno garanzie di girare su VM pulite ed effimere e possono essere compromessi in modo persistente da codice non fidato in un workflow; per questo non dovrebbero quasi mai servire repository pubblici, dove chiunque può aprire una pull request. Le contromisure sono i runner effimeri o just-in-time, che eseguono al più un job e vengono rimossi, e i runner group a livello di organizzazione o enterprise. GitHub raccomanda l’autoscaling con runner effimeri e lo sconsiglia con runner persistenti.

Fra “hosted puro” e “flotta di VM da patchare a mano” esiste una fascia intermedia che all’esame è spesso la risposta corretta. Su Kubernetes, Actions Runner Controller (ARC) è un operator che scala runner self-hosted effimeri tramite runner scale set, con un listener pod che mantiene la connessione HTTPS verso GitHub: gira su qualunque cluster, incluso AKS. Su Azure DevOps, i Managed DevOps Pools sono un servizio gestito in cui le VM vivono in una subscription Microsoft: immagini custom, scalabilità, accesso alle risorse della rete aziendale oppure isolamento verso endpoint specifici, mantenimento dello stato fino a sette giorni per sfruttare le cache, job fino a due giorni, integrazione con Key Vault e supporto proxy. È l’opzione da proporre quando il requisito è “rete privata più immagine custom, ma senza flotta da manutenere”.

Job in container e scenari ibridi

Il container job disaccoppia il toolchain dall’immagine dell’agent: l’agent scarica e avvia il container, poi esegue gli step al suo interno. Serve quando ti servono versioni esatte di OS e dipendenze, o la stessa build su più runtime in matrice.

I vincoli sono precisi e vengono chiesti. Su Azure Pipelines: solo pipeline YAML (le classic non li supportano), agent hosted Windows o Ubuntu — macOS non supporta i container — Docker installato con accesso al daemon, e l’agent deve girare direttamente sull’host, perché i container annidati non sono supportati. Per i container Linux servono bash, base glibc, assenza di ENTRYPOINT e capacità di eseguire Node.js; le immagini stripped-down tipo Alpine richiedono setup aggiuntivo. Per i container Windows servono Windows Server 1803 o superiore e versioni di kernel host e container coincidenti. I registry privati si raggiungono con una service connection: Azure Container Registry è supportato, mentre per Amazon ECR non è possibile configurare una service connection. Su GitHub Actions vale un vincolo analogo: con i runner hosted, job container e service container richiedono un runner Ubuntu.

Lo scenario ibrido è quasi sempre la risposta a requisiti misti: pool hosted per build e test che consumano solo dipendenze pubbliche, pool self-hosted o gestito per gli stage di deploy che devono parlare con sistemi privati. Su Azure Pipelines il routing verso l’agent giusto passa da capabilities e demands, pensati proprio per i self-hosted; sugli hosted si sceglie invece l’immagine. Su GitHub l’equivalente sono le label dei runner.

Come cade all’esame

  • “Le build devono raggiungere un database on-premises non esposto a internet” → agent o runner self-hosted in rete, che comunicano in uscita verso il servizio; non hosted con IP range in allowlist.
  • “Servono immagini custom e accesso alla rete aziendale, ma il team di piattaforma non vuole gestire VM”Managed DevOps Pools su Azure DevOps, o runner effimeri via ARC su AKS; non una scale set self-gestita.
  • “Repository pubblico con contributi esterni: come eseguiamo le build in sicurezza”runner hosted; i self-hosted sono sconsigliati sui repository pubblici per rischio di compromissione persistente.
  • “L’autoscaling dei runner deve minimizzare il rischio di contaminazione fra job” → runner effimeri / just-in-time più runner group per limitare l’accesso; non runner persistenti riusati.
  • “Le build superano il limite di durata del job hosted”spezzare la pipeline in più job, oppure passare a compute senza limiti di tempo (self-hosted) o con finestre più ampie.
  • “La stessa suite va eseguita su tre versioni di runtime senza moltiplicare le immagini agent”container job in matrice, con i vincoli di OS e Docker del pool scelto.
  • “Un job in container deve girare su agent macOS” → richiesta impossibile: gli agent macOS non supportano i container.