Condividi tramite


Come Microsoft utilizza DevOps nello sviluppo.

Microsoft si impegna a usare One Engineering System per creare e distribuire tutti i prodotti Microsoft con un solido processo DevOps incentrato su un flusso di distribuzione e rilascio Git. Questo articolo illustra l'implementazione pratica, il modo in cui il sistema viene ridimensionato da piccoli servizi a esigenze di sviluppo di piattaforme di grandi dimensioni e lezioni apprese dall'uso del sistema in vari team Microsoft.

L'adozione di un processo di sviluppo standardizzato è un'impresa ambiziosa. I requisiti delle diverse organizzazioni Microsoft variano notevolmente, e i requisiti dei vari team all'interno delle organizzazioni crescono in proporzione alla dimensione e complessità. Per soddisfare queste diverse esigenze, Microsoft usa una strategia di diramazione basata su trunk per aiutare a sviluppare rapidamente i prodotti, distribuirli regolarmente e distribuire le modifiche in modo sicuro nell'ambiente di produzione.

Microsoft usa anche i principi di progettazione della piattaforma come parte del sistema one engineering.

Flusso di aggiornamento Microsoft

Ogni organizzazione deve stabilire un processo di rilascio del codice standard per garantire la coerenza tra i team. Il flusso di versione Microsoft incorpora i processi DevOps dallo sviluppo al rilascio. I passaggi di base del flusso di rilascio sono costituiti da branch, push, pull request e merge.

Branch

Per correggere un bug o implementare una funzionalità, uno sviluppatore crea un nuovo ramo fuori dal ramo di integrazione principale. Il modello di diramazione Git lightweight crea questi rami di argomenti di breve durata per ogni contributo del codice. Gli sviluppatori eseguono il commit anticipato ed evitano rami di funzionalità a esecuzione prolungata usando flag di funzionalità.

Spingere

Quando lo sviluppatore è pronto per l'integrazione e la distribuzione delle modifiche al resto del team, esegue il push del ramo locale in un ramo nel server e apre una richiesta pull. I repository con diverse centinaia di sviluppatori che lavorano in molti rami usano una convenzione di denominazione per i rami del server per alleviare confusione e proliferazione dei rami. Gli sviluppatori in genere creano rami denominati users/<username>/feature, dove <username> è il nome dell'account.

Richiesta di pull

Le richieste pull controllano il ramo dell'argomento si uniscono nel ramo principale e assicurano che i criteri dei rami siano soddisfatti. Il processo di richiesta pull compila le modifiche proposte ed esegue un passaggio di test rapido. I gruppi di test di primo e secondo livello eseguono circa 60.000 test in meno di cinque minuti. Questa non è la matrice di test Microsoft completa, ma è sufficiente per fornire rapidamente fiducia nelle richieste pull.

Successivamente, altri membri del team esaminano il codice e approvano le modifiche. La revisione del codice rileva dove sono stati interrotti i test automatizzati ed è particolarmente utile per individuare i problemi di architettura. Le revisioni manuali del codice assicurano che altri tecnici del team abbiano visibilità sulle modifiche e che la qualità del codice rimanga elevata.

Merge

Una volta che la pull request soddisfa tutti i criteri di compilazione e i revisori hanno approvato, il branch di lavoro viene unito al branch di integrazione principale e la pull request viene completata.

Dopo l'unione, vengono eseguiti altri test di accettazione che richiedono più tempo per il completamento. Questi test tradizionali di post-checkin eseguono una convalida più approfondita. Questo processo di test offre un buon equilibrio tra avere test veloci durante la revisione delle richieste pull e avere una copertura di test completa prima del rilascio.

Differenze rispetto a GitHub Flow

GitHub Flow è un flusso di rilascio di sviluppo basato su trunk diffuso per le organizzazioni per implementare un approccio scalabile a Git. Tuttavia, alcune organizzazioni trovano che, man mano che le proprie esigenze aumentano, devono divergere da parti del flusso GitHub.

Ad esempio, una parte spesso trascurata di GitHub Flow è che le richieste pull devono essere distribuite nell'ambiente di produzione per i test prima di poter eseguire il merge nel ramo principale. Questo processo significa che tutte le pull request attendono nella coda di distribuzione per la fusione.

Alcuni team hanno diverse centinaia di sviluppatori che lavorano costantemente in un singolo repository, che possono completare più di 200 richieste pull nel ramo principale al giorno. Se ogni pull request richiede una distribuzione in più data center di Azure in tutto il mondo per i test, gli sviluppatori dedicano tempo ad aspettare il merge dei branch, anziché scrivere software.

