Il canarino è un piccolo uccello che canta incessantemente. Questi uccellini sono sensibili al metano e al monossido di carbonio. Anche una piccola concentrazione di gas in eccesso nell'aria li porta a svenire o a morire. I cercatori d'oro e i minatori portavano con sé gli uccelli: mentre i canarini cantano, si può lavorare; se tacciono, significa che c'è gas nella miniera ed è il momento di andare via. I minatori sacrificavano i piccoli uccelli per tornare dalle miniere vivi.

Questa pratica si è trasferita anche nel settore IT. Ad esempio, nel compito standard di deployare una nuova versione di un servizio o di un'applicazione in produzione, dopo averla testata. L'ambiente di test può essere troppo costoso, i test automatizzati non coprono tutto ciò che si vorrebbe, e non testare e sacrificare la qualità è rischioso. In questi casi aiuta l'approccio del Canary Deployment, in cui un po' di traffico reale di produzione viene inviato alla nuova versione. Questo approccio aiuta a verificare in modo sicuro la nuova versione in produzione, sacrificando poco per un grande obiettivo. Maggiori dettagli su come funziona l'approccio, quale sia il suo valore e come realizzarlo saranno forniti da Andrey Markelov (), tramite un esempio di implementazione nella società Infobip.
Andrey Markelov — ingegnere programmatore senior in Infobip, ha dedicato 11 anni allo sviluppo di applicazioni Java nei settori 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 telecomunicazioni 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, la stabilità e l'affidabilità sono fondamentali 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 produzione;
- 2500 istanze di servizi, molto più rispetto ai team;
- 4,5 Tbyte di traffico mensile;
- 4,5 miliardi di numeri di telefono;
Il business cresce, e con esso il numero di rilasci. Effettuamo circa 60 rilasci al giorno, perché i clienti richiedono più funzionalità e capacità. Ma è complicato: ci sono molti servizi e pochi team. Dobbiamo scrivere rapidamente codice che deve funzionare in produzione senza errori.
Rilasci
Un rilascio tipico da noi avviene in questo modo. 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 distribuire una nuova versione, ma i team dei servizi B, C, D ed E non ne sono al corrente. Ci sono due opzioni su come agirà il team del servizio A.
Effettuerà un rilascio incrementale: prima sostituirà una versione, poi l'altra.

Ma c'è una seconda opzione: il team trovare risorse e macchine aggiuntive, distribuirà una nuova versione e poi cambierà il router, e la versione inizierà a funzionare in produzione.

In ogni caso, dopo il deployment sorgono quasi sempre problemi, anche se la versione è stata testata. I test possono essere manuali, automatizzati o anche non effettuati — i problemi si presenteranno comunque. Il modo più semplice e corretto per risolverli è tornare alla versione funzionante. Solo allora si potrà affrontare il danno, le cause e correggerle.
Quindi, cosa vogliamo?
Non vogliamo problemi. Se i clienti li scoprono prima di noi, questo danneggia la reputazione. Pertanto, dobbiamo individuare i problemi più rapidamente dei clienti. Lavorando in anticipo, minimizziamo il danno.
Allo stesso tempo, vogliamo accelerare il deployment, affinché avvenga in modo rapido, facile, spontaneo e senza stress per il team. È importante proteggere ingegneri, DevOps e programmatori — il rilascio di una nuova versione è fonte di stress. Il team non è una risorsa usa e getta, miriamo a utilizzare razionalmente le risorse umane..
Problemi di deployment
Il traffico cliente è imprevedibile.Non è possibile prevedere quando il traffico cliente sarà al minimo. Non sappiamo dove e quando i clienti avvieranno le proprie campagne — potrebbe essere stanotte in India e domani a Hong Kong. Considerando la grande differenza di fuso orario, effettuare un deployment anche alle 2 del mattino non garantisce che i clienti non subiscano danni.
Problemi dei fornitori.I messenger e i fornitori sono nostri partner. A volte hanno guasti che causano errori durante il deployment di nuove versioni.
Team distribuiti.I team che sviluppano la parte client e il backend si trovano in fusi orari diversi. Questo porta spesso a difficoltà nel trovare accordi tra loro.
I data center non possono essere replicati in stage.In un data center ci sono 200 rack - replicare questo in un sandbox non è nemmeno lontanamente realizzabile.
Downtimenon è accettabile! Abbiamo un livello di disponibilità accettabile (Error Budget), che è del 99,99% del tempo, per esempio, e i restanti percentuali rappresentano il "diritto all'errore". Raggiungere il 100% di affidabilità è impossibile, ma è importante monitorare costantemente i cali e i downtime.
Soluzioni classiche
Scrivere codice senza bug. Quando ero un giovane sviluppatore, i manager venivano da me chiedendo di fare un rilascio senza bug, ma questo non è sempre possibile.
Scrivere test. I test funzionano, ma a volte in modo completamente diverso da quanto desidera il business. Fare soldi non è compito dei test.
Testare in stage. Nei 3,5 anni che ho lavorato in Infobip, non ho mai visto uno stato dello stage che corrispondesse anche solo parzialmente all'ambiente di produzione.

