Il canarino è un uccello piccolo che canta incessantemente. Questi uccelli sono sensibili al metano e al monossido di carbonio. Anche a basse concentrazioni di gas tossici nell'aria, possono svenire o morire. I cercatori d'oro e i minatori portavano i canarini con sé: finché cantano, si può continuare a lavorare; se smettono, è il segnale che c'è gas nella miniera e che è il momento di andarsene. I minatori sacrificavano i piccoli uccelli per tornare vivi in superficie.

Questa pratica è stata adottata anche nell'IT. Ad esempio, nell'attività standard di deployment di una nuova versione di un servizio o di un'applicazione in produzione, con test preliminari. L'ambiente di test può risultare troppo costoso, i test automatizzati non coprono tutto ciò che si vorrebbe e non testare può compromettere la qualità, il che è rischioso. In questi casi è utile l'approccio del Canary Deployment, dove una piccola quantità di traffico di produzione viene indirizzata verso la nuova versione. Questo metodo aiuta a verificare in modo sicuro la nuova versione in produzione, sacrificando poco per un grande obiettivo. Maggiori dettagli su come funziona l'approccio, i suoi vantaggi e come implementarlo saranno forniti da Andrey Markelov (), ad esempio, implementato nella società Infobip.
Andrey Markelov — ingegnere principale presso Infobip, lavora da 11 anni nello sviluppo di applicazioni Java nel settore finanziario e delle telecomunicazioni. Sviluppa prodotti Open Source, partecipa attivamente alla comunità Atlassian e scrive plugin per i prodotti Atlassian. Evangelista di Prometheus, Docker e Redis.

Informazioni su Infobip
È una piattaforma di telecomunicazione globale che consente a banche, rivenditori, negozi online e aziende di trasporto di inviare messaggi ai propri clienti tramite SMS, push, email e messaggi vocali. In questo settore, stabilità e affidabilità sono essenziali affinché i clienti ricevano i messaggi in tempo.
L'infrastruttura IT di Infobip in numeri:
- 15 data center in tutto il mondo;
- 500 servizi unici in esercizio;
- 2500 istanze di servizi, molte più delle squadre;
- 4,5 Tbyte di traffico mensile;
- 4,5 miliardi di numeri di telefono;
Il business cresce, così come il numero di release. Effettuiamo circa 60 release al giorno, perché i clienti desiderano più funzionalità e potenza. Ma non è facile: ci sono molti servizi e poche squadre. Bisogna scrivere velocemente codice che deve funzionare in produzione senza errori.
Release
Il nostro rilascio tipico avviene così. Ad esempio, ci sono i servizi A, B, C, D ed E, ognuno dei quali è sviluppato da un team separato.

A un certo punto, il team del servizio A decide di rilasciare una nuova versione, ma i team dei servizi B, C, D ed E non ne sono a conoscenza. Ci sono due possibilità per come procederà il team del servizio A.
Effettuerà un rilascio incrementale: prima sostituisce una versione, poi l'altra.

Ma c'è una seconda possibilità: il team troverà risorse e macchine aggiuntive, rilascerà una nuova versione e poi commuterà il router, e la versione inizierà a funzionare in produzione.

