Condividi tramite


DevOps

Suggerimento

Questo contenuto è un estratto dell'eBook, Progettazione di applicazioni .NET native del cloud per Azure, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Cloud Native .NET apps for Azure eBook cover thumbnail.

Il motto preferito dei consulenti software è rispondere "Dipende" a qualsiasi domanda posta. Non è perché i consulenti software non vogliono mai prendere posizione. È perché a qualsiasi domanda sul software non c'è un’unica risposta. Non ci sono un giusto e sbagliato assoluti, ma piuttosto un equilibrio tra opposti.

Prendi, ad esempio, le due principali scuole di sviluppo applicazioni Web: applicazioni a pagina singola e applicazioni lato server. Da un lato, l'esperienza utente tende ad essere migliore con le applicazioni a pagina singola e la quantità di traffico verso il server Web può essere ridotta al minimo, il che permette di ospitarle in un ambiente semplice come l'hosting statico. D'altra parte, le applicazioni a pagina singola tendono a essere sviluppate più lentamente e sono più difficili da testare. Quale è la scelta giusta? Beh, dipende dalla tua situazione.

Le applicazioni native del cloud non sono immuni alla stessa dicotomia. Hanno chiari vantaggi in termini di velocità di sviluppo, stabilità e scalabilità, ma la loro gestione può essere molto più difficoltosa.

Anni fa, non era raro che il processo di trasferimento di un'applicazione dallo sviluppo alla produzione richiedesse un mese o anche più. Le aziende rilasciavano software ogni 6 mesi o anche ogni anno. Basta guardare a Microsoft Windows per avere un'idea della cadenza delle versioni che erano accettabili prima dei giorni ever-green di Windows 10. Sono passati cinque anni da Windows XP e Vista, e altri tre da Vista a Windows 7.

È ormai assodato che la capacità di rilasciare software rapidamente offre alle aziende in rapida evoluzione un enorme vantaggio sul mercato rispetto ai loro concorrenti più lenti. È per questo motivo che gli aggiornamenti principali di Windows 10 ora escono ogni sei mesi circa.

I modelli e le procedure che consentono versioni più veloci e affidabili per offrire valore all'azienda sono note come DevOps. Si tratta di un’ampia serie di idee che spaziano dall'intero ciclo di vita dello sviluppo software, alla specifica di un'applicazione fino al recapito e alla messa in funzione di tale applicazione.

Il DevOps è nato prima dei microservizi ed è probabile che lo spostamento verso servizi più piccoli e più adatti allo scopo non sarebbe stato possibile senza di esso, in quanto ha reso più semplice il rilascio e il funzionamento non solo di una ma molte applicazioni nell'ambiente di produzione.

Figure 10-1 Search trends show that the growth in microservices doesn't start until after DevOps is a fairly well-established idea.

Figura 10-1 - DevOps e microservizi.

Grazie alle buone pratiche DevOps, è possibile sfruttare i vantaggi delle applicazioni native del cloud senza soffocare sotto una montagna di lavoro per la gestione delle applicazioni.

Non c'è un martello dorato, un unico strumento risolutivo quando si tratta di DevOps. Nessuno può vendere una soluzione completa e onnicomprensiva per il rilascio e il funzionamento di applicazioni di alta qualità. Questo perché ogni applicazione è molto diversa da tutte le altre. Tuttavia, ci sono strumenti che possono fare di DevOps una proposta molto meno scoraggiante. Uno di questi strumenti è noto come Azure DevOps.

Azure DevOps

Azure DevOps ha un lungo pedigree. Le sue radici risalgono a quando Team Foundation Server è stato spostato online per la prima volta e a quando ha cambiato nome più volte: Visual Studio Online e Visual Studio Team Services. Nel corso degli anni, tuttavia, è diventato molto più dei suoi predecessori.

Azure DevOps è suddiviso in cinque componenti principali:

Figure 10-2 The five major areas of Azure DevOps

Figura 10-2 - Azure DevOps.

Azure Repos: gestione del codice sorgente che supporta il venerabile Controllo della versione di Team Foundation e il Git preferito del settore. Le richieste pull consentono di abilitare il social coding promuovendo la discussione delle modifiche apportate.

Azure Boards: fornisce uno strumento di rilevamento dei problemi e degli elementi di lavoro che consentire agli utenti di scegliere i flussi di lavoro che fanno al loro caso. Include diversi modelli preconfigurati, tra cui quelli per supportare gli stili SCRUM e Kanban dello sviluppo.

Azure Pipelines: sistema di gestione di compilazione del rilascio che supporta una stretta integrazione con Azure. Le compilazioni possono essere eseguite su diverse piattaforme da Windows a Linux a macOS. È possibile eseguire il provisioning degli agenti di compilazione nel cloud o in locale.

Azure Test Plans: nessun addetto al controllo di qualità verrà lasciato indietro con la gestione e il supporto esplorativo dei test offerti dalla funzionalità Test Plans.

Azure Artifacts: feed di artefatti che consente alle aziende di creare le proprie versioni interne di NuGet, npm e altre. Ha il doppio scopo di fungere da cache di pacchetti upstream se si verifica un errore di un repository centralizzato.

L'unità organizzativa di primo livello in Azure DevOps è nota come Progetto. All'interno di ogni progetto i vari componenti, ad esempio Azure Artifacts, possono essere attivati e disattivati. Ognuno di questi componenti offre diversi vantaggi per le applicazioni native del cloud. I tre principali sono i repository, le lavagne e le pipeline. Se gli utenti vogliono gestire il codice sorgente in un altro stack di repository, ad esempio GitHub, ma sfruttare comunque Azure Pipelines e altri componenti, è del tutto possibile.

Fortunatamente, i team di sviluppo hanno molte opzioni quando si seleziona un repository. Uno di essi è GitHub.

Azioni di GitHub

Fondato nel 2009, GitHub è un repository ampiamente diffuso basato sul Web per l'hosting di progetti, documentazione e codice. Molte grandi aziende del settore tecnologico, come Apple, Amazon, Google, e le principali società usano GitHub. GitHub usa come base il sistema di controllo della versione distribuita open source denominato Git. Aggiunge poi un proprio set di funzionalità, tra cui rilevamento dei difetti, funzionalità e richieste pull, gestione delle attività e wiki per ogni codebase.

Man mano che GitHub si evolve, aggiunge anche funzionalità DevOps. GitHub, ad esempio, ha una propria pipeline di integrazione continua/recapito continuo (CI/CD), denominata GitHub Actions. GitHub Actions è uno strumento di automatizzazione del flusso di lavoro basato sulla community. Consente ai team DevOps di integrarsi con gli strumenti esistenti, combinare e abbinare nuovi prodotti e associare il ciclo di vita del software, inclusi i partner CI/CD esistenti."

GitHub ha oltre 40 milioni di utenti, il che lo rende il più grande host di codice sorgente al mondo. Nell’ottobre 2018 Microsoft ha acquistato GitHub. Microsoft ha promesso che GitHub rimarrà una piattaforma aperta a cui qualsiasi sviluppatore può collegarsi ed estenderla. Continua a operare come società indipendente. GitHub offre piani per account aziendali, team, professionali e gratuiti.

Controllo del codice sorgente

L'organizzazione del codice per un'applicazione nativa del cloud può risultare complessa. Al contrario di una singola ed enorme applicazione, le applicazioni native del cloud tendono a essere costituite da una rete di applicazioni più piccole che comunicano tra loro. Come succede spesso in informatica, quale sia la migliore disposizione del codice rimane una domanda aperta. Esistono esempi di applicazioni di successo che usano diversi tipi di layout, ma due varianti sembrano più popolari.

Prima di passare al controllo del codice sorgente effettivo, è opportuno decidere quanti progetti sono appropriati. All'interno di un singolo progetto è disponibile il supporto per più repository e pipeline di compilazione. Le lavagne sono un po' più complicate, ma anche lì, le attività possono essere assegnate facilmente a più team all'interno di un singolo progetto. È possibile supportare centinaia, anche migliaia di sviluppatori, da un singolo progetto Azure DevOps. Questo è probabilmente l'approccio migliore perché fornisce un'unica posizione da cui tutti gli sviluppatori possono lavorare e riduce la confusione nel trovare una determinata applicazione quando non si è sicuri del progetto in cui si trova.

La suddivisione del codice per i microservizi all'interno del progetto Azure DevOps può risultare leggermente più complessa.

Figure 10-3 Single versus Multiple Repositories

Figura 10-3 - Uno e molti repository.

Repository per microservizio

A prima vista, questo approccio sembra il più logico per suddividere il codice sorgente per i microservizi. Ogni repository può contenere il codice necessario per compilare un determinato microservizio. I vantaggi di questo approccio sono facilmente visibili:

  1. Le istruzioni per la compilazione e la gestione dell'applicazione possono essere aggiunte a un file README nella radice di ogni repository. Quando si sfogliano i repository, è facile trovare queste istruzioni, riducendo il tempo di avviamento per gli sviluppatori.
  2. Ogni servizio si trova in una posizione logica, facilmente individuabile conoscendo il nome del servizio.
  3. Le compilazioni possono essere facilmente configurate in modo che vengano attivate solo quando viene apportata una modifica al repository proprietario.
  4. Il numero di modifiche in arrivo in un repository è limitato al numero ridotto di sviluppatori che lavorano sul progetto.
  5. La sicurezza è facile da configurare, limitando i repository per cui gli sviluppatori dispongono di autorizzazioni di lettura e scrittura.
  6. Le impostazioni a livello di repository possono essere modificate dal team proprietario con un minimo di discussione con altri utenti.

Una delle idee chiave alla base dei microservizi è che i servizi devono essere isolati e separati l'uno dall'altro. Quando si usa una progettazione basata su dominio per decidere i limiti dei servizi, i servizi fungono da limiti transazionali. Gli aggiornamenti del database non devono estendersi su più servizi. Questa raccolta di dati correlati viene definita contesto delimitato. Questa idea si riflette nell'isolamento dei dati dei microservizi in un database separato e autonomo dal resto dei servizi. Ha molto senso portare questa idea fino al codice sorgente.

Tuttavia, questo approccio non è privo di problemi. Uno dei problemi di sviluppo più complessi del nostro tempo è la gestione delle dipendenze. Prendi in considerazione il numero di file che costituiscono la directory media node_modules. È probabile che una nuova installazione di qualcosa di simile a create-react-app porti con sé migliaia di pacchetti. Come gestire queste dipendenze è una questione complessa.

Se una dipendenza viene aggiornata, anche i pacchetti downstream devono aggiornare questa dipendenza. Sfortunatamente, ciò richiede un lavoro di sviluppo e quindi, inevitabilmente, la directory node_modules si ritrova con più versioni di un singolo pacchetto, ognuna delle quali è una dipendenza di un altro pacchetto con versioni leggermente diverse. Quando si distribuisce un'applicazione, quale versione di una dipendenza deve essere usata? La versione attualmente in produzione? L’attuale versione beta, ma che probabilmente sarà in produzione nel momento in cui il consumer arriverà in produzione? Problemi complessi che non vengono risolti usando solo i microservizi.

Esistono librerie che dipendono da un'ampia varietà di progetti. Dividendo i microservizi ognuno in ciascun repository, le dipendenze interne possono essere risolte meglio usando il repository interno, Azure Artifacts. Le build per le librerie eseguiranno il push delle versioni più recenti in Azure Artifacts per l'utilizzo interno. Il progetto downstream deve comunque essere aggiornato manualmente perché assuma una dipendenza dai pacchetti appena aggiornati.

Un altro svantaggio si presenta quando si trasferisce il codice tra i servizi. Anche se sarebbe bello credere che la prima divisione di un'applicazione in microservizi sia corretta al 100%, la realtà è che raramente siamo così previdenti di non commettere errori di divisione del servizio. Di conseguenza, le funzionalità e il codice che le guida dovranno passare da servizio a servizio: da repository a repository. Quando si passa da un repository a un altro, il codice perde la cronologia. Esistono molti casi, soprattutto nell’eventualità di un controllo, in cui la cronologia completa su un frammento di codice è estremamente preziosa.

L’ultimo e più importante svantaggio è il coordinamento dei cambiamenti. In un'applicazione true di microservizi non devono esistere dipendenze di distribuzione tra i servizi. Deve essere possibile distribuire i servizi A, B e C in qualsiasi ordine in quanto hanno accoppiamento libero. In realtà, tuttavia, ci sono momenti in cui è consigliabile apportare una modifica che interessa più repository contemporaneamente. Tra gli esempi ci sono l'aggiornamento di una libreria per risolvere un problema di sicurezza o la modifica a un protocollo di comunicazione usato da tutti i servizi.

Per eseguire una modifica su più repository, è necessario eseguire un commit in ogni repository in successione. Ogni modifica in ogni repository dovrà essere richiesta tramite pull ed esaminata separatamente. Questa attività può essere difficile da coordinare.

Un'alternativa all'uso di molti repository consiste nel mettere insieme tutto il codice sorgente in un unico grande repository, ben noto.

Repository singolo

In questo approccio, talvolta definito monorepository, tutto il codice sorgente di ogni servizio viene inserito nello stesso repository. In un primo momento, questo approccio sembra un'idea terribile, che rischia di rendere macchinosa la gestione del codice sorgente. Tuttavia, ci sono alcuni chiari vantaggi a lavorare in questo modo.

Il primo vantaggio è che è più facile gestire le dipendenze tra i progetti. Invece di basarsi su alcuni feed di artefatti esterni, i progetti si possono direttamente importare a vicenda. Ciò significa che gli aggiornamenti sono istantanei e le versioni in conflitto vengono probabilmente trovate in fase di compilazione nella workstation dello sviluppatore. Di fatto, spostando parte del test di integrazione a sinistra.

Quando si sposta il codice tra progetti, ora è più facile conservare la cronologia perché i file verranno rilevati come spostati invece di riscritti.

Un altro vantaggio è che è possibile apportare in un singolo commit modifiche ampie che superano i limiti del servizio. Questa attività riduce il sovraccarico dovuto alle potenziali decine di modifiche da esaminare singolarmente.

Sono disponibili molti strumenti che possono eseguire l'analisi statica del codice per rilevare procedure di programmazione non sicure o un uso problematico delle API. In un mondo multi-repository, ogni repository dovrà essere iterato per individuare i problemi in esso contenuti. Il singolo repository consente di eseguire l'analisi in un'unica posizione.

Ci sono anche molti svantaggi nell'approccio singolo repository. Uno dei più preoccupanti è che avere un singolo repository genera problemi di sicurezza. Se il contenuto di un repository viene perso in un repository per modello di servizio, la quantità di codice persa è minima. Con un unico repository, tutto ciò che la società possiede potrebbe essere persa. In passato ci sono stati molti esempi in cui questo è avvenuto e ha fatto deragliare interi sforzi di sviluppo del gioco. La presenza di più repository espone meno superficie di attacco, un tratto auspicabile nella maggior parte delle procedure di sicurezza.

È probabile che le dimensioni del singolo repository diventino rapidamente non gestibili. Ciò presenta alcune implicazioni interessanti sulle prestazioni. Potrebbe essere necessario usare strumenti specializzati, come Virtual File System per Git, originariamente progettati per migliorare l'esperienza degli sviluppatori del team di Windows.

Spesso l'argomentazione a favore dell'uso di un singolo repository si riduce al fatto che Facebook o Google usino questo metodo per la disposizione del codice sorgente. Se l'approccio è sufficiente per queste aziende, allora, sicuramente, è l'approccio corretto per tutte le aziende. La verità è che poche aziende operano su una scala simile a quella di Facebook o Google. I problemi che si verificano su tali scale sono diversi da quelli che affronterà la maggior parte degli sviluppatori. Ciò che è buono per l'oca potrebbe non essere buono per il papero.

Alla fine, entrambe le soluzioni possono essere usate per ospitare il codice sorgente per i microservizi. Tuttavia, nella maggior parte dei casi, la gestione e il sovraccarico tecnico per il funzionamento di un singolo repository non giustificano gli scarsi vantaggi. La suddivisione del codice su più repository incoraggia una migliore separazione delle problematiche e l'autonomia tra i team di sviluppo.

Struttura di directory standard

Indipendentemente dal dibattito tra repository singoli e multipli, ogni servizio avrà una propria directory. Una delle migliori ottimizzazioni per consentire agli sviluppatori di passare rapidamente da un progetto all’altro consiste nel mantenere una struttura di directory standard.

Figure 10-4 A standard directory structure for both the email and sign-in services

Figura 10-4 - Struttura di directory standard.

Ogni volta che viene creato un nuovo progetto, deve essere usato un modello che inserisce la struttura corretta. Questo modello può includere anche elementi utili come un file README dello scheletro e un azure-pipelines.yml. In qualsiasi architettura di microservizi, un livello elevato di varianza tra i progetti rende più difficili le operazioni bulk sui servizi.

Sono disponibili molti strumenti che possono fornire modelli per un'intera directory, contenente diverse directory del codice sorgente. Yeoman è popolare nel mondo JavaScript, e GitHub ha recentemente rilasciato modelli di repository, che offrono molte delle stesse funzionalità.

Gestione attività

La gestione delle attività in qualsiasi progetto può essere difficile. In primo piano ci sono innumerevoli domande a cui rispondere sul tipo di flussi di lavoro da configurare per garantire una produttività ottimale agli sviluppatori.

Le applicazioni native del cloud tendono a essere più piccole dei prodotti software tradizionali o almeno sono suddivise in servizi più piccoli. Il rilevamento di problemi o attività correlate a questi servizi è importante come per qualsiasi altro progetto software. Nessuno vuole perdere traccia di un elemento di lavoro o spiegare a un cliente che il problema non è stato registrato correttamente. Le lavagne sono configurate a livello di progetto, ma all'interno di ogni progetto è possibile definire diverse aree. Queste consentono di suddividere i problemi in più componenti. Il vantaggio di mantenere tutto il lavoro dell'intera applicazione in un'unica posizione è la possibilità di spostare facilmente gli elementi di lavoro da un team a un altro perché sono più comprensibili.

Azure DevOps include diversi modelli comuni preconfigurati. Nella configurazione più semplice, tutto ciò che è necessario sapere è cosa c'è nel backlog, su cosa stanno lavorando le persone e cosa è già stato fatto. È importante avere questa visibilità sul processo di compilazione del software, in modo da poter stabilire le priorità e riferire al cliente le attività completate. Naturalmente, alcuni progetti software si limitano a un processo semplice come to do, doing e done. Non ci vuole molto tempo per iniziare ad aggiungere al processo passaggi come QA o Detailed Specification.

Una delle parti più importanti delle metodologie Agile è l'auto-verifica a intervalli regolari. Queste analisi sono progettate per fornire informazioni dettagliate sui problemi che il team sta affrontando e su come possono essere risolti. Spesso questo significa modificare il flusso di problemi e funzionalità tramite il processo di sviluppo. Quindi, è perfettamente sano espandere i layout delle lavagne con fasi aggiuntive.

Le fasi delle lavagne non sono l'unico strumento organizzativo. A seconda della configurazione della lavagna, esiste una gerarchia di elementi di lavoro. L'elemento più granulare che può comparire su una lavagna è l'attività. Un'attività predefinita contiene campi per un titolo, una descrizione, una priorità, una stima della quantità di lavoro rimanente e la possibilità di collegarsi ad altri elementi di lavoro o elementi di sviluppo (rami, commit, richieste pull, compilazioni e così via). Gli elementi di lavoro possono essere classificati in diverse aree dell'applicazione e diverse iterazioni (sprint) per semplificarne la ricerca.

Figure 10-5 An example task in Azure DevOps

Figura 10-5 - Attività in Azure DevOps.

Il campo descrizione supporta gli stili normali previsti (grassetto, sottolineato, corsivo e barrato) e la possibilità di inserire immagini. Questo lo rende uno strumento potente da usare quando si specificano i bug o il lavoro.

Le attività possono essere raggruppate in funzionalità, che definiscono un'unità di lavoro più grande. Le funzionalità, a loro volta, possono essere raggruppate in epiche. Classificando le attività in questa gerarchia, è molto più semplice capire quanto sia vicina l’uscita di una grande funzionalità.

Figure 10-6 Work item types configured by default in the Basic process template

Figura 10-6 - Elemento di lavoro in Azure DevOps.

Esistono diversi tipi di visualizzazioni dei problemi in Azure Boards. Gli elementi non ancora pianificati compaiono nel backlog. Da qui, possono essere assegnati a uno sprint. Uno sprint è una finestra temporale durante la quale si prevede di completare una certa quantità di lavoro. Questo lavoro può includere attività, ma anche la risoluzione dei ticket. Una volta nello sprint, questo può essere gestito dalla sezione Sprint della lavagna. Questa visualizzazione mostra che il lavoro è in corso e include un grafico burn-down che fornisce una stima sempre aggiornata del successo dello sprint.

Figure 10-7 A board with a sprint defined

Figura 10-7 - Lavagna in Azure DevOps.

A questo punto, dovrebbe essere evidente la grande potenzialità delle lavagne di Azure DevOps. Per gli sviluppatori, ci sono semplici visualizzazioni di ciò su cui si sta lavorando. Per i project manager, visualizzazioni del lavoro imminente, nonché una panoramica del lavoro esistente. Per i manager, sono disponibili molti report relativi al resourcing e alla capacità. Sfortunatamente, non c'è nulla di magico per le applicazioni native del cloud che permetta di eliminare la necessità di tenere traccia del lavoro. Tuttavia, se è necessario tenere traccia del lavoro, poche soluzioni offrono un’esperienza migliore di quella di Azure DevOps.

Pipeline CI/CD

Quasi nessun cambiamento nel ciclo di vita dello sviluppo software è stato così rivoluzionario come l'avvento dell'integrazione continua (CI) e del recapito continuo (CD). La compilazione e l'esecuzione di test automatizzati sul codice sorgente di un progetto non appena viene verificata una modifica rilevano gli errori in anticipo. Prima dell'avvento delle compilazioni di integrazione continua, non era raro eseguire il pull del codice dal repository e scoprire che non aveva superato i test o che non era stato nemmeno possibile compilarlo. Questo ha portato alla necessità di rilevare l'origine dell'interruzione.

Tradizionalmente, inviare il software all'ambiente di produzione richiedeva una documentazione completa e un elenco di passaggi. Ognuno di questi passaggi doveva essere completato manualmente in un processo soggetto a molti errori.

Figure 10-8 A checklist

Figura 10-8 - Checklist.

La sorella dell'integrazione continua è il recapito continuo, in cui i pacchetti appena compilati vengono distribuiti in un ambiente. Il processo manuale non può essere ridimensionato in modo che corrisponda alla velocità di sviluppo, perciò l'automazione diventa più importante. Le checklist vengono sostituite da script che possono eseguire le stesse attività in modo più rapido e accurato rispetto a qualsiasi essere umano.

L'ambiente di destinazione del recapito continuo può essere un ambiente di test o, come avviene in molte aziende tecnologiche importanti, potrebbe essere l'ambiente di produzione. Quest'ultimo richiede un investimento in test di alta qualità, che assicurano che una modifica non interrompa la produzione per gli utenti. Allo stesso modo in cui l'integrazione continua intercettava tempestivamente i problemi nel codice, il recapito continuo rileva in anticipo i problemi nel processo di distribuzione.

L'importanza di automatizzare il processo di compilazione e recapito è accentuata dalle applicazioni native del cloud. Le distribuzioni si verificano più frequentemente e in più ambienti, per cui la distribuzione manuale diventa praticamente impossibile.

Compilazioni di Azure

Azure DevOps offre un set di strumenti per semplificare l'integrazione continua e la distribuzione. Questi strumenti si trovano in Azure Pipelines. Il primo è Azure Builds, uno strumento per l'esecuzione di definizioni di compilazione basate su YAML su larga scala. Gli utenti possono usare macchine di compilazione personalizzate (ideali per i casi in cui la compilazione richiede un ambiente meticolosamente configurato) o usare il computer di un pool costantemente aggiornato di macchine virtuali ospitate in Azure. Questi agenti di compilazione ospitati sono preinstallati con un'ampia gamma di strumenti di sviluppo non solo per lo sviluppo .NET, ma per tutti gli altri tipi di sviluppo, da Java a Python a iPhone.

DevOps include un'ampia gamma di definizioni di compilazione predefinite che possono essere personalizzate per qualsiasi compilazione. Le definizioni di compilazione vengono definite in un file denominato azure-pipelines.yml e archiviate nel repository in modo che possano essere controllate insieme al codice sorgente. In questo modo è molto più semplice apportare modifiche alla pipeline di compilazione di un ramo, perché le modifiche possono essere archiviate solo in tale ramo. Un esempio azure-pipelines.yml per la creazione di un'applicazione Web ASP.NET nel framework completo è illustrato nella figura 10-9.

name: $(rev:r)

variables:
  version: 9.2.0.$(Build.BuildNumber)
  solution: Portals.sln
  artifactName: drop
  buildPlatform: any cpu
  buildConfiguration: release

pool:
  name: Hosted VisualStudio
  demands:
  - msbuild
  - visualstudio
  - vstest

steps:
- task: NuGetToolInstaller@0
  displayName: 'Use NuGet 4.4.1'
  inputs:
    versionSpec: 4.4.1

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    restoreSolution: '$(solution)'

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: '$(solution)'
    msbuildArgs: '-p:DeployOnBuild=true -p:WebPublishMethod=Package -p:PackageAsSingleFile=true -p:SkipInvalidConfigurations=true -p:PackageLocation="$(build.artifactstagingdirectory)\\"'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: VSTest@2
  displayName: 'Test Assemblies'
  inputs:
    testAssemblyVer2: |
     **\$(buildConfiguration)\**\*test*.dll
     !**\obj\**
     !**\*testadapter.dll
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: CopyFiles@2
  displayName: 'Copy UI Test Files to: $(build.artifactstagingdirectory)'
  inputs:
    SourceFolder: UITests
    TargetFolder: '$(build.artifactstagingdirectory)/uitests'

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: '$(artifactName)'
  condition: succeededOrFailed()

Figura 10-9 - Esempio azure-pipelines.yml

Questa definizione di compilazione usa una serie di attività predefinite che rendono le compilazioni semplici come la costruzione di un set Lego (più semplice del gigantesco Millennium Falcon). Ad esempio, l'attività NuGet ripristina i pacchetti NuGet, mentre l'attività VSBuild chiama gli strumenti di compilazione di Visual Studio per eseguire la compilazione effettiva. In Azure DevOps sono disponibili centinaia di attività diverse, con altre migliaia di attività gestite dalla community. È probabile che, indipendentemente da quali attività di compilazione si sta cercando di eseguire, qualcuno ne abbia già creata una.

Le compilazioni possono essere attivate manualmente, da un check-in, in base a una pianificazione o dal completamento di un'altra compilazione. Nella maggior parte dei casi, è auspicabile che la creazione avvenga a ogni check-in. Le compilazioni possono essere filtrate in modo che diverse compilazioni vengano eseguite su parti diverse del repository o su rami diversi. Ciò consente scenari come l'esecuzione di compilazioni veloci con test ridotti sulle richieste pull e l'esecuzione di una suite di regressione completa sul trunk su base notturna.

Il risultato finale di una compilazione è una raccolta di file noti come artefatti della compilazione. Questi artefatti possono essere passati alla fase successiva del processo di compilazione o aggiunti a un feed di Azure Artifacts, in modo che possano essere usati da altre compilazioni.

Versioni di Azure DevOps

Le compilazioni si occupano della compilazione del software in un pacchetto spedibile, ma gli artefatti devono comunque essere inseriti in un ambiente di test per completare il recapito continuo. Per questo motivo, Azure DevOps usa uno strumento separato denominato Versioni. Lo strumento Versioni usa la stessa libreria di attività disponibile per la compilazione, ma introduce il concetto di "fasi". Una fase è un ambiente isolato in cui è installato il pacchetto. Ad esempio, un prodotto può usare uno sviluppo, un controllo di qualità e un ambiente di produzione. Il codice viene distribuito continuamente nell'ambiente di sviluppo in cui è possibile eseguire i test automatizzati. Una volta che la versione passa i test, si sposta nell'ambiente controllo di qualità per il test manuale. Infine, il codice viene inserito nell'ambiente di produzione in cui è visibile a tutti.

Figure 10-10 An example release pipeline with Develop, QA, and Production phases

Figura 10-10 - Pipeline delle versioni

Ogni fase della compilazione può essere attivata automaticamente dal completamento della fase precedente. In molti casi, tuttavia, questo non è auspicabile. Lo spostamento del codice nell'ambiente di produzione potrebbe richiedere l'approvazione da parte di qualcuno. Lo strumento Versioni supporta questa funzionalità consentendo ai responsabili approvazione di intervenire a ogni passaggio della pipeline di versione. Le regole possono essere configurate in modo che una persona o un gruppo specifico di persone debba firmare una versione prima che venga inserita nell'ambiente di produzione. Questi varchi consentono di effettuare controlli di qualità manuali e di soddisfare i requisiti normativi correlati al controllo di ciò che entra in produzione.

Tutti ottengono una pipeline di compilazione

Per la configurazione di molte pipeline di compilazione non è previsto alcun costo, quindi è vantaggioso avere almeno una pipeline di compilazione per ogni microservizio. Idealmente, i microservizi sono distribuibili in modo indipendente in qualsiasi ambiente, perciò è perfetto che ognuno di essi possa essere rilasciato tramite la propria pipeline senza che venga rilasciata una massa di codice non correlato. Ogni pipeline può avere un proprio set di approvazioni che consentono variazioni nel processo di compilazione per ogni servizio.

Release controllo delle versioni

Uno svantaggio nell'uso della funzionalità Versioni è che non può essere definito in un file archiviato azure-pipelines.yml. Esistono molti motivi per cui è consigliabile eseguire questa operazione, dalla presenza di definizioni di versione per ramo all'inclusione di una versione scheletro nel modello di progetto. Fortunatamente, il lavoro per spostare alcune delle fasi di supporto nel componente di compilazione è in corso. Questa operazione sarà nota come compilazione a più fasi e la prima versione è ora disponibile!