Abbiamo persino cercato di sviluppare questa idea: prima avevamo lo stage, poi il pre-produzione, e infine il pre-produzione della pre-produzione. Ma neanche questo ha aiutato: non corrispondevano nemmeno per capacità. Con lo stage possiamo garantire la funzionalità di base, ma non sappiamo come funziona sotto carico.
Il rilascio è fatto da chi sviluppa. Questa è una buona pratica: anche se qualcuno cambia il nome di un commento, dovrebbe subito aggiungerlo in produzione. Questo aiuta a sviluppare responsabilità e a non dimenticare le modifiche apportate.
Ci sono anche ulteriori complessità. Per uno sviluppatore è stressante impiegare molto tempo per controllare tutto manualmente.
Rilascio concordati. Questa opzione è solitamente proposta dalla direzione: "Concordiamo che testerete e aggiungerete nuove versioni ogni giorno". Questo non funziona: c'è sempre un team che attende gli altri o viceversa.
Smoke-test
Un altro modo per risolvere i nostri problemi di deployment. Consideriamo come funzionano gli smoke-test nell'esempio precedente, quando il team A vuole rilasciare una nuova versione.
Inizialmente il team rilascia un'istanza in produzione. Con messaggi nell'istanza da mock si simula il traffico reale, in modo che corrisponda al normale traffico quotidiano. Se tutto va bene, il team commuta la nuova versione sul traffico degli utenti.

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

Svantaggi degli smoke-test:
- Non si può fare affidamento sui test. Dove ottenere lo stesso traffico della produzione? È possibile utilizzare quello di ieri o di una settimana fa, ma non sempre corrisponde a quello attuale.
- Difficile da mantenere. Dobbiamo gestire gli account di test, resettandoli costantemente prima di ogni deployment, quando nel repository vengono inviati record attivi. È più complesso che scrivere un test nella propria sandbox.
L'unico vantaggio qui è la possibilità di verificare le prestazioni.
Rilascio canary
A causa delle limitazioni dei test di smoke, abbiamo iniziato a utilizzare i rilasci canary.
Una pratica simile a come i minatori usavano i canarini per indicare i livelli di gas ha trovato applicazione anche nell'IT. Noi inviamo un po' di traffico reale in produzione nella nuova versione, cercando di rispettare il Service Level Agreement (SLA). SLA è il nostro "diritto all'errore", che possiamo utilizzare una volta all'anno (o in un altro intervallo di tempo). Se tutto va bene, aggiungiamo più traffico. Altrimenti, torniamo alle versioni precedenti.

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

Il deployment avviene così: rimuoviamo un nodo dal bilanciatore (1), cambiamo versione (2) e separatamente facciamo passare un po' di traffico (3).

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

Mostrerò schematizzando come appare per i microservizi nella maggior parte dei casi.
C'è Service Discovery e altri due servizi: S1N1 e S2. Il primo servizio (S1N1) avvisa Service Discovery quando si avvia, e Service Discovery lo memorizza. Il secondo servizio con due nodi (S2N1 e S2N2) avvisa anch'esso Service Discovery all'avvio.

Il secondo servizio per il primo funziona come server. Il primo richiede a Service Discovery informazioni sui propri server e, quando le riceve, li cerca e li verifica ("health check"). Quando ha finito, invia messaggi a loro.
Quando qualcuno desidera implementare una nuova versione del secondo servizio, informa Service Discovery che il secondo nodo sarà un nodo canary: riceverà meno traffico, poiché si procederà con il deployment. Rimuoviamo il nodo canary dal bilanciatore e il primo servizio non gli invia traffico.