In ogni caso, dopo il rilascio ci sono quasi sempre problemi, anche se la versione è stata testata. Può essere testata manualmente, in modo automatizzato o non testata affatto — i problemi si presenteranno in ogni caso. Il modo più semplice e corretto per risolverli è tornare alla versione funzionante. Solo dopo si possono analizzare i danni, le cause e correggerle.
Quindi, cosa vogliamo?
Non vogliamo problemi. Se i clienti li scoprono prima di noi, danneggerà la nostra reputazione. Pertanto, dobbiamo trovare i problemi più velocemente dei clienti. Lavorando in anticipo, riduciamo al minimo i danni.
Allo stesso tempo, vogliamo accelerare il deployment, affinché avvenga in modo rapido, semplice e senza sforzo per il team. È importante tutelare ingegneri, DevOps e programmatori — il rilascio di una nuova versione è fonte di stress. Il team non è un materiale di consumo, cerchiamo di utilizzare razionalmente le risorse umane.
Problemi di deployment
Il traffico dei clienti è imprevedibile. Non è possibile prevedere quando il traffico dei clienti sarà minimale. Non sappiamo dove e quando i clienti avvieranno le loro campagne — potrebbe essere questa notte in India e domani a Hong Kong. Considerando il grande fuso orario, un deployment anche alle 2 di notte non garantisce che i clienti non ne risentano.
Problemi dei fornitori. I messaggeri e i fornitori sono nostri partner. A volte hanno dei malfunzionamenti che causano errori durante il deployment delle nuove versioni.
Team distribuiti. I team che sviluppano la parte client e il backend si trovano in diversi fusi orari. Questo spesso impedisce loro di mettersi d'accordo.
I data center non possono essere replicati in staging. In un data center ci sono 200 rack — replicarlo in un ambiente di test non è nemmeno lontanamente possibile.
Downtimenon è tollerabile! Abbiamo un livello di disponibilità accettabile (Error Budget) che implica un funzionamento del 99,99% del tempo, mentre il restante rappresenta un "diritto all'errore". Raggiungere un'affidabilità del 100% è impossibile, ma è fondamentale monitorare costantemente i crash e i downtime.
Opzioni classiche di soluzione
Scrivere codice senza bug. Quando ero un giovane sviluppatore, i manager venivano da me chiedendo di effettuare un rilascio senza bug, ma questo non è sempre possibile.
Scrivere test. I test funzionano, ma a volte non agiscono affatto come desidera il business. Generare profitto non è compito dei test.
Testare in staging. Nei 3,5 anni che ho lavorato in Infobip, non ho mai visto che lo stato dello staging corrispondesse, anche solo parzialmente, a quello della produzione.

Abbiamo persino cercato di sviluppare questa idea: prima avevamo uno staging, poi un pre-produzione, e poi un pre-pre-produzione. Ma anche questo non ha funzionato: non coincidevano nemmeno per capacità. Con lo staging possiamo garantire una funzionalità di base, ma non sappiamo come si comporterà sotto carico.
Il rilascio è fatto da chi ha sviluppato. È una buona pratica: anche se qualcuno modifica il titolo di un commento, lo aggiunge subito in produzione. Questo aiuta a sviluppare responsabilità e a non dimenticare le modifiche apportate.
Ci sono anche ulteriori complessità. Per lo sviluppatore è stressante dover trascorrere molto tempo a verificare tutto manualmente.
Rilascio coordinato. Questa opzione è solitamente proposta dalla dirigenza: «Concordiamo che ogni giorno testerete e aggiungerete nuove versioni». Questo non funziona: c'è sempre un team che aspetta gli altri o viceversa.
Smoke test
Un altro modo per risolvere i nostri problemi di deploy. Consideriamo come funzionano gli smoke test nel precedente esempio, quando il team A vuole implementare una nuova versione.
Inizialmente, il team implementa un'istanza in produzione. Con messaggi nell'istanza dai mock, si simula il traffico reale, in modo che corrisponda al traffico quotidiano normale. Se tutto va bene, il team commuta la nuova versione sul traffico degli utenti.

La seconda opzione è implementare con hardware aggiuntivo. Il team lo testa in produzione, poi commuta, e tutto funziona.

Svantaggi degli smoke test:
- Non ci si può fidare dei test. Dove reperire lo stesso traffico che si ha in produzione? Si può usare quello di ieri o di una settimana fa, ma non sempre corrisponde a quello attuale.
- Difficile da mantenere. Dovremo gestire gli account di test, ripristinandoli costantemente prima di ogni deploy, quando nel repository vengono inviate registrazioni attive. È più complicato che scrivere un test nel proprio sandbox.
L'unico vantaggio qui è che si può verificare le performance.
I rilasci Canary
A causa delle limitazioni dei test smoke, abbiamo iniziato a utilizzare i rilasci canary.
Una pratica simile a quella degli minatori che usavano canarini per indicare i livelli di gas ha trovato applicazione anche in IT. Facciamo passare un po' di traffico di produzione reale sulla nuova versione, cercando di mantenere i requisiti del Service Level Agreement (SLA). Lo SLA rappresenta il nostro "diritto all'errore", che possiamo utilizzare una volta all'anno (o con un altro intervallo di tempo). Se va tutto bene, aumentiamo il traffico. Se no, ripristiniamo le versioni precedenti.