I team Microsoft continuano invece a sviluppare nel ramo principale e raggruppare le distribuzioni in versioni temporali, in genere allineate a una cadenza sprint di tre settimane.

Dettagli sull'implementazione

Ecco alcuni dettagli principali dell'implementazione del flusso di rilascio Microsoft:

Strategia del repository Git

Diversi team hanno strategie diverse per la gestione dei repository Git. Alcuni team mantengono la maggior parte del codice in un repository Git. Il codice viene suddiviso in componenti, ognuno nella propria cartella a livello radice. I componenti di grandi dimensioni, in particolare i componenti meno recenti, possono avere più sottocomponenti che dispongono di sottocartelle separate all'interno del componente padre.

Screenshot che mostra una struttura del repository Git.

Repository aggiuntivi

Alcuni team gestiscono anche repository aggiuntivi. Ad esempio, gli agenti e le attività di compilazione e rilascio, l'estensione VS Code e i progetti open source vengono sviluppati in GitHub. Le modifiche di configurazione vengono archiviate in un repository separato. Altri pacchetti da cui dipende il team provengono da altre posizioni e vengono utilizzati tramite NuGet.

Repository mono o multi-repo

Mentre alcuni team scelgono di avere un singolo repository monolitico, il mono-repo, altri prodotti Microsoft utilizzano un approccio multi-repo. Skype, ad esempio, ha centinaia di piccoli repository che si combinano in varie combinazioni per creare molti client, servizi e strumenti diversi. Soprattutto per i team che adottano microservizi, il multi-repo può essere l'approccio corretto. Di solito, i prodotti più vecchi che sono iniziati come monoliti trovano che un approccio mono-repo sia considerata la transizione più semplice verso Git, e l'organizzazione del loro codice riflette questo.

Rami di rilascio

Il flusso di rilascio Microsoft mantiene il ramo principale costruibile in ogni momento. Gli sviluppatori lavorano in rami di argomenti di breve durata che si uniscono a main. Quando un team è pronto per la spedizione, sia alla fine di uno sprint che per un aggiornamento principale, avvia un nuovo ramo di rilascio fuori dal ramo principale. I rami di rilascio non si uniscono mai al ramo principale, quindi potrebbe essere necessario eseguire un cherry-picking delle modifiche importanti.

Il diagramma seguente mostra i rami di breve durata in blu e i rami di rilascio in nero. Un ramo con un commit che richiede il cherry-picking appare in rosso.

Diagramma che mostra la struttura del ramo di rilascio Git.

Criteri e autorizzazioni dei rami

Le politiche dei rami Git aiutano a far rispettare la struttura del ramo di rilascio e a mantenere pulito il ramo principale. Ad esempio, i criteri di ramo possono impedire il push diretto al ramo principale.

Per mantenere ordinata la gerarchia dei rami, i team usano le autorizzazioni per bloccare la creazione di rami a livello radice della gerarchia. Nell'esempio seguente tutti gli utenti possono creare rami in cartelle come utenti/, funzionalità/e team/. Solo i responsabili delle versioni hanno l'autorizzazione per creare rami in versioni/e alcuni strumenti di automazione dispongono dell'autorizzazione per le integrazioni/ cartella.

Screenshot che mostra i rami.

Flusso di lavoro del repository Git

All'interno del repository e della struttura dei rami, gli sviluppatori svolgono il proprio lavoro quotidiano. Gli ambienti di lavoro variano notevolmente in base al team e ai singoli utenti. Alcuni sviluppatori preferiscono la riga di comando, altri come Visual Studio e altri funzionano su piattaforme diverse. Le strutture e i criteri applicati nei repository Microsoft garantiscono una base solida e coerente.

Un flusso di lavoro tipico prevede le attività comuni seguenti:

Creare una nuova funzionalità

La creazione di una nuova funzionalità è il nucleo del lavoro di uno sviluppatore software. Le parti non Git del processo includono l'analisi dei dati di telemetria, la progettazione e una specifica e la scrittura del codice effettivo. Quindi, lo sviluppatore inizia a lavorare con il repository eseguendo la sincronizzazione con il commit più recente in main. Il ramo principale è sempre compilabile, quindi è garantito che sia un buon punto di partenza. Lo sviluppatore verifica un nuovo ramo di funzionalità, apporta modifiche al codice, commit, push al server e avvia una nuova richiesta pull.

Usare i criteri e i controlli dei rami

Al momento della creazione di una richiesta pull, i sistemi automatizzati verificano che il nuovo codice si compili, non causi interruzioni né violi alcun criterio di sicurezza o conformità. Questo processo non impedisce l'esecuzione in parallelo di altri lavori.

I criteri e i controlli dei rami possono richiedere un build riuscito, inclusi i test superati, l'approvazione dai proprietari di qualsiasi codice toccato e diversi controlli esterni per verificare le politiche aziendali prima che una richiesta pull possa essere completata.

Screenshot che mostra i controlli in una richiesta pull.

Integrazione con Microsoft Teams

Molti team configurano l'integrazione con Microsoft Teams, che annuncia la nuova richiesta pull ai compagni di squadra degli sviluppatori. I proprietari di qualsiasi codice toccato vengono aggiunti automaticamente come revisori. I team Microsoft usano spesso revisori facoltativi per il codice che molti utenti toccano, ad esempio la generazione di client REST e i controlli condivisi, per ottenere occhi esperti su tali modifiche.

Screenshot che mostra l'integrazione di Teams.

Screenshot che mostra la notifica di Teams di una pull request.

Eseguire la distribuzione con i flag di funzionalità

Una volta soddisfatti i revisori, i proprietari del codice e l'automazione, lo sviluppatore può completare la richiesta pull. Se si verifica un conflitto di merge, lo sviluppatore riceve istruzioni su come eseguire la sincronizzazione con il conflitto, correggerlo ed eseguire nuovamente il push delle modifiche. L'automazione viene eseguita di nuovo nel codice fisso, ma gli esseri umani non devono disconnettersi di nuovo.

Il ramo viene unito a maine il nuovo codice viene distribuito nello sprint o nella versione principale successiva. Ciò non significa che la nuova funzionalità verrà visualizzata immediatamente. Microsoft separa la distribuzione e l'esposizione delle nuove funzionalità usando i flag di funzionalità.

Anche se la funzionalità richiede un po' di lavoro prima che sia pronta per essere presentata, è sicuro procedere a main quando il prodotto viene compilato e distribuito. Una volta in main, il codice diventa parte di una build ufficiale, in cui viene nuovamente testato, confermato per soddisfare i criteri e firmato digitalmente.

Spostarsi a sinistra per rilevare i problemi in anticipo

Questo flusso di lavoro Git offre diversi vantaggi. Prima di tutto, lavorare a partire da un unico ramo principale elimina praticamente il debito di merge. In secondo luogo, il flusso della richiesta pull fornisce un punto comune per applicare test, revisione del codice e rilevamento degli errori nelle prime fasi della pipeline. Questa strategia di spostamento a sinistra consente di abbreviare il ciclo di feedback agli sviluppatori perché può rilevare errori in minuti, non ore o giorni. Questa strategia offre anche la sicurezza per il refactoring, perché tutte le modifiche vengono testate costantemente.

Attualmente, un prodotto con più di 200 richieste pull potrebbe produrre 300 compilazioni di integrazione continua al giorno, pari a 500+ esecuzioni di test ogni 24 ore. Questo livello di test sarebbe impossibile senza il flusso di lavoro di diramazione e rilascio basato su trunk.

Rilascio in fase di attività cardine sprint

Alla fine di ogni sprint, il team crea un ramo di rilascio dal ramo principale. Ad esempio, alla fine dello sprint 129, il team crea un nuovo ramo di versionereleases/M129. Il team mette quindi il ramo sprint 129 in modalità di produzione.

Dopo il ramo del ramo di rilascio, il ramo principale rimane aperto per consentire agli sviluppatori di unire le modifiche. Queste modifiche verranno distribuite tre settimane dopo nella distribuzione sprint successiva.

Illustrazione del ramo di rilascio allo sprint 129.

Hotfix di rilascio

A volte le modifiche devono essere implementate rapidamente in ambiente di produzione. Microsoft in genere non aggiungerà nuove funzionalità al centro di uno sprint, ma a volte vuole introdurre rapidamente una correzione di bug per sbloccare gli utenti. I problemi possono essere minori, ad esempio errori di digitazione o sufficientemente grandi da causare un problema di disponibilità o un evento imprevisto del sito attivo.

La correzione di questi problemi inizia con il normale flusso di lavoro. Uno sviluppatore crea un ramo da main, ottiene il codice esaminato e completa la richiesta pull per unirla. Il processo inizia sempre apportando la modifica in main primo luogo. In questo modo è possibile creare rapidamente la correzione e convalidarla in locale senza dover passare al ramo di rilascio.

L'esecuzione di questo processo garantisce anche che la modifica venga inserita in main, che è fondamentale. Correggere un bug nel ramo di rilascio senza riportare la modifica in main significherebbe che il bug si ripeterebbe durante la distribuzione successiva, quando lo sprint 130 ramifica da main. È facile dimenticare di aggiornare main durante la confusione e lo stress che possono verificarsi durante un'interruzione. Portare modifiche al main per primo significa avere sempre le modifiche sia nel ramo principale che nel ramo di rilascio.

La funzionalità Git abilita questo flusso di lavoro. Per introdurre immediatamente le modifiche nell'ambiente di produzione, una volta che uno sviluppatore unisce una richiesta pull in main, può usare la pagina della richiesta pull per selezionare le modifiche nel ramo di rilascio. Questo processo crea una nuova pull request destinata al ramo di rilascio, tramite backporting del contenuto appena unito in main.

Illustrazione della selezione di un commit hotfix nel ramo 129.

L'uso della funzionalità cherry-pick (scelta selettiva) apre rapidamente una richiesta pull, fornendo la tracciabilità e l'affidabilità dei criteri dei branch. L'operazione cherry-pick può avvenire sul server, senza dover scaricare il branch di rilascio su un computer locale. Apportare modifiche, correggere i conflitti di merge o apportare modifiche minime a causa delle differenze tra i due rami possono verificarsi nel server. Teams può modificare le modifiche direttamente dall'editor di testo basato su browser o tramite l'estensione dei conflitti di merge delle richieste pull per un'esperienza più avanzata.

Quando una pull request è destinata al ramo di rilascio, il team la rivede nuovamente, valuta le regole dei rami, testa la pull request e la unisce. Dopo l'unione, la correzione viene distribuita nel primo anello di server in pochi minuti. Da qui, il team distribuisce progressivamente la correzione a più account usando gli anelli di distribuzione. Man mano che le modifiche vengono distribuite a più utenti, il team monitora l'esito positivo e verifica che la modifica corregge il bug senza introdurre carenze o rallentamenti. La correzione viene infine distribuita in tutti i data center di Azure.

Passare allo sprint successivo

Durante le tre settimane successive, il team completa l'aggiunta di funzionalità allo sprint 130 e si prepara a distribuire tali modifiche. Creano il nuovo ramo di versione, releases/M130 da maine distribuiscono tale ramo.

A questo punto, ci sono in realtà due rami nell'ambiente di produzione. Con una distribuzione basata su anello per apportare modifiche all'ambiente di produzione in modo sicuro, l'anello veloce ottiene le modifiche dello sprint 130 e i server circolari lenti rimangono nello sprint 129 mentre le nuove modifiche vengono convalidate nell'ambiente di produzione.

La correzione rapida di una modifica durante una distribuzione potrebbe richiedere hotfix di due diversi rilasci, il rilascio sprint 129 e il rilascio sprint 130. Il team porta e distribuisce l'hotfix in entrambi i rami di rilascio. I rami 130 vengono ridistribuiti con l'hotfix agli anelli che sono già stati aggiornati. Il ramo 129 viene ridistribuito con l'hotfix ai ring esterni che non sono ancora stati aggiornati alla versione del prossimo sprint.

Una volta distribuiti tutti gli anelli, il ramo sprint 129 precedente viene abbandonato, perché tutte le modifiche apportate al ramo sprint 129 come hotfix sono state apportate anche in main. Quindi, queste modifiche saranno anche nel ramo releases/M130.

Illustrazione di un ramo di rilascio allo sprint 130.

Riassunto

Il modello di flusso di rilascio è al centro del modo in cui Microsoft sviluppa con DevOps per offrire servizi online. Questo modello utilizza una semplice strategia di diramazione basata su tronco. Ma invece di mantenere gli sviluppatori bloccati in una coda di distribuzione, in attesa di unire le modifiche, il flusso di versione Di Microsoft consente agli sviluppatori di continuare a lavorare.

Questo modello di versione consente anche di distribuire nuove funzionalità nei data center di Azure a cadenza regolare, nonostante le dimensioni delle codebase Microsoft e il numero di sviluppatori che lavorano in tali data center. Il modello consente anche di portare le correzioni rapide nell'ambiente di produzione in modo rapido ed efficiente.