In Azure Pipelines, un approval che non viene completato entro il suo timeout marca lo stage come skipped, non come fallito. Uno stage skipped è verde nella maggior parte delle dashboard e dei template di notifica, quindi una promozione di modello che nessuno ha guardato può completare una pipeline run che riporta successo mentre la modifica non è mai uscita, o peggio, mentre uno stage precedente ne ha già fatto metà. La Parte 8 ha messo le applicazioni sopra il gateway. Questa parte porta il gateway stesso in produzione in modo ripetibile, e trasforma l'email di ritiro del modello della Parte 1 in una query sul database.

Cosa viene effettivamente deployato

Quattro artifact, che cambiano a tre ritmi diversi, ed è per questo che sono tre pipeline invece di una.

  • Infrastruttura di piattaforma dal 10-platform della Parte 2: API Management, AKS, rete, observability. Cambia poche volte l'anno, viene deployata con un approval umano, e nessuno ha fretta.
  • Model deployment e configurazione del gateway, il layer 20-models più la policy XML e la mappa degli alias. Cambia ogni settimana. È la pipeline che ha bisogno dell'eval gate.
  • Configurazione dei tenant, il layer 30-tenants. Cambia all'onboarding, ed è l'unico posto in cui una pull request self-service da un team consumer ha senso.
  • Il container del control plane su AKS. CI/CD applicativo ordinario, e il meno interessante dei quattro proprio perché è ordinario.

La policy XML merita una nota a parte. Viene eseguita a ogni richiesta, quindi è codice di produzione, e raggiunge la produzione attraverso la stessa pipeline con la stessa review del Terraform. Il portale serve per leggere, non per modificare, e il modo per far rispettare questa regola è un drift check nella pipeline che fallisce quando la policy deployata differisce dal repository.

La service connection, e il timer che nessuno imposta

Le pipeline si autenticano verso Azure con una service connection Azure Resource Manager, e la configurazione raccomandata è la workload identity federation, con una app registration oppure una managed identity, che toglie di mezzo del tutto i segreti e la loro gestione. Per una piattaforma il cui intero argomento della Parte 5 riguardava il ciclo di vita delle credenziali, usare qui un segreto salvato sarebbe difficile da difendere.

Due dettagli operativi vale la pena metterli nel runbook invece di scoprirli. Azure Pipelines disabilita automaticamente le service connection non usate per 100 giorni, e serve un amministratore della service connection o un Project Administrator per riabilitarle. Una pipeline che gira solo per le modifiche infrastrutturali trimestrali è esattamente la pipeline che ci inciampa, e il fallimento arriva nel momento peggiore, quando qualcosa di urgente deve essere deployato. E una service connection convertita da segreto a federation può essere riportata indietro entro sette giorni; dopo, un nuovo segreto va creato a mano, e le connection convertite e poi modificate manualmente non possono essere riportate indietro dallo strumento in alcun modo.

L'eval gate appartiene all'environment, non al job

L'istinto è aggiungere uno step di eval alla fine del deploy job. Così l'eval diventa una cosa che la pipeline fa, il che significa che l'autore della pipeline può riordinarlo, saltarlo con una condizione o spostarlo dopo il deployment che avrebbe dovuto controllare.

Azure Pipelines ha la primitiva giusta: i check sull'environment o sulla service connection che lo stage consuma. Prima che uno stage giri, tutti i check su tutte le risorse che usa devono essere soddisfatti, e una singola decisione negativa nega lo stage. Configura l'eval come un check Invoke REST API che chiama il servizio di eval del control plane, e il gate smette di appartenere alla definizione della pipeline e inizia ad appartenere all'environment.

Le regole di finalità qui contano. Le decisioni degli approval e della maggior parte dei check sono definitive. I check Invoke Azure Function e Invoke REST API sono l'eccezione, possono essere rieseguiti, e se imposti un time between evaluations diverso da zero, la decisione del check diventa non definitiva e verrà rivalutata. Per un eval gate è una scelta di design, non un dettaglio: un check non definitivo che continua a rigirare è quello giusto per "l'eval deve essere ancora verde quando questo stage finalmente parte", e uno definitivo è quello giusto per "questa build ha passato le sue eval, spediscila". Scegline uno, e sappi che la forma asincrona configurata nel modo raccomandato è definitiva.

Sopra l'eval aggiungi un approval umano, sull'environment di produzione, con l'opzione restrict approvers from approving their own runs attivata. E imposta il timeout deliberatamente, per via del lead di questo post: un approval che scade marca lo stage come skipped, e skipped si legge come verde. La mitigazione è un timeout corto, alert mirati proprio sugli stage skipped, e non trattare mai una pipeline verde come prova che un deployment sia avvenuto.

Cosa controlla davvero l'eval

