Come creare uno sviluppo interno completo utilizzando l'esperienza DevOps - VTB

Le pratiche DevOps funzionano. Lo abbiamo constatato noi stessi quando abbiamo ridotto di 10 volte il tempo di installazione delle versioni. Nel sistema FIS Profile che utilizziamo in VTB, l'installazione ora richiede 90 minuti invece di 10. Il tempo di compilazione del rilascio è diminuito da due settimane a due giorni. Allo stesso tempo, il numero dei difetti permanenti di attuazione è sceso quasi al minimo. Per allontanarci dal "lavoro manuale" ed eliminare la dipendenza dal venditore, abbiamo dovuto affrontare il lavoro con le stampelle e trovare soluzioni inaspettate. Sotto il taglio c'è una storia dettagliata su come abbiamo costruito uno sviluppo interno a tutti gli effetti.

Come creare uno sviluppo interno completo utilizzando l'esperienza DevOps - VTB
 

Prologo: DevOps è una filosofia

Nell'ultimo anno, abbiamo svolto molto lavoro per organizzare lo sviluppo interno e l'implementazione delle pratiche DevOps in VTB:

  • Costruito processi di sviluppo interni per 12 sistemi;
  • Abbiamo lanciato 15 pipeline, di cui quattro portate in produzione;
  • Scenari di test automatizzati 1445;
  • Implementazione con successo di una serie di versioni preparate dai team interni.

Uno degli aspetti più difficili da organizzare per lo sviluppo interno e l'implementazione delle pratiche DevSecOps si è rivelato essere il sistema FIS Profile, un processore di prodotti al dettaglio su un DBMS non relazionale. Tuttavia, siamo stati in grado di creare lo sviluppo, avviare la pipeline, installare singoli pacchetti non rilasciati sul prodotto e imparare come assemblare le versioni. Il compito non era facile, ma interessante e senza evidenti restrizioni nell'implementazione: ecco il sistema: è necessario costruire uno sviluppo interno. L'unica condizione è utilizzare il CD prima di un ambiente produttivo.

Inizialmente, l’algoritmo di implementazione sembrava semplice e chiaro:

  • Sviluppiamo competenze di sviluppo iniziale e raggiungiamo un livello accettabile di qualità dal team di codifica senza difetti grossolani;
  • Ci integriamo il più possibile nei processi esistenti;
  • Per trasferire il codice tra le fasi ovvie, tagliamo la pipeline e la appoggiamo con una delle estremità sul prod.

Durante questo periodo, il team di sviluppo delle dimensioni richieste deve sviluppare competenze e aumentare la quota del proprio contributo ai rilasci a un livello accettabile. E questo è tutto, puoi considerare l'attività completata.

Sembrerebbe che questo sia un percorso completamente efficiente dal punto di vista energetico verso il risultato richiesto: ecco DevOps, ecco i parametri di performance del team, ecco l'esperienza accumulata... Ma in pratica, abbiamo ricevuto un'altra conferma che DevOps è ancora una questione di filosofia e non "collegato al processo gitlab, ansible, nexus e così via".

Dopo aver analizzato ancora una volta il piano d'azione, ci siamo resi conto che stavamo costruendo dentro di noi una sorta di esternalizzazione dei fornitori. All'algoritmo sopra descritto si è quindi aggiunta la reingegnerizzazione dei processi e lo sviluppo delle competenze lungo tutto il percorso di sviluppo per raggiungere un ruolo di primo piano in tale processo. Non è l'opzione più semplice, ma questa è la via per uno sviluppo ideologicamente corretto.
 

Come avviare lo sviluppo interno 

Non era il sistema più amichevole con cui lavorare. Dal punto di vista architettonico, era un grande DBMS non relazionale, costituito da molti oggetti eseguibili separati (script, procedure, batch, ecc.) che venivano chiamati secondo necessità e funzionava secondo il principio della scatola nera: riceve una richiesta - fornisce una risposta. Altre difficoltà degne di nota includono:

  • Linguaggio Esotico (MUMPS);
  • Interfaccia della console;
  • Mancanza di integrazione con strumenti e framework di automazione popolari;
  • La quantità di dati in decine di terabyte;
  • Carico di oltre 2 milioni di operazioni all'ora;
  • Importanza: fondamentale per l'azienda.