Implementazione e dettagli
Come abbiamo implementato i rilasci canary? Ad esempio, un gruppo di clienti invia messaggi attraverso il nostro servizio.

Il deployment avviene in questo modo: rimuoviamo un nodo dal bilanciatore (1), cambiamo la versione (2) e poi facciamo transitare un po' di traffico separatamente (3).

In generale, nel gruppo saranno tutti felici, anche se un utente non è soddisfatto. Se tutto va bene, cambiamo tutte le versioni.

Ecco un diagramma che mostra come appare nella maggior parte dei casi per i microservizi.
C'è Service Discovery e due servizi: S1N1 e S2. Il primo servizio (S1N1) avvisa Service Discovery quando si avvia, mentre Service Discovery lo memorizza. Il secondo servizio ha due nodi (S2N1 e S2N2) e informa anch'esso Service Discovery all'avvio.

Il secondo servizio per il primo funge da server. Il primo chiede a Service Discovery informazioni sui propri server e, quando le riceve, li cerca e li verifica ("health check"). Quando ha eseguito il controllo, invia loro dei messaggi.
Quando qualcuno desidera eseguire il deployment di una nuova versione del secondo servizio, informa Service Discovery che il secondo nodo sarà un nodo canary: riceverà meno traffico perché il deployment è in corso. Rimuoviamo il nodo canary dal bilanciatore e il primo servizio non invia traffico a esso.

Cambiamo la versione e Service Discovery riconosce che il secondo nodo ora è canary — possiamo dargli meno carico (5%). Se va tutto bene, cambiamo la versione, ripristiniamo i carichi e proseguiamo.
Per realizzare tutto questo, abbiamo bisogno di:
- bilanciamento;
- monitoraggio, poiché è importante sapere cosa si aspetta ogni utente e come funzionano dettagliatamente i nostri servizi;
- analisi delle versioni, per comprendere quanto bene la nuova versione funzionerà in produzione;
- automazione — scriviamo la sequenza di distribuzione (deployment pipeline).

Bilanciamento
È la prima cosa a cui dobbiamo pensare. Ci sono due strategie di bilanciamento.
La soluzione più semplice è quando un nodo è sempre canary. Questo nodo riceve sempre meno traffico e iniziamo il deploy da esso. In caso di problemi, confronteremo il suo funzionamento prima e durante il deploy. Ad esempio, se gli errori aumentano di 2 volte, significa che anche i danni sono raddoppiati.
Il nodo canary è impostato durante il deploy. Quando il deploy è terminato e rimuoviamo il suo status di nodo canary, il bilanciamento del traffico verrà ripristinato. Con meno macchine avremo una distribuzione equa.
Monitoraggio
La pietra angolare dei rilasci canary. Dobbiamo comprendere esattamente perché lo facciamo e quali metriche vogliamo raccogliere.
Esempi di metriche che raccogliamo dai nostri servizi.
- Numero di errori, che vengono scritti nei log. Questo è un chiaro indicatore che tutto funziona come dovrebbe. In generale, è una buona metrica.
- Tempo di esecuzione delle richieste (latenza). Questa metrica è monitorata da tutti, perché tutti vogliono lavorare velocemente.
- Dimensione della coda (throughput).
- Numero di risposte riuscite al secondo.
- Tempo di esecuzione del 95% di tutte le richieste.
- Metriche di business: quanto denaro guadagna l’azienda in un determinato intervallo di tempo o il tasso di abbandono degli utenti. Queste metriche per la nostra nuova versione potrebbero essere più importanti di quelle aggiunte dagli ingegneri.
Esempi di metriche nella maggior parte dei sistemi di monitoraggio popolari.
Counter. Questa è una certa quantità crescente, ad esempio, il numero di errori. Questa metrica è semplice da interpolare e studiare: ieri ci sono stati 2 errori, oggi 500, quindi qualcosa è andato storto.
Il numero di errori al minuto o al secondo è un indicatore fondamentale che può essere calcolato utilizzando il Counter. Questi dati forniscono una chiara visione delle prestazioni del sistema nel tempo. Consideriamo, ad esempio, un grafico del numero di errori al secondo per due versioni di un sistema di produzione.