Un golden set di casi che interessano ai consumer della piattaforma, versionato nel repository accanto alla policy XML, eseguito contro la configurazione candidata attraverso il gateway invece che contro un endpoint del modello. Quest'ultimo punto è ciò che lo rende un eval di piattaforma e non un benchmark del modello: esercita la risoluzione degli alias, le policy, il filtro di content safety e il percorso di retrieval, tutte cose che possono rompere una risposta senza che il modello cambi affatto.

Tre soglie, e la terza è quella che la gente salta. Un punteggio di qualità aggregato sul golden set. Un budget di latenza, perché un modello marginalmente migliore e sensibilmente più lento non è un upgrade per un assistente interattivo. E una lista di regressioni: casi specifici che non devono peggiorare, indipendentemente dalla media. Una nuova versione del modello che migliora la media rompendo i tre prompt da cui dipende il team del customer service è la forma normale di una promozione sbagliata, e solo la lista di regressioni la intercetta.

Promozione, canary e rollback sono un unico meccanismo

Poiché la Parte 3 ha messo un alias davanti a un backend pool, un cambio di versione del modello non tocca nessuna applicazione. È una modifica di configurazione con tre forme:

  • Canary: aggiungi il nuovo deployment al pool dell'alias con un peso basso nello stesso priority group. Una piccola quota di traffico ci finisce, le metriche della Parte 5 mostrano le differenze di latenza e di token per model alias, e niente ha dovuto essere rideployato.
  • Promozione: sposta i pesi, poi rimuovi il vecchio deployment dal pool.
  • Rollback: il cambio di pesi inverso. Secondi, non un redeploy, che è l'intero argomento per cui l'alias esiste.

L'unica cosa che questo non fa è rendere le risposte identiche attraverso il confine del canary. Due versioni del modello che servono lo stesso alias producono output diversi per lo stesso prompt, quindi qualsiasi cosa che mette in cache, confronta o rigioca le risposte deve sapere quale versione ha risposto. È un campo di log, aggiunto nello stesso momento del canary, non dopo.

Il ritiro, che ora è una query

L'email di ritiro della Parte 1 è costata due giorni di grep sui repository. Ora dovrebbe costare una query, perché tre decisioni precedenti hanno reso la risposta conoscibile. La Parte 2 ha bloccato le versioni dei modelli e impostato version_upgrade_option deliberatamente, quindi nessun deployment si muove da solo. La Parte 3 ha messo ogni applicazione dietro un alias, quindi la mappatura da alias a deployment è un dato. La Parte 5 ha loggato ogni richiesta con il suo tenant e il suo modello risolto, quindi l'insieme dei tenant che hanno davvero usato un deployment negli ultimi 30 giorni è una query sui log invece che un'assunzione.

Il runbook del ritiro diventa allora quattro passi che stanno in un ticket: interrogare quali tenant hanno usato il deployment di recente; tirare su il deployment sostitutivo ed eseguire l'eval gate contro di esso; fare canary sull'alias e guardare la lista di regressioni; commutare e rimuovere. Le applicazioni non vengono mai a sapere che è successo qualcosa, che è il risultato per cui l'intera serie esiste.

Modalità di fallimento da tenere d'occhio

  • Skipped letto come riuscito. Un approval scaduto salta lo stage. Fai alert esplicitamente sullo stato skipped, nella notifica della pipeline e in qualunque dashboard il team guardi davvero.
  • L'eval come step del job. Se può essere riordinato o disattivato con una condizione, è documentazione. Come check sull'environment è un gate.
  • Bypass senza visibilità. Bypassare un check richiede il permesso di amministratore sulla risorsa e viene registrato con chi l'ha fatto, il che è un bene, e vale la pena farlo emergere in una review settimanale invece di lasciarlo nel pannello dei check.
  • Una service connection disabilitata a 100 giorni. La pipeline infrastrutturale trimestrale lo scopre durante un incident.
  • Modifiche alla policy dal portale. Senza un drift check, un hotfix fatto nel portale sopravvive finché il deploy successivo non lo annulla in silenzio, che è il tempismo peggiore possibile.
  • Un eval set che nessuno aggiorna. Un golden set fermo da un anno sta misurando il prodotto dell'anno scorso.

Cosa eredita la Parte 10

Un gateway che va in produzione tramite pipeline, con le eval come gate e i cambi di modello come configurazione. Ogni meccanismo della serie è ora al suo posto, e ognuno emette qualcosa. L'ultima parte riguarda cosa farne: le trace, le eval in produzione invece che nella pipeline, le domande di governance che pone l'EU AI Act, e i numeri promessi dalla Parte 1.

Leggi questo dopo

Per il lato infrastruttura e piattaforma di come far girare tutto questo su scala, gli appunti tecnici sono su ercan.cloud, e l'hub è su ercanermis.com.

Riferimenti