No results found
Chi si occupa di sviluppo software sa che ormai la disciplina tende a migrare verso una definizione più industriale a partire da quella che invece per tanto tempo è stata un processo artigianale; spesso piccole realtà, soprattutto quelle in cui magari non ci si occupa esclusivamente di sviluppo software ma a un certo punto si sviluppa anche il software insieme ad altri servizi o prodotti, accade che il processo di realizzazione di questi progetti sia molto artigianale, cioè molto legato più agli interessi dei programmatori del team a alle loro competenze, senza che si adottino delle metodologie strutturate.
Oggi sappiamo, vista la complessità dei progetti software, delle tecnologie che spesso sono coinvolte anche all'interno dello stesso progetto è facile trovare applicazioni anche piuttosto piccole che richiedono la conoscenza o l'integrazione di linguaggi diversi, librerie diverse e tecnologie diverse e così via, si rende quindi fondamentale l’adozione di un processo più rigoroso.
Ecco perché anche in letteratura si comincia a parlare di processi industriali legati alle metodologie di sviluppo software. Poi ci sono altre parole che cominciano a essere veramente ricorrenti, come lo sviluppo software mediante metodologia Jail e si parla anche di lezioni apprese da esperienze pregresse.
In quest’ottica può essere interessante leggere un saggio che si trova on-line, chiamato La cattedrale e il bazaar, che analizza due modelli di rilascio nei progetti Open Source; in particolare l’autore mostra cosa succede quando c’è poca interazione tra gli sviluppatori.
Per farlo prende ad esempio progetti Open Source molto noti, cioè da una parte il kernel di Linux e dall'altra il compilatore gcc e mette a confronto le due metodologie di sviluppo: il kernel di Linux è un progetto estremamente dinamico e aperto, nel senso che tutti gli sviluppatori che collaborano al progetto possono in qualunque momento revisionare il codice; questo è chiamato Bazar, a indicare un’apparente confusione.
A questo si contrappone ciò che l’autore chiama Cattedrale, quindi una modalità più verticale, più rigida, che era quella seguita dal progetto gcc, nel quale c’era un’elite di sviluppatori che effettivamente sviluppava le versioni e solo quando si arrivava a una versione stabile il codice veniva aperto.
Per quanto sembri confusionario, il modello Bazar presenta tra i punti di forza l’interazione tra gli sviluppatori, ossia il fatto che chiunque abbia subito accesso alle modifiche che ha fatto qualcun altro e può quindi revisionarle aumenta la qualità del prodotto finale e consente di avere un team più conscio di quelle che sono le caratteristiche del progetto.
Questo preambolo serve a far capire quali sono le metodiche di governance di un progetto di sviluppo software anche di piccola entità; spesso si crede che metodologie come la Jail siano cose da relegare ai grandi progetti, ma in realtà proprio nei piccoli team, composti da un numero ridotto di sviluppatori riuscire ad ottimizzare l'utilizzo delle risorse ha anche umane quindi evitare che gli sviluppatori vengano impegnati in operazioni ripetitive come il deploy è fondamentale.
Concepire i progetti software come processi industriali e quindi come realizzazione di prodotti industriali implica la definizione di un ciclo di sviluppo software, dove, come propone l’immagine seguente, tipicamente ci sono sempre sei fasi.
In realtà il numero può differire in base al tipo di software. Può succedere, ad esempio, che manchi un passaggio di deploy ma ci sia un passaggio di integrazione.
Quello che ci interessa è come ottimizzare il processo.
In questo contesto si colloca il DevOps (contrazione delle parole Development e Operations) ossia l’unione delle attività di sviluppo e delle Operations, cioè di tutte quelle attività tipicamente IT che invece servono per far sì che l'applicazione che abbiamo sviluppato sia operativa e possa funzionare.
Nel caso di un prodotto software che poi è associato di un servizio, è chiaro che l’aspetto di Operations ha un ruolo importante; nel caso di un'applicazione desktop, invece, l'aspetto di Operation può essere legato alla gestione dei repository per gli aggiornamenti.
C'è invece chi fa una distinzione meno netta e considera come Operations anche tutti quegli aspetti legati ai servizi a supporto degli sviluppatori, quindi anche la gestione dei tool interni all'azienda che vengono utilizzati dal team di sviluppo e quindi sono comunque problematiche IT, si fanno ricadere sotto l'ombrello delle Operations.
Tutto questo per dire che le metodologie DevOps hanno alla base l'idea di creare una sinergia tra team di sviluppo e team IT; DevOps è quindi l’insieme di metodologie e strumenti che consentono un incremento significativo della capacità di sviluppare servizi velocemente, quindi non solo di lavorare meglio ma anche più in fretta rispetto a un qualsiasi altro processo industriale.
Per sua natura, lo sviluppo software richiede tempi rapidissimi: si va dall'analisi alla raccolta dei requisiti al rilascio in tempi brevissimi. Quindi cercare di ottimizzare gli aspetti legati al ciclo di vita del software in modo da rendere le operazioni ripetitive sempre più automatizzabili quindi sempre più rapide, determina un incremento prestazionale. Ciò viene schematizzato nell’immagine seguente, dove nell’anello di Moebius due aspetti fondamentali sono proprio Continuous integration e Continuous feedback.
Con la Continuous Integration cerchiamo di ottimizzare i processi che permettono di integrare le modifiche al codice sorgente, quindi con la Continuous Feedback partire dai servizi in esecuzione dell'applicazione in esecuzione e ottenere feedback da utilizzare in progettazione.
In informatica DevOps (dalla contrazione dei termini Development e operations, inteso come deployment o rilascio) è una metodologia di sviluppo del software che punta alla comunicazione, collaborazione e integrazione tra sviluppatori e addetti alle operations dell'information technology. DevOps vuole rispondere all'interdipendenza tra sviluppo software e IT operations, puntando ad aiutare a sviluppare in modo più rapido ed efficiente prodotti e servizi software.
Nel discorso appena fatto si inquadra GitLab. La stesura di software e firmware non è limitata alla conoscenza e all’utilizzo dei linguaggi di programmazione, al debug e all’ottimizzazione del codice: coinvolge e ha sempre coinvolto anche la gestione delle versioni.
Proprio il versioning è diventato, con il lavoro in team e la collaborazione in rete, un problema rilevante, al quale sono state fornite alcune soluzioni, tra le quali spicca Git, che è un motore di versioning gestibile da interfacce grafiche utente (GUI) sia in locale, sia su piattaforme web, una delle quali è GitLab.
Per capire che cos’è GitLab è necessario innanzitutto spiegare che cos’è Git; una volta che ciò risulterà chiaro arriveremo quindi a spiegare che cosa sia la piattaforma GitLab (open source GitHub) e a conoscere le sue caratteristiche principali, per poi concludere con una breve demo dove faremo insieme i primi passi nel mondo di Git.
In linea generale si chiama sistema di controllo di versione (o versioning oppure VCS, acronimo di Version Control Systems) tutto ciò che permette di gestire le versioni, modifiche, release di software e firmware; questo, allo scopo sia di identificare cronologicamente le modifiche, sia di annullare quelle che hanno determinato un malfunzionamento o che sono divenute superflue nel tempo, sia di rintracciare l’autore nel caso di progetto al quale lavorano più sviluppatori.
Dunque, un sistema di versioning:
Git è un software di controllo di versione che permette ad ogni utente (client) di fare anche da server per se stesso; inoltre possiede una copia locale del repository. Git è il motore della piattaforma GitLab. Git funziona a riga di comando, perciò per renderne l’utilizzo più agevole è conveniente ricorrere a un’interfaccia grafica che semplifichi l’uso dei comandi; l'interfaccia può anche essere implementata in un servizio web.
Tre concetti fondamentali di Git, che ricorreranno in questo tutorial, sono gli snapshot, i commit, i repository e i branch: analizziamoli insieme.
Snapshot
Gli snapshot (istantanee) sono il modo in cui git mantiene traccia della cronologia del tuo codice; uno snapshot essenzialmente registra lo stato attuale di tutti i file in un dato momento.
Con Git decidi tu quando creare uno snapshot e su quali file ed hai sempre la possibilità di andare indietro a rileggere uno snapshot precedente (ossia una versione) per ricercare un bug o effettuare o rimuovere una modifica al codice.
Commit
Il commit è, in Git, l’azione con la quale creiamo uno snapshot; i commit rappresentano il modo in cui si “salvano” le modifiche fatte al codice. Di seguito trovate un esempio di commit. Il commit corrisponde al salvataggio di uno o più file sul repository; è quindi giunto il momento di spiegare che cos’è quest’ultimo.
Repository
Letteralmente tradotto in deposito o magazzino (ma anche contenitore, se vogliamo) il repository è una collezione di tutti i file, con in evidenza la loro cronologia e l’history dei commit. La parola repository indica un archivio ordinato dove sono raccolti i file del progetto.
Il repository può risiedere su una computer locale o su un server remoto come, è nel caso di GitLab.
In senso generale, un repository è un’area di memoria di massa accessibile da un’interfaccia utente, che riporta un gruppo di file e può essere condiviso, come nel caso di GitHub, dal quale tramite web scarichiamo software, firmware ecc.
Le principali funzionalità connesse ai repository sono:
Branch
Letteralmente tradotto in “ramo”, il branch è una branca o diramazione nella struttura ad albero del repository, come mostrato nella figura sottostante.
Git archivia i file e li tiene ordinati ad albero, evidenziando come sia facile ed intuitivo poter vedere le differenze di un file (o progetto) dopo che è stato modificato dal primo salvataggio “commit”.
Va ricordato che:
Quindi si può affermare che dei commit tra loro collegati risiedono in uno o più branch, all’interno di un repository. Ciò detto, abbiamo finalmente tutte le basi per riprendere la domanda iniziale e darle una risposta.
Che cos’è Gitlab?
Si tratta di una piattaforma on-line basata su Git, che facilita lo sviluppo collaborativo di firmware e software, semplifica il lavoro distribuito mettendo a disposizione una piattaforma centralizzata; sostanzialmente GitLab è una piattaforma che consente di utilizzare il “motore” di versioning Git, analogamente a quanto avviene sulla piattaforma GitHub. Prima di procedere è opportuna una precisazione, visto che spesso si fa molta confusione: GitHub è nata per ospitare progetti sviluppati con Git; offre spazio a pagamento per progetti privati e spazio gratuito per progetti condivisi open source. GitLab è invece un prodotto free.
GitLab è disponibile in due versioni:
La prima consente di installare GitLab su un proprio server locale o su un servizio di cloud privato e consente il pieno controllo della piattaforma e dei propri dati: è l’ideale per le aziende che necessitano di accedere esclusivamente e in ogni momento ai dati dei propri progetti, giacché offre più sicurezza dagli attacchi esterni e velocità di accesso, data l’esclusività dell’utilizzo del server. Tale versione è totalmente gratuita.
La versione SaaS (Software as a Service), invece, risiede tutta sulla soluzione cloud gitlab.com, file compresi. A differenza di piattaforme on-line come GitHub e servizi quali Bit Bucket (basato anch’esso su Git), su GitLab abbiamo:
Gitlab è una piattaforma con focus particolare in ambito DevOps, offrendo:
Ora concentriamoci sui due aspetti cardine del DevOps; come anticipato, per Continuous Integration (CI) intendiamo quella serie di metodologie che permettono di monitorare le modifiche al codice sorgente, principalmente al fine di evitare regression, ma non solo; invece per Continuous Delivery (CD) ci riferiamo la possibilità di automatizzare quanto possibile il processo di deploy della propria applicazione. Questo, come vedremo, comporta diversi vantaggi non soltanto in termini di utilizzo delle risorse del team (perché a questo punto non è più uno dei programmatori che deve occuparsi di fare il deploy) ma anche in termini di sicurezza e qualità, in quanto il tutto avviene con delle operazioni prestabilite e in un ambiente controllato.
In particolare GitLab offre una soluzione abbastanza completa perché è completamente multipiattaforma, è indipendente dal linguaggio ed è una soluzione altamente scalabile.
Tra i vantaggi per le figure che gestiscono il team e che devono poi valutare le performance del team, c’è un modulo di reportistica che consente effettivamente di avere un alto grado di monitoraggio e quindi di sapere esattamente cosa è avvenuto ogni qualvolta per esempio si è fatto una modifica (un commit) sui repository.
Vediamo come funziona la Continuous Integration in GitLab, aiutandoci con l’immagine seguente, la quale mostra graficamente quanto anticipato: il tutto parte dai repository, dai codici sorgenti.
Nel momento in cui uno sviluppatore effettua un commit, automaticamente si avvia la pipeline di Continuous Integration e quindi si avvia una serie di operazioni, dalla compilazione al build dell'applicazione (qualora la tecnologia sulla quale si basa lo prevede) fino all’esecuzione dei test integrati; quindi degli unit test che vengono fatti a partire proprio dal codice che è stato appena “committato” e qualora queste operazioni diano esito positivo GitLab avvia per noi anche la pipeline di delivery che avvia tutta una serie di operazioni che poi possono portare direttamente al deployment dell'applicazione.
Quindi come vedete l'ottimizzazione dell'utilizzo delle risorse consiste nel fatto che gli sviluppatori devono soltanto occuparsi di sviluppo e tutte le altre operazioni connesse alla revisione del codice o addirittura il deployment, vengono gestite automaticamente da GitLab in base alla configurazione del repository.
Vediamo come funziona questa infrastruttura, aiutandoci con l’immagine seguente: GitLab è un'applicazione web e gira su uno o più server. Ogni commit avvia delle pipeline e una pipeline è in pratica una sequenza di operazioni, che nel gergo di GitLab si chiamano job. I job vengono eseguiti su altre macchine, siano esse fisiche, virtuali o container attraverso il Runner.
Quindi il Runner è un servizio che gira su una macchina opportunamente configurata e connessa al server GitLab, che si prende carico di uno o più job ogni qualvolta viene fatta l'operazione sul repository. GitLab gestisce questi job monitorandone lo stato, quindi verifica se il job effettivamente ha dato errore, se è fallito oppure no e nel caso in cui non vi siano stati errori procede con il successivo nella pipeline.
Le pipeline vengono eseguite fino alla fine qualora non vi siano errori, oppure nel caso in cui un job sia andato in errore, si interrompe e viene data notifica agli sviluppatori interessati che un determinato commit sul repository, ossia una determinata modifica ai codici sorgente, ha generato degli errori.
L’immagine seguente esemplifica graficamente come GitLab esegue una pipeline; i riquadri sono dei job divisi in più stati, perché tipicamente ci sono quattro stati in un progetto software: si compila l'applicazione, si testa, di impacchetta e si esegue l’upload.
Questo dipende chiaramente dalla tecnologia utilizzata. Dunque dopo la build si avviano gli unit test sui vari componenti che sono oggetto del repository e se i test danno esito positivo si passa a un ambiente di staging, che poi permetterà di fare un deployment in produzione.
Il pregio di questa architettura è che le azioni scaturiscono automaticamente qualora si faccia un commit sul repository.
Il dettaglio sulle operazioni che Vengono eseguite dalla pipeline dipende fortemente dal progetto in questione, sia per le tecnologie utilizzate che per la natura del progetto stesso.
Gli autori di GitLab hanno quindi previsto un piccolo linguaggio derivato da YAML, per consentire agli sviluppatori di descrivere le pipeline e quindi job che devono essere eseguiti sia in Continuous Integration che in Continuous Delivery; tali operazioni vengono descritte in un file da aggiungere nella root del repository, che si chiama .gitlab-ci.yml. Nell’immagine seguente vedete un esempio di tale file.
Si tratta di una pipeline che esegue tre stati, uno di compilazione, uno di testing e uno di deploy per un'applicazione scritta in C++ per Linux e MacOs. Ci sono più job perché serve un runner per compilare l’applicazione su MacOs (parte che inizia con build:osx:) e un altro per compilarla su Linux (build:linux:) allo stesso modo. Siccome la compilazione produrrà eseguibili diversi sui vari Runner, avremo uno script di testing per MacOS (test:osx:) e un altro per Linux (test:linux:).
Poi c'è lo stadio di deploy, unica perché che viene eseguito da entrambi Runner.
Quindi con il file .gitlab-ci.yml si descrivono i job della pipeline, quindi si istruisce GitLab sulle operazioni che caratterizzano le nostre pipeline di Continuous Integration e Continuous Delivery.
Ma come avviene effettivamente l'esecuzione? Allora, quello che succede per esempio su gitlab.com e quindi sul servizio messo a disposizione in Cloud da GitLab, i runner (i server che effettivamente eseguono i job...) sono dei container docker opportunamente configurati. GitLab dispone di un server particolarmente efficiente che si chiama Bastion (immagine seguente) che permette di attivare on demand i container che servono per eseguire le pipeline dei repository degli utenti di GitLab, quindi in questo modo con i runner che vengono gestiti on-thefly si evita il problema di avere tantissime macchine che stanno in idle per la maggior parte del tempo e vengono attivate esclusivamente quando si avvia una pipeline (per esempio quando si effettua un commit).
Chiaramente la stessa architettura può essere replicata anche nel caso di un'installazione on-premises quindi qualora decidessimo di installare GitLab su una nostra macchina bisogna avere cura di configurare non soltanto GitLab, ma anche Bastion, un repository di immagini e quant'altro. L'operazione è descritta nella documentazione ufficiale; non si tratta di una cosa estremamente complessa ma conferisce un elevato grado di efficienza perché permette di riutilizzare le risorse dei server GitLab in base alle nostre istanze.
Quindi spesso quello che succede è che si configurano dei runner condivisi, cioè non si configura un runner per ogni progetto (per ogni repository che abbiamo su GitLab) bito perché questo sarebbe poco efficiente, ma in genere si adotta una struttura di questo tipo. GitLab offre anche la possibilità di avere dei Runner dedicati, cioè delle macchine particolari dedicate a effettuare un job nelle pipeline di un determinato repository qualora in quel repository abbiamo necessità di eseguire operazioni specifiche.
Facciamo ora un esempio di utilizzo del servizio di configurazione di una semplice pipeline di Continuous Integration in un repository su GitHub.
Partiamo da un repository contenente una semplicissima applicazione scritta in C++ composta dal classico file main.cpp e da un makefile che quindi ci consente di compilare l'applicazione.
Se vogliamo costruire una pipeline di Continuous Integration possiamo iniziare direttamente dal comando di menu Setup CI/CD situato a sinistra della pagina di GitLab (immagine seguente).
Questo ci consente di aggiungere, direttamente dall'interfaccia di GitLab, un file nel repository; come vedete, il nome suggerito è il solito .gitlab-ci.yml. Questo file ovviamente può essere inserito anche direttamente dalla propria workstation e poi si può effettuare il commit e il push, esattamente come si farebbe per qualunque altro tipo di sorgenti. In questo caso utilizziamo direttamente l'interfaccia web, per comodità.
Con riferimento all’immagine seguente, analizziamo le righe componenti il file .gitlab-ci.yml: la prima riga (image: gcc) serve a istruire GitLab sul tipo di runner che vogliamo venga associato al nostro repository.
In questo caso vogliamo un'immagine Docker che contenga un compilatore C++, quindi l'immagine corrispondente nel registro delle immagini Docker su gitlab.com è gcc. Dopodiché descriviamo con la sintassi che trovate nella documentazione di GitLab un semplice stadio con un solo job (Build) il cui comando da eseguire è make, che produrrà un file eseguibile (artifacts) che si chiama Hello. Effettuiamo un commit direttamente dall'applicazione web (basta cliccare sul pulsante verde Commit changes) e a questo punto GitLab inizia ad eseguire una pipeline di Continuous Integration; questo perché ha riconosciuto il file .gitlab-ci ed ha iniziato le operazioni descritte nello stadio build della nostra pipeline. In questo caso la prima cosa che fa è fare il pull dell'immagine Docker che contiene il compilatore per poter effettuare la compilazione del programma. Dopodiché parte il comando make sul runner e l'eseguibile viene generato.
Come mostra l’immagine seguente, in alto a sinistra apparirà il pulsante verde passed il quale comunica che la pipeline è stata eseguita con successo ed è stato creato un artifact; questo è quello che ci aspettiamo ogniqualvolta eseguiamo un commit sul repository e non sono stati introdotti degli errori.
Possiamo anche, se lo vogliamo, scaricare l'eseguibile prodotto su un nostro computer locale o dove desideriamo: basta cliccare sull’apposito pulsante in fondo alla riga (Artifacts) del pulsante verde passed.
Questo vale se non si verifica alcun errore, ma cosa accade in caso nella pipeline qualcosa non vada per il verso giusto?
Creiamo un altro commit ed effettuiamo al file creato prima una modifica ai sorgenti che introduce un errore, per esempio di compilazione, visto che l'unica cosa che abbiamo fatto è una pipeline di build. In pratica modifichiamo il messaggio Hello, però supponiamo che il nostro programmatore crei un errore di sintassi e che non si accorga di questo errore ed effettui direttamente un commit sul su repository.
Lo stesso vale nel caso in cui il compito venga effettuato da una copia locale del repository poi sincronizzata con il repositori stesso.
Se andiamo a verificare lo stato di esecuzione della pipeline esattamente come prima, in questo caso l'operazione di compilazione genera un errore e quindi la pagina si interrompe con un esito negativo (immagine seguente, pulsante rosso con scritto Failed); questo permette a chi si occupa di revisione del codice e agli altri sviluppatori di verificare immediatamente qual è il commit che ha introdotto l’errore e correggere il problema, perché lo possono immediatamente individuare.
Basta cliccare sul pulsante rosso Failed e si accede all’elenco dei commit, dove verrà evidenziato in rosso quello corrispondente alla modifica che ha generato l’errore.
Questo è, in sintesi, il cuore della Continuous Integration, cioè la possibilità di integrare velocemente le modifiche apportate al codice e di revisionare immediatamente senza che vi siano dei falsi positivi dovuti, per esempio, all'ambiente di sviluppo. Ad esempio, nel caso di un'applicazione C++ è possibile che lo sviluppatore non ricompili tutto da zero e semplicemente questo errore non salti fuori.
A proposito del monitoraggio, nell'esempio precedente in cui la pipeline non è stata eseguita correttamente lo sviluppatore avrà ricevuto un'e-mail immediatamente con la notifica del numero di versione del Commit che ha generato l'errore e con in allegato l'indicazione del dettaglio dell'errore nella pipeline.