Nella prima versione c'erano pochi errori, probabilmente l'audit non funzionava. Nella seconda versione la situazione è molto peggiore. Possiamo affermare con certezza che ci sono problemi, quindi dobbiamo ripristinare questa versione.
Gauge. Le metriche sono simili al Counter, ma registriamo valori che possono sia aumentare che diminuire. Ad esempio, il tempo di esecuzione delle query o la dimensione della coda.
Nel grafico è mostrato un esempio del tempo di risposta (latency). Si può notare che le versioni sono simili e possono essere utilizzate. Ma se si guarda attentamente, è evidente come cambia il valore. Se il tempo di esecuzione delle query aumenta con l'aggiunta di utenti, è chiaro che ci sono problemi — non era così in precedenza.

Summary. Uno degli indicatori più importanti per le imprese sono i percentili. La metrica mostra che nel 95% dei casi il nostro sistema funziona come vogliamo. Possiamo accettare che ci siano problemi in alcuni punti, perché comprendiamo la tendenza generale su quanto tutto stia andando bene o male.
Strumenti
ELK Stack. Il canary può essere implementato utilizzando Elasticsearch: registriamo gli errori quando si verificano eventi. Con una semplice chiamata API possiamo ottenere il numero di errori in qualsiasi momento e confrontarlo con segmenti passati: GET /applg/_count?q=level:errore.
Prometheus. Ha dimostrato buone prestazioni in Infobip. Permette di implementare metriche multidimensionali grazie all'uso di etichette.
Possiamo utilizzare livello, istanza, service, combinarli in un unico sistema. Con offset possiamo, ad esempio, vedere il valore di una grandezza una settimana fa con un solo comando GET /api/v1/query?query={query}, dove {query}:
rate(logback_appender_total{
level="error",
instance=~"$instance"
}[5m] offset $offset_value)Analisi delle versioni
Ci sono diverse strategie per l'analisi delle versioni.
Monitorare le metriche solo del nodo canary. Una delle opzioni più semplici: abbiamo distribuito una nuova versione e studiamo solo le prestazioni. Ma se nel frattempo un ingegnere inizia a controllare i log, riavviando nervosamente le pagine, questa soluzione non si distingue dalle altre.
Il nodo canary viene confrontato con qualsiasi altro nodo.. Questa è una comparazione con altre istanze che operano con traffico completo. Ad esempio, se con poco traffico le prestazioni sono peggiori o non migliori delle istanze reali, allora c'è qualcosa che non va.
Il nodo canary viene confrontato con se stesso nel passato. I nodi dedicati a canary possono essere confrontati con dati storici. Ad esempio, se una settimana fa tutto andava bene, possiamo fare riferimento a quei dati per comprendere la situazione attuale.
Automazione
Vogliamo liberare gli ingegneri dal confronto manuale, quindi è importante implementare l'automazione. Il processo di distribuzione (deployment pipeline) di solito appare così:
- iniziamo;
- rimuoviamo il nodo dal bilanciatore;
- installiamo il nodo canary;
- attiviamo il bilanciatore con una quantità limitata di traffico;
- confrontiamo.

A questo punto implementiamo il confronto automatico. Come può apparire e perché è migliore rispetto al controllo post-distribuzione, lo esamineremo con un esempio da Jenkins.
Questa è la pipeline in Groovy.
while (System.currentTimeMillis() < endCanaryTs) {
def isOk = compare(srv, canary, time, base, offset, metrics)
if (isOk) {
sleep DEFAULT SLEEP
} else {
echo "Il canary è fallito, è necessario tornare indietro"
return false
}
} Qui nel ciclo definiamo che confronteremo il nuovo nodo per un'ora. Se il processo canary non è ancora terminato, chiameremo la funzione. Essa informa se tutto va bene o meno: def isOk = compare(srv, canary, time, base, offset, metrics).
Se tutto va bene — sleep DEFAULT SLEEP, ad esempio, per un secondo e continuiamo. Se no, usciamo — il deploy è fallito.
Descrizione della metrica. Vediamo come potrebbe apparire la funzione compare con un esempio di DSL.
metric(
'errorCounts',
'rate(errorCounts{node=~"$canaryInst"}[5m] offset $offset)',
{ baseValue, canaryValue ->
if (canaryValue > baseValue * 1.3) return false
return true
}
)Supponiamo di confrontare il numero di errori e vogliamo sapere quanti errori al secondo negli ultimi 5 minuti.
Abbiamo due valori: quello base e il nodo canary. Il valore del nodo canary è attuale. Il valore base è baseValue — quello di un qualsiasi altro nodo che non è canary. Confrontiamo i valori tra di loro secondo una formula, che stabiliremo in base alla nostra esperienza e osservazioni. Se il valore canaryValue è scarso, allora il deploy è fallito e torniamo indietro.
A cosa serve tutto questo?
Un essere umano non può controllare centinaia e migliaia di metriche., tanto più farlo rapidamente. Il confronto automatico aiuta a controllare tutte le metriche e avverte rapidamente sui problemi. Il tempo di notifica è cruciale: se qualcosa è successo negli ultimi 2 secondi, il danno sarà minore rispetto a se fosse accaduto 15 minuti fa. Mentre qualcuno si accorge del problema, scrive al supporto e il supporto a noi per ripristinare, possiamo perdere clienti.
Se il processo è andato a buon fine, distribuiamo automaticamente tutte le altre nodi. In questo momento gli ingegneri non fanno nulla. Solo quando avviano il canary decidono quali metriche prendere, quanto tempo dedicare al confronto e quale strategia utilizzare.