Cambiamo versione e il Service Discovery sa che il secondo nodo è ora canary: possiamo dargli meno carico (5%). Se tutto va bene, cambiamo versione, ripristiniamo i carichi e continuiamo a lavorare.
Per realizzare tutto ciò, abbiamo bisogno di:
- bilanciamento;
- monitoraggio, poiché è importante sapere cosa si aspetta ogni utente e come funzionano in dettaglio i nostri servizi;
- analisi delle versioni, per capire quanto bene funzionerà la nuova versione in produzione;
- automazione — scrivere 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 deployment da esso. In caso di problemi confronteremo il suo funzionamento prima del deployment e durante di esso. Ad esempio, se gli errori sono raddoppiati, significa che il danno è aumentato di due volte.
Il nodo canary è definito durante il deployment.Quando il deployment è completato e rimuoviamo il suo stato di nodo canary, l'equilibrio del traffico viene ripristinato. Con un numero minore di macchine otteniamo una distribuzione equa.
Monitoraggio
La pietra angolare dei rilasci canary. Dobbiamo capire 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 indicatore ovvio che tutto funziona come dovrebbe. In generale, è una buona metrica.
- Tempo di esecuzione delle richieste (latency). Questa metrica è monitorata da tutti, perché tutti vogliono lavorare rapidamente.
- Dimensione della coda (throughput).
- Numero di risposte correttamente ricevute al secondo.
- Tempo di esecuzione del 95% di tutte le richieste.
- Metriche aziendali: quanto denaro guadagna l'azienda in un determinato periodo 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 nei sistemi di monitoraggio più popolari.
Counter. È una certa quantità crescente, ad esempio, il numero di errori. Questa metrica può essere facilmente interpolata e possiamo analizzare i grafici: ieri ci sono stati 2 errori, oggi 500, il che significa che qualcosa è andato storto.
Il numero di errori al minuto o al secondo è un indicatore cruciale che può essere calcolato utilizzando il Counter. Questi dati offrono una chiara comprensione del funzionamento del sistema nel tempo. Prendiamo come esempio il grafico del numero di errori al secondo per due versioni del sistema di produzione.

Nella prima versione c'erano pochi errori, forse l'audit non funzionava. Nella seconda versione, invece, la situazione è molto peggiore. Possiamo affermare con certezza che ci sono problemi, quindi dobbiamo fare il rollback di questa versione.
Gauge. Le metriche sono simili al Counter, ma registriamo valori che possono aumentare o diminuire. Ad esempio, il tempo di esecuzione delle richieste o la dimensione della coda.
Nel grafico un esempio del tempo di risposta (latency). Dal grafico si evince che le versioni sono simili e possono essere utilizzate. Ma se si osserva attentamente, si nota come la grandezza cambi. Se il tempo di esecuzione delle richieste aumenta con l'inserimento di utenti, è subito chiaro che ci sono problemi: prima non succedeva così.

Summary. Uno degli indicatori più importanti per il business sono i percentili. La metrica mostra che nel 95% dei casi il nostro sistema funziona come desideriamo. Possiamo tollerare eventuali problemi, perché comprendiamo la tendenza generale, quanto tutto vada bene o male.
Strumenti
ELK Stack. Il canary può essere implementato utilizzando Elasticsearch: registriamo gli errori in esso 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/_cunt?q=level:errr.
Prometheus. Si è dimostrato efficace in Infobip. Permette di implementare metriche multidimensionali, poiché vengono utilizzate etichette.
Possiamo utilizzare level, instance, service, combinandoli in un unico sistema. Con l'aiuto di offset è possibile visualizzare, ad esempio, il valore della grandezza di una settimana fa con un'unica 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.
Controllare le metriche solo per i nodi canary. Una delle soluzioni più semplici: abbiamo distribuito una nuova versione e studiamo solo il suo funzionamento. Ma se un ingegnere in quel momento inizia a esaminare i log, ricaricando nervosamente le pagine, questa soluzione non è diversa dalle altre.
Il nodo canary è confrontato con qualsiasi altro nodo.. Questo confronto con altre istanze che operano su traffico completo. Ad esempio, se con un piccolo traffico le cose vanno peggio, o non meglio che nelle istanze reali, c'è qualcosa che non va.
Il nodo canary viene confrontato con se stesso nel passato. I nodi dedicati al canary possono essere confrontati con dati storici. Ad esempio, se una settimana fa andava tutto bene, possiamo orientarci su questi dati per comprendere la situazione attuale.
Automazione
Vogliamo liberare gli ingegneri dal confronto manuale, quindi è importante implementare l'automazione. Il processo di deployment (pipeline di distribuzione) di solito appare in questo modo:
- partiamo;
- rimuoviamo il nodo dal bilanciatore;
- installa il nodo canary;
- attiviamo il bilanciatore con un volume di traffico limitato;
- confrontiamo.

