In DevOps, uno dei problemi più difficili è chi blocca attivamente le proprie indagini. In lizza per il titolo di campionato, il secondo peggior tipo di problemi sono quelli che si verificano a intermittenza. Questo articolo è una storia di avventura di un'epoca in cui il sistema di integrazione continua/distribuzione continua (CI/CD) a monte di Podman incontrava entrambi contemporaneamente.
Una tempesta perfetta
C'era una volta, l'automazione dei test Podman ha iniziato a diventare troppo grande per i suoi pantaloni da "ragazzo grosso". Ciò si è verificato anni fa, quando praticamente tutti i sistemi CI/CD erano basati su container. Podman, essendo uno strumento di gestione (e debug) di container (e pod), non può essere esercitato completamente all'interno di un container. Forse peggio, la maggior parte dei servizi di automazione delle materie prime supportava solo Ubuntu. Questo è diventato rapidamente un assoluto non-starter, poiché Podman doveva essere eseguito su macchine virtuali (VM). Doveva anche funzionare su più distribuzioni, inclusa la distribuzione upstream di Red Hat, Fedora.
Avendo esperienza con flussi di lavoro CI/CD innestati sotto una configurazione cloud-sdk + SSH (e/o Ansible) per l'accesso al cloud, la maggiore complessità sembra causare sempre problemi. Poi un giorno mi sono imbattuto in Cirrus-CI. Cirrus-CI è uno strumento di automazione incentrato su Git in grado di orchestrare container e macchine virtuali, utilizzando una miriade di provider cloud. Ha consentito al team Podman di pagare e gestire il servizio cloud indipendentemente dalla sua orchestrazione. Potremmo mantenere il controllo completo sulle macchine virtuali e sui dati di registro, con Cirrus-CI che gestisce solo l'orchestrazione.
Il flusso di lavoro generale è più o meno così:
- Uno sviluppatore invia proposte di modifiche al codice a monte nel repository Git di Podman.
- Cirrus-CI rileva, legge un file di configurazione e quindi avvia le VM necessarie nel nostro cloud.
- I servizi di metadati cloud nativi gestiscono l'esecuzione degli script e la registrazione su Cirrus-CI.
- Cirrus-CI elimina le VM e fornisce allo sviluppatore un feedback positivo/non riuscito.
- Le modifiche vengono apportate e il ciclo si ripete finché il codice non viene accettato.
Per anni, questo flusso di lavoro ha funzionato in modo quasi impeccabile, con la maggior parte dei problemi incentrati su test e script, errori che il team Podman gestisce facilmente. Raramente, i problemi all'interno di Cirrus-CI, Internet e/o il nostro provider cloud provocano l'orfana (mancata rimozione) delle VM. Per il resto, il personale di supporto di Cirrus Labs è stato fantastico, molto disponibile, con reattività e responsabilità di prim'ordine.
Quindi un giorno nell'ottobre 2020, dopo aver ruotato in una serie di immagini VM appena aggiornate (ovvero, l'immagine del disco copiata per ogni nuova istanza VM), i lavori apparentemente casuali hanno iniziato a non funzionare. L'analisi iniziale dell'output dello script non ha fornito informazioni. Letteralmente, tutto l'output si interromperebbe improvvisamente, senza uno schema distinguibile rispetto ad altri fallimenti. Come previsto, Cirrus-CI ripulirebbe diligentemente la VM e presenterebbe allo sviluppatore l'impossibilità di capire. Spesso, dopo aver rieseguito il lavoro non riuscito, l'operazione avrebbe avuto successo senza incidenti.
Questa situazione è andata avanti per diverse settimane senza problemi di corrispondenza con eventuali interruzioni dell'infrastruttura nel nostro cloud, GitHub o Cirrus. Il problema era piuttosto raro, forse una manciata di fallimenti al giorno, su centinaia di lavori di successo. La ricerca dei guasti era difficile e la ripetizione continua dei lavori non poteva essere una soluzione a lungo termine. Oltre ai rapporti regolari sugli incidenti da parte del mio team, non riuscivo a distinguere alcun modello di alto livello per i fallimenti. Poiché le nuove immagini della macchina virtuale hanno fornito vantaggi significativi, anche il costo del rollback sarebbe stato elevato.
Per quasi tutti i problemi sistemici come questo, trovare modelli di comportamento è un elemento chiave per una risoluzione dei problemi di successo. Poiché il successo lavorativo affidabile è lo stato desiderato, avere almeno qualche nozione, o un suggerimento, era un requisito arduo. Sfortunatamente, in questo caso, la nostra capacità di osservare i modelli era estremamente limitata dagli errori casuali e dalla caratteristica altamente desiderabile:ripulire le VM cloud in disuso, che sprecano denaro reale.
L'esecuzione manuale semplice, ripetuta e manuale del test non ha riprodotto affatto il problema. Né il lancio di più risorse di CPU e memoria ha influito sul comportamento. Ho passato giorni a riflettere e a fare brainstorming sulle opzioni per raccogliere dati aggiuntivi associati ai fallimenti. Infine, mi è sembrato che avessi bisogno di un modo per interrompere selettivamente la pulizia della VM, ma solo nei casi in cui i test non sono stati eseguiti.
[ Potrebbe piacerti anche: Da sysadmin a DevOps ]
In altre parole, dovevo associare in qualche modo il completamento del test con successo (non solo il passaggio/non superamento) con il permesso che si verificasse la pulizia. È stato allora che mi sono ricordato di una piccola casella di controllo che ho visto una volta frugando nella WebUI del nostro cloud:Protezione dall'eliminazione . Quando questo flag è impostato, Cirrus-CI si lamenterà ad alta voce perché è bloccata dalla rimozione di una VM, ma per il resto lascerà le cose indisturbate.
Avevo bisogno di strumentare il nostro flusso di lavoro in modo che le macchine virtuali stesse potessero impostare e annullare il proprio flag di protezione dall'eliminazione. Qualcosa del genere:
- La VM abilita la protezione dall'eliminazione su se stessa.
- Esegui test.
- VM disabilita la propria protezione dall'eliminazione.
- Cirrus-CI pulisce o non riesce a ripulire la VM e ne fa una puzza.
In questo modo, una volta completati i test, Cirrus-CI rimuoverà felicemente la VM come al solito. Tuttavia, se si verificasse il problema, i test non sarebbero stati completati e Cirrus-CI avrebbe riscontrato la protezione dall'eliminazione (ancora) abilitata. Fortunatamente, questo flusso di lavoro è stato interamente realizzabile tramite semplici opzioni della riga di comando per il programma di utilità di gestione del cloud che ho potuto installare sulle macchine virtuali.
Con questa strumentazione del flusso di lavoro in atto, tutto ciò che dovevo fare era attivare ripetutamente la matrice di test e attendere che le macchine virtuali orfane venissero visualizzate. Quel giorno il destino mi sorrideva perché il problema si è riprodotto quasi all'istante. Tuttavia, l'ho eseguito un paio di volte in più, quindi avrei avuto più di alcune VM orfane da ispezionare.
Inaspettatamente, questo è quando la situazione è diventata ancora più interessante:non potevo inviare SSH nelle VM orfane. In effetti, non risponderebbero nemmeno ai ping dall'interno o dall'esterno del nostro cloud. Ho eseguito un hard reset su una macchina virtuale e ho controllato i registri di sistema una volta avviato. Non c'era niente. Cerniera lampo. Nada. Non un solo briciolo di indizio diverso da quello che già sapevamo:i test hanno semplicemente smesso di essere eseguiti, morti, insieme al resto del sistema.
Dato che era già il mio giorno fortunato, ho deciso di spingerlo e di nuovo sono andato a curiosare nella WebUI del mio cloud. Alla fine, ho trovato un'altra piccola impostazione incredibilmente utile:Registro di output della console seriale . Questa era fondamentalmente una linea di comunicazione diretta e di basso livello direttamente nel kernel. Se accadesse qualcosa di abbastanza orribile da causare un blocco completo del sistema, sicuramente il kernel urlerebbe fuori dalla sua porta seriale virtuale. Bingo, Yahtzee e huzzah!
[ 1203.905090] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 1203.912275] #PF: supervisor read access in kernel mode
[ 1203.917588] #PF: error_code(0x0000) - not-present page
[ 1203.922881] PGD 8000000124e2d067 P4D 8000000124e2d067 PUD 138d5e067 PMD 0
[ 1203.929939] Oops: 0000 [#1] SMP PTI
...blah...blah...blah
[ 1204.052766] Call Trace:
[ 1204.055440] bfq_idle_extract+0x52/0xb0
[ 1204.059468] bfq_put_idle_entity+0x12/0x60
[ 1204.063722] bfq_bfqq_served+0xb0/0x190
[ 1204.067718] bfq_dispatch_request+0x2c2/0x1070
Perché, ciao, vecchio amico! È stato davvero un giorno molto fortunato!
Questo era un panico del kernel che avevo visto entrambi un anno prima e avevo lavorato instancabilmente per mesi con l'ingegneria del kernel a monte per risolverlo. Era un bug nel sottosistema di archiviazione del kernel responsabile del miglioramento dell'efficienza e della garanzia di preziosi 0 e 1 scritti su disco. In questo caso, invece di una memoria corrotta, il kernel ha lanciato una bandiera bianca e ha fermato tutto sul suo cammino.
Avendo lavorato quasi allo stesso identico problema in precedenza, ero già a conoscenza della soluzione alternativa a una riga. Non c'era bisogno di trascinare di nuovo il mio team attraverso mesi di debug. Potrei semplicemente segnalare la ricorrenza e implementare con sicurezza la soluzione alternativa, sapendo che avrebbe risolto il problema al 100%:
echo mq-deadline > /sys/block/sda/queue/scheduler
Ora, non consiglio di eseguire quel comando, volenti o nolenti, su ogni sistema Linux che possiedi. In questo caso specifico, sapevo dall'esperienza passata che era completamente sicuro scambiare l'algoritmo di buffering (chiamato anche I/O Elevator). Nessuno sviluppatore Podman noterebbe nemmeno il cambiamento nei risultati dei test.
[ Iniziare con i container? Dai un'occhiata a questo corso gratuito. Distribuzione di applicazioni containerizzate:una panoramica tecnica. ]
Concludi
Se tu o qualcuno che ami dovessi mai incontrare un panico nel kernel, ti consiglio di guardare questo recente articolo sull'argomento. Altrimenti, il principale punto di partenza è questo:durante la risoluzione dei problemi alla cieca, è assolutamente vitale risolvere gli aspetti causa-offuscamento. Il secondo è lavorare con i dati per rendere il problema più riproducibile. Avrai difficoltà a fare quest'ultimo senza l'assistenza del primo.