Se ci sono problemi, ripristiniamo automaticamente la nodo canary, lavoriamo su versioni precedenti e correggiamo gli errori che abbiamo trovato. Le metriche ci aiutano a trovarli facilmente e a vedere il danno della nuova versione.
Ostacoli
Realizzarlo, ovviamente, non è facile. Prima di tutto è necessaria una sistema di monitoraggio comune. Gli ingegneri hanno le loro metriche, il supporto e gli analisti ne hanno altre, e il business ne ha di terze. Un sistema comune è il linguaggio che unisce business e sviluppo.
Deve essere testato in pratica stabilità delle metriche. Il controllo aiuta a comprendere quale set minimo di metriche sia necessario per garantire la qualità.
Come possiamo raggiungerlo? Utilizzare un servizio canary non al momento del deployment. Aggiungiamo nella vecchia versione un certo servizio che possa prendere in qualsiasi momento un nodo dedicato, ridurre il traffico senza fare il deploy. Poi confrontiamo: studiamo gli errori e cerchiamo il punto in cui raggiungiamo la qualità.

Quali benefici abbiamo ottenuto dai rilasci canary
Abbiamo minimizzato la percentuale di danni causati da bug. La maggior parte degli errori di deployment si verifica a causa di incoerenze nei dati o nella priorità. Questi errori sono diminuiti drasticamente, poiché possiamo risolvere i problemi nei primi attimi.
Abbiamo ottimizzato il lavoro dei team. I principianti hanno il ‘diritto di sbagliare’: possono fare il deploy in produzione senza paura di commettere errori, questo genera iniziativa e stimolo a lavorare. Se rompono qualcosa, non sarà critico e non saranno licenziati.
Abbiamo automatizzato il deployment. Non è più un processo manuale come prima, ma un vero e proprio processo automatizzato. Tuttavia, richiede più tempo.
Abbiamo identificato metriche importanti. L'intera azienda, dai collaboratori agli ingegneri, comprende ciò che è davvero importante nel nostro prodotto, come le metriche, ad esempio, il churn e l'acquisizione degli utenti. Controlliamo il processo: testiamo le metriche, ne introduciamo di nuove, osserviamo come funzionano quelle esistenti, per costruire un sistema che produca profitto in modo più efficiente.
Abbiamo molte buone pratiche e sistemi che ci aiutano. Detto ciò, puntiamo a essere professionisti e a svolgere il nostro lavoro con qualità, indipendentemente dal fatto che abbiamo o meno un sistema che ci supporti.
Approcci e pratiche ingegneristiche — . Se hai fatto progressi nel tuo percorso verso l'eccellenza tecnica e sei pronto a condividere ciò che ti ha aiutato — .
Prevediamo di tenere 8 giugno. Comprendiamo che ora è difficile prendere decisioni sulla partecipazione alla conferenza. Tuttavia, riteniamo che la quarantena non sia una ragione per fermare la comunicazione e lo sviluppo professionale. Pertanto, troveremo comunque un modo per discutere delle mansioni del team leader e degli approcci per affrontare le loro sfide: se necessario, ci sposteremo online e attiveremo il networking lì!
Fonte: habr.com