In questa fase implementiamo il confronto automatico. Come potrebbe apparire e perché è migliore rispetto al controllo post-deployment, lo esamineremo con un esempio di Jenkins.
Questa è una 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 completato, chiamiamo la funzione. Essa riporta se va tutto bene o no: def isOk = compare(srv, canary, time, base, offset, metrics).
Se va tutto bene — sleep DEFAULT SLEEP, ad esempio, per un secondo, e continuiamo. Se no, usciamo — il deployment non è andato a buon fine.
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 che stiamo confrontando il numero di errori e vogliamo conoscere il numero di errori al secondo negli ultimi 5 minuti.
Abbiamo due valori: il valore base e quello del nodo canary. Il valore del nodo canary è attuale. Il valore base è baseValue — questo è il valore di qualsiasi altro nodo non canary. Confrontiamo i valori tra di loro seguendo la formula che stabiliremo in base alla nostra esperienza e osservazioni. Se il valore canaryValue è scarso, il deployment non è riuscito, e dobbiamo tornare indietro.
A cosa serve tutto questo?
Una persona non può controllare centinaia e migliaia di metriche., tanto più farlo rapidamente. Il confronto automatico aiuta a controllare tutte le metriche e segnala rapidamente eventuali problemi. Il tempo di notifica è critico: se qualcosa è accaduto negli ultimi 2 secondi, il danno sarà minore rispetto a se fosse accaduto 15 minuti fa. Mentre qualcuno nota il problema, scrive al supporto, e il supporto ci contatta per fare un rollback, possiamo perdere clienti.
Se il processo è andato a buon fine, possiamo distribuire automaticamente tutte le altre nodi. In quel momento gli ingegneri non fanno nulla. Solo quando avviano il canary, decidono quali metriche raccogliere, per quanto tempo fare il confronto e quale strategia utilizzare.

Se ci sono problemi, facciamo automaticamente il rollback della nodi canary, lavoriamo su versioni precedenti e correggiamo gli errori trovati. Le metriche rendono facile trovarli e vedere il danno della nuova versione.
Ostacoli
Realizzarlo, ovviamente, non è semplice. Prima di tutto serve un sistema di monitoraggio comune. Gli ingegneri hanno le loro metriche, il supporto e gli analisti hanno altre, e il business ne ha delle terze. Un sistema comune è la lingua condivisa tra business e sviluppo.
È necessario testare nella pratica la stabilità delle metriche. Il controllo aiuta a capire quale sia il set minimo di metriche necessario per garantire la qualità.
Come possiamo raggiungerlo? Utilizzare il servizio canary non al momento del deployment. Aggiungiamo alla versione precedente un certo servizio che in qualsiasi momento può assumere una qualsiasi nodi dedicata, riducendo il traffico senza deployment. Poi confrontiamo: analizziamo gli errori e cerchiamo il confine in cui raggiungiamo la qualità.

Quali benefici abbiamo ottenuto dai rilasci canary
Abbiamo minimizzato la percentuale di danno causato dai bug. La maggior parte degli errori di deployment si verifica a causa di incoerenze nei dati o priorità. Questi errori sono diminuiti notevolmente, poiché possiamo risolvere i problemi nei primi secondi.
Abbiamo ottimizzato il lavoro dei team. I neofiti hanno il "diritto all'errore": possono distribuire in produzione senza paura di sbagliare, si crea un'iniziativa aggiuntiva, uno 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 processo automatizzato. Tuttavia dura di più.
Abbiamo identificato metriche importanti. L'intera azienda, dai business e ingegneri, comprende ciò che è veramente importante nel nostro prodotto, quali metriche, ad esempio, churn e acquisizione utenti. Controlliamo il processo: testiamo le metriche, introduciamo nuove, osserviamo come funzionano quelle vecchie, per costruire un sistema che guadagni di più.
Abbiamo tante pratiche e sistemi interessanti che ci aiutano. Nonostante ciò, ci impegniamo a essere dei professionisti e a svolgere il nostro lavoro con qualità, indipendentemente dal fatto che abbiamo un sistema che ci assista o meno.
Gli approcci e le pratiche ingegneristiche — . Se hai raggiunto dei successi nel tuo percorso verso l'eccellenza tecnica e sei pronto a raccontare ciò che ti ha aiutato, — .
Prevediamo di tenere l'8 giugno. Comprendiamo che attualmente è difficile prendere decisioni sulla partecipazione alla conferenza. Ma allo stesso tempo, riteniamo che il lockdown non sia un motivo per fermare la comunicazione e lo sviluppo professionale. Pertanto, troveremo comunque un modo per discutere i problemi del tech lead e gli approcci per risolverli — se necessario, passeremo all'online e svilupperemo il networking lì!
Fonte: habr.com