Allo stesso tempo, da parte nostra non esisteva alcun repository di codice sorgente. Affatto. C'era la documentazione, ma tutte le conoscenze e le competenze chiave erano da parte di un'organizzazione esterna.
Abbiamo iniziato a padroneggiare lo sviluppo del sistema praticamente da zero, tenendo conto delle sue caratteristiche e della bassa distribuzione. Iniziato nell'ottobre 2018:

  • Studiato la documentazione e le basi della generazione del codice;
  • Abbiamo studiato il breve corso di sviluppo ricevuto dal venditore;
  • Padroneggiare le capacità di sviluppo iniziali;
  • Compilato un manuale di formazione per i nuovi membri del team;
  • Concordato l'inclusione della squadra nel lavoro in modalità "combattimento";
  • Risolto il problema con il controllo della qualità del codice;
  • Organizzato uno stand per lo sviluppo.

Abbiamo trascorso tre mesi sviluppando competenze e immergendoci nel sistema e, dall'inizio del 2019, lo sviluppo interno ha iniziato il suo movimento verso un futuro più luminoso, a volte con un cigolio, ma con sicurezza e determinazione.

Migrazione del repository e test automatici

Il primo compito di DevOps è il repository. Abbiamo concordato rapidamente di fornire l'accesso, ma era necessario migrare dall'attuale SVN con un ramo del tronco al nostro Git di destinazione con il passaggio a un modello di più rami e lo sviluppo di Git Flow. Disponiamo anche di 2 team con la propria infrastruttura, più parte del team del venditore all’estero. Dovevo convivere con due Git'ami e garantire la sincronizzazione. In questa situazione, era il minore dei due mali.

La migrazione del repository è stata più volte rinviata e si è conclusa solo ad aprile, con l'aiuto dei colleghi in prima linea. Con Git Flow, abbiamo deciso di mantenere le cose semplici per cominciare e abbiamo optato per lo schema classico con hotfix, sviluppo e rilascio. Hanno deciso di abbandonare il master (aka prod-like). Di seguito spiegheremo perché questa opzione si è rivelata ottimale per noi. Come lavoratore è stato utilizzato un repository esterno appartenente al fornitore, comune a due team. Si è sincronizzato con il repository interno secondo una pianificazione. Ora con Git e Gitlab è stato possibile automatizzare i processi.

Il problema degli autotest è stato risolto in modo sorprendentemente semplice: ci è stato fornito un quadro già pronto. Tenendo conto delle peculiarità del sistema, chiamare un'operazione separata era una parte comprensibile del processo aziendale e allo stesso tempo fungeva da test unitario. Tutto ciò che restava da fare era preparare i dati del test e impostare l'ordine desiderato per richiamare gli script e valutare i risultati. Man mano che l'elenco degli scenari, formato sulla base delle statistiche operative, della criticità dei processi e della metodologia di regressione esistente, veniva compilato, iniziarono ad apparire test automatici. Ora potremmo iniziare a costruire il gasdotto.

Com'era: il modello prima dell'automazione

Il modello esistente del processo di implementazione è una storia a parte. Ogni modifica è stata trasferita manualmente come pacchetto di installazione incrementale separato. Successivamente è arrivata la registrazione manuale in Jira e l'installazione manuale negli ambienti. Per i singoli pacchetti tutto sembrava chiaro, ma con la preparazione del rilascio le cose si sono complicate.

L'assemblaggio è stato effettuato a livello delle singole consegne, che erano oggetti indipendenti. Qualsiasi modifica è una nuova fornitura. Tra le altre cose, ai 60-70 pacchetti della composizione della versione principale sono state aggiunte 10-15 versioni tecniche: versioni ottenute aggiungendo o escludendo qualcosa dalla versione e riflettendo i cambiamenti nelle vendite al di fuori delle versioni.

Gli oggetti all'interno delle consegne si sovrapponevano tra loro, soprattutto nel codice eseguibile, che era unico per meno della metà. C'erano molte dipendenze sia dal codice già installato che da quello di cui era appena stata pianificata l'installazione. 

Per ottenere la versione corretta del codice è stato necessario seguire scrupolosamente l'ordine di installazione, durante il quale gli oggetti venivano sovrascritti fisicamente più volte, circa 10-12 volte.

Dopo aver installato una serie di pacchetti, ho dovuto seguire manualmente le istruzioni per inizializzare le impostazioni. La versione è stata assemblata e installata dal fornitore. La composizione del rilascio è stata specificata quasi fino al momento dell'implementazione, che ha comportato la creazione di pacchetti di "disaccoppiamento". Di conseguenza, una parte significativa delle consegne è passata da un rilascio all'altro con la sua coda di "disaccoppiamenti".

Ora è chiaro che con questo approccio - assemblando il puzzle del rilascio a livello di pacchetto - un unico ramo principale non aveva senso pratico. L'installazione sul prodotto ha richiesto da un'ora e mezza a due ore di lavoro manuale. È positivo che almeno a livello di installatore sia stato impostato l'ordine di elaborazione degli oggetti: i campi e le strutture sono arrivati ​​prima dei dati relativi e delle procedure. Tuttavia, funzionava solo all'interno di un pacchetto separato.

Il risultato logico di questo approccio sono stati i difetti di installazione obbligatori sotto forma di versioni storte degli oggetti, codice non necessario, istruzioni mancanti e influenze reciproche non contabilizzate degli oggetti, che sono state febbrilmente eliminate dopo il rilascio. 

Primi aggiornamenti: build tramite commit e consegna

L'automazione è iniziata facendo passare il codice attraverso un tubo lungo questo percorso:

  • Ritirare la fornitura finita dal magazzino;
  • Installalo su un ambiente dedicato;
  • Eseguire test automatici;
  • Valutare il risultato dell'installazione;
  • Chiama la pipeline successiva dal lato del team di test.

La pipeline successiva dovrebbe registrare l'attività in Jira e attendere la distribuzione dei comandi ai cicli di test selezionati, che dipendono dai tempi di implementazione dell'attività. Trigger: una lettera sulla disponibilità della consegna a un determinato indirizzo. Questa, ovviamente, era una stampella ovvia, ma era necessario iniziare da qualche parte. Da maggio 2019 è iniziato il trasferimento del codice con i controlli sui nostri ambienti. Il processo è iniziato, resta da portarlo in una forma decente:

  • Ogni revisione viene eseguita su un ramo separato che corrisponde al pacchetto di installazione e viene unito al ramo principale di destinazione;
  • L'innesco del lancio della pipeline è la comparsa di un nuovo commit nel ramo master attraverso una richiesta di fusione, che viene chiusa dai manutentori del team interno;
  • I repository si sincronizzano ogni cinque minuti;
  • Inizia l'assemblaggio del pacchetto di installazione, utilizzando l'assemblatore ricevuto dal fornitore.

Successivamente c'erano già dei passaggi per il controllo e il trasferimento del codice, per la conduzione della tubazione e per la costruzione dalla nostra parte.

Questa opzione è stata lanciata a luglio. Le difficoltà della transizione hanno portato ad una certa insoddisfazione nei confronti del venditore e del personale in prima linea, ma nel mese successivo siamo riusciti a rimuovere tutti gli ostacoli e a migliorare il processo nei team. Abbiamo una build per commit e consegna.
Ad agosto siamo riusciti a eseguire la prima installazione di un pacchetto separato per la produzione utilizzando la nostra pipeline e da settembre, senza eccezioni, tutte le installazioni dei singoli pacchetti fuori rilascio sono state eseguite tramite il nostro strumento CD. Inoltre, siamo riusciti a ottenere una quota di compiti interni pari al 40% della composizione del rilascio con un team più piccolo di quello del fornitore: questo è un sicuro successo. Rimaneva il compito più serio: creare e installare la versione.

Soluzione finale: pacchetti di installazione cumulativi 

Abbiamo capito perfettamente che scrivere le istruzioni del fornitore era un’automazione così così; abbiamo dovuto ripensare il processo stesso. La soluzione era ovvia: raccogliere una fornitura cumulativa dal ramo di rilascio con tutti gli oggetti delle versioni richieste.

Abbiamo iniziato con una prova di concetto: abbiamo assemblato manualmente il pacchetto di rilascio in base ai contenuti dell'implementazione passata e lo abbiamo installato nei nostri ambienti. Tutto ha funzionato, il concetto si è rivelato fattibile. Successivamente, abbiamo risolto il problema relativo allo scripting delle impostazioni di inizializzazione e alla loro inclusione nel commit. Abbiamo preparato un nuovo pacchetto e lo abbiamo testato in ambienti di test come parte dell'aggiornamento del contour. L'installazione ha avuto successo, anche se con un'ampia gamma di commenti da parte del team di implementazione. Ma la cosa principale è che ci è stato dato il via libera per entrare in produzione nella versione di novembre con il nostro assemblaggio.

Con poco più di un mese rimasto, le forniture selezionate indicavano chiaramente che il tempo stava per scadere. Hanno deciso di realizzare la build dal ramo di rilascio, ma perché dovrebbe essere separato? Non abbiamo un Prod-like e i rami esistenti non vanno bene: c'è molto codice non necessario. Abbiamo urgentemente bisogno di tagliare i prod-like, e si tratta di oltre tremila commit. L'assemblaggio a mano non è affatto un'opzione. Abbiamo abbozzato uno script che esegue il registro di installazione del prodotto e raccoglie i commit sul ramo. La terza volta ha funzionato correttamente e dopo aver “finito con un file” il ramo era pronto. 

L'assemblatore del pacchetto di installazione ha scritto il proprio, lo ha fatto in una settimana. Quindi ho dovuto modificare il programma di installazione dalle funzionalità principali del sistema, poiché è open source. Dopo una serie di controlli e miglioramenti, il risultato è stato riconosciuto come positivo. Nel frattempo ha preso forma la composizione della release, per la corretta installazione della quale è stato necessario allineare il circuito di test con quello produttivo, per questo è stato scritto uno script separato.

Naturalmente ci sono stati molti commenti sulla prima installazione, ma nel complesso il codice ha funzionato. E dopo circa la terza installazione tutto ha iniziato a sembrare a posto. Il controllo della composizione e il controllo della versione degli oggetti sono stati monitorati separatamente in modalità manuale, il che in questa fase era abbastanza giustificato.

Un'ulteriore sfida è stata il gran numero di mancate pubblicazioni di cui tenere conto. Ma con il ramo simile a Prod e Rebase, il compito è diventato trasparente.

Fin dalla prima volta, velocemente e senza errori

Al momento del rilascio, ci siamo avvicinati con un atteggiamento ottimista e più di una dozzina di installazioni di successo su diversi circuiti. Ma letteralmente un giorno prima della scadenza, si è scoperto che il venditore non aveva completato il lavoro di preparazione del rilascio per l'installazione nel modo accettato. Se per qualche motivo la nostra build fallisce, la versione verrà interrotta. E dai nostri sforzi, il che è particolarmente spiacevole. Non avevamo via d'uscita. Pertanto, abbiamo considerato opzioni alternative, preparato piani d'azione e avviato l'installazione.

Sorprendentemente, l'intera versione, composta da più di 800 oggetti, è iniziata correttamente, la prima volta e in soli 10 minuti. Per un'ora abbiamo controllato i log alla ricerca di errori, ma non ne abbiamo trovato nessuno.

Per tutto il giorno successivo nella chat di rilascio c'è stato silenzio: nessun problema di implementazione, versioni storte o codice “inappropriato”. È stato anche un po' imbarazzante. Successivamente sono emersi alcuni commenti, ma rispetto ad altri sistemi e alle esperienze precedenti, il loro numero e la loro priorità erano notevolmente inferiori.

Un ulteriore effetto del cumulativo è stato un aumento della qualità dell'assemblaggio e dei test. A causa delle molteplici installazioni della versione completa, i difetti di creazione e gli errori di distribuzione sono stati identificati in modo tempestivo. I test nelle configurazioni della versione completa hanno consentito di identificare inoltre i difetti nell'interazione degli oggetti che non venivano visualizzati durante le installazioni incrementali. È stato sicuramente un successo, soprattutto considerando il nostro contributo del 57% al rilascio.

Risultati e conclusioni

In meno di un anno abbiamo ottenuto:

  • Costruisci uno sviluppo interno a tutti gli effetti utilizzando un sistema esotico;
  • Eliminare la dipendenza critica dal fornitore;
  • Esegui CI/CD per un'eredità molto ostile;
  • Elevare i processi di implementazione a un nuovo livello tecnico;
  • Ridurre significativamente i tempi di implementazione;
  • Ridurre significativamente il numero di errori di implementazione;
  • Dichiarati con sicurezza come uno dei principali esperti di sviluppo.

Naturalmente, gran parte di ciò che viene descritto sembra una vera e propria schifezza, ma queste sono le caratteristiche del sistema e le limitazioni del processo che esistono in esso. Al momento, i cambiamenti hanno interessato prodotti e servizi IS Profile (conti master, carte plastiche, conti di risparmio, deposito a garanzia, prestiti in contanti), ma potenzialmente l'approccio può essere applicato a qualsiasi IS per il quale è impostato il compito di implementare le pratiche DevOps. Il modello cumulativo può essere replicato in modo sicuro per implementazioni successive (comprese quelle senza rilascio) da molte consegne.

Fonte: habr.com

Aggiungi un commento