.NET Core su Linux, DevOps al top

Abbiamo sviluppato DevOps come potevamo. Eravamo 8 persone e Vasya era il migliore su Windows. Improvvisamente Vasya se n'è andato e ho dovuto affrontare il compito di avviare un nuovo progetto per la fornitura di sviluppo Windows. Quando ho messo sul tavolo tutto il stack di sviluppo Windows, mi sono reso conto che la situazione era critica...

Così inizia la storia Alexander Sinchinov in DevOpsConf. Quando il principale specialista Windows ha lasciato l'azienda, Alexander si è chiesto cosa fare ora. Passare a Linux, naturalmente! Alexander racconterà come è riuscito a creare un precedente e a trasferire parte dello sviluppo Windows su Linux, utilizzando un progetto realizzato per 100.000 utenti finali.

.NET Core su Linux, DevOps al top

Come è facile e senza sforzo consegnare un progetto in RPM, utilizzando TFS, Puppet, Linux .NET core? Come mantenere il versionamento del database di un progetto se gli sviluppatori sentono per la prima volta parole come Postgres e Flyway, e la scadenza è dopodomani? Come integrare con Docker? Come motivare gli sviluppatori .NET ad abbandonare Windows e i frullati a favore di Puppet e Linux? Come risolvere i conflitti ideologici quando non ci sono né forze né desiderio né risorse per mantenere Windows in produzione? Di questo, oltre a Web Deploy, testing, CI, pratiche di utilizzo di TFS in progetti esistenti, e ovviamente di stampelle rotte e soluzioni funzionanti, parlerà la relazione di Alexander.

Guarda il video

Dunque, Vasya è andato, il compito è mio e gli sviluppatori attendono con le forche impazienti. Quando ho finalmente realizzato che non avrei potuto riportare Vasya, mi sono messo al lavoro. Per prima cosa ho valutato la percentuale di VM Windows nel nostro parco. I risultati non erano a favore di Windows.

.NET Core su Linux, DevOps al top

Poiché stiamo sviluppando attivamente DevOps, ho capito che era necessario cambiare approccio per l'implementazione di una nuova applicazione. La decisione è stata chiara: se possibile, trasferire tutto su Linux. Google mi ha aiutato: a quel tempo .Net era già stato portato su Linux e ho capito che era questa la soluzione!

Perché .NET core in combinazione con Linux?

Ci sono state diverse ragioni. Tra "pagare" e "non pagare" la maggior parte sceglie la seconda opzione, come me. Una licenza per MSDB costa circa 1.000 $, mentre il mantenimento di un parco di macchine virtuali Windows si traduce in centinaia di dollari. Per una grande azienda, sono spese significative. Perciò il risparmio  è la prima ragione. Non la più importante, ma certamente una delle più significative.

Le macchine virtuali Windows richiedono più risorse rispetto ai loro equivalenti Linux - sono pesanti. Considerando le dimensioni di una grande azienda, abbiamo scelto Linux.

Il sistema si integra semplicemente nel CI esistente. Ci consideriamo sviluppatori DevOps all'avanguardia, utilizziamo Bamboo, Jenkins e GitLab CI, pertanto gran parte del nostro lavoro si basa su Linux.

L'ultima ragione è la facilità di supporto. Dovevamo abbattere la soglia di ingresso per i "supporters" – ragazzi che comprendono la parte tecnica, garantiscono la continuità e gestiscono i servizi di secondo livello. Erano già familiari con il stack Linux, quindi per loro è molto più semplice comprendere il nuovo prodotto, supportarlo e assisterlo piuttosto che spendere risorse aggiuntive per capire una funzionalità analoga del software per la piattaforma Windows.

Requisiti

Prima di tutto la praticità di una nuova soluzione per gli sviluppatori. Non tutti si sono mostrati pronti ai cambiamenti, soprattutto dopo aver sentito la parola Linux. Gli sviluppatori vogliono il loro amato Visual Studio, TFS con test automatici sui build e smoothie. Come avviene la consegna in produzione non è importante per loro. Pertanto, abbiamo deciso di non cambiare il processo abituale e mantenere tutto invariato per lo sviluppo su Windows.

Il nuovo progetto deve essere integrato nel CI esistente. I binari erano già presenti e tutto il lavoro doveva essere eseguito tenendo conto dei parametri del sistema di gestione della configurazione, degli standard di consegna adottati e dei sistemi di monitoraggio.

Semplicità di supporto e funzionamento, come condizione per una soglia minima di ingresso per tutti i nuovi partecipanti provenienti dai diversi reparti e dal dipartimento di supporto.

Il termine ultimo era ieri.

Il gruppo di sviluppo Win

Con cosa lavorava quindi il team di Windows?

.NET Core su Linux, DevOps al top

Ora posso dire con certezza che IdentityServer4 è una fantastica alternativa gratuita ad ADFS con funzionalità simili, oppure che Entity Framework Core è un paradiso per gli sviluppatori, dove si può smettere di preoccuparsi di scrivere script SQL e descrivere le query nel database in termini di OOP. Ma allora, nella discussione sul piano d'azione, guardavo a questo stack come se fosse una scrittura cuneiforme sumera, riconoscendo solo PostgreSQL e Git.

A quel tempo utilizzavamo attivamente Puppet come sistema di gestione della configurazione. Nella maggior parte dei nostri progetti applicavamo GitLab CI, Elastic, bilanciavamo i servizi altamente caricati con l'aiuto di HAProxy, tutte le osservazioni venivano fatte usando Zabbix, la combinazione GrafanaPrometheus, Jaeger, e tutto ciò girava su dei macchinari HPESXi in VMware. È conosciuto da tutti – un classico del genere.

.NET Core su Linux, DevOps al top

Vediamo e cerchiamo di capire cosa stava succedendo prima che avessimo intrapreso tutti questi interventi.

Cosa c'era

TFS è un sistema piuttosto potente che non solo distribuisce il codice dallo sviluppatore alla macchina di produzione finale, ma offre anche un set per una integrazione molto flessibile con vari servizi, al fine di garantire CI a livello cross-platform.

.NET Core su Linux, DevOps al top
In passato c'erano solo finestre. TFS utilizzava diversi agenti di Build, sui quali erano raccolti molti progetti. In ogni agente c'erano 3-4 worker per parallelizzare i task e ottimizzare il processo. Poi, secondo i piani di rilascio, TFS consegnava il Build appena sfornato su un server Windows per applicazioni.

A cosa volevamo arrivare

Per la consegna e lo sviluppo utilizziamo TFS, mentre l'applicazione viene eseguita su un server applicativo Linux, e tra di loro c'è una sorta di magia. Questa Magic Box è il fulcro del lavoro futuro. Prima di esaminare i dettagli, mi permetto una digressione per dire un paio di parole sull'applicazione.

Progetto

L'applicazione fornisce funzionalità per gestire carte prepagate.

.NET Core su Linux, DevOps al top

Cliente

C'erano due tipi di utenti. Primo accedeva autenticandosi con un certificato SSL SHA-2. A secondo aveva accesso tramite login e password.

HAProxy

Successivamente, la richiesta del cliente arrivava a HAProxy, che svolgeva i seguenti compiti:

  • autenticazione primaria;
  • terminazione SSL;
  • ottimizzazione delle richieste HTTP;
  • traduzione delle richieste.

Il controllo del certificato del cliente avveniva lungo una catena. Noi siamo authority e possiamo permettercelo, poiché emettiamo i certificati per i clienti del servizio.

Fai attenzione al terzo punto, ci torneremo tra poco.

Backend

Il backend era pianificato per essere realizzato su Linux. Il backend interagisce con il database, carica l'elenco necessario di privilegi e poi, in base ai privilegi dell'utente autenticato, fornisce accesso alla firma di documenti finanziari e alla loro invio per l'esecuzione, oppure alla generazione di un certo report.

Risparmi con HAProxy

Oltre ai due contesti attraverso i quali transitavano ciascuno dei clienti, esisteva anche il contesto identity. IdentityServer4 che consente l'autenticazione, è un'alternativa gratuita e potente per ADFS  Active Directory Federation Services.

La richiesta in identity veniva elaborata in diversi passaggi. Il primo passaggio - un client arrivava nel backend, che scambiava dati con questo server e controllava la presenza di un token per il cliente. Se non lo trovava, la richiesta veniva restituita al contesto da cui proveniva, ma già con il reindirizzamento, e il reindirizzamento andava su identity.

Il secondo passo - la richiesta arrivava alla pagina di autorizzazione in IdentityServer, dove il cliente si registrava, e nel database di IdentityServer compariva quel tanto atteso token.

Il terzo passo - il cliente veniva reindirizzato di nuovo al contesto da cui era venuto.

.NET Core su Linux, DevOps al top

IdentityServer4 ha una caratteristica: la risposta alla richiesta di ritorno viene restituita tramite HTTP. Non importa quanto ci siamo sforzati di configurare il server, quanto ci siamo istruiti sulla documentazione, ma ogni volta ricevevamo la richiesta originale del cliente con un URL arrivato tramite HTTPS, e IdentityServer restituiva lo stesso contesto, ma tramite HTTP. Siamo rimasti scioccati! E abbiamo inoltrato tutto questo attraverso il contesto identity su HAProxy, e negli header abbiamo dovuto modificare il protocollo HTTP in HTTPS.

Qual è il miglioramento e dove abbiamo risparmiato?

Abbiamo risparmiato denaro utilizzando una soluzione gratuita per l'autorizzazione di un gruppo di utenti, risorse, poiché non abbiamo separato IdentityServer4 come nodo distinto in un segmento separato, ma lo abbiamo utilizzato insieme al backend sullo stesso server dove gira il backend dell'applicazione.

Come dovrebbe funzionare

Quindi, come promesso - Magic Box. Comprendiamo già che ci stiamo muovendo in modo garantito verso Linux. Formuliamo le specifiche esigenze che richiedevano una soluzione.

.NET Core su Linux, DevOps al top

Manifesti Puppet. Per distribuire e gestire la configurazione del servizio e dell'applicazione, era necessario scrivere ricette di alta qualità. Un rotolo con una matita mostra eloquentemente quanto velocemente e con qualità sia stato fatto.

Modalità di consegna. Lo standard è RPM. Tutti capiscono che su Linux non si può fare a meno, ma il progetto stesso, dopo la compilazione, consisteva in un insieme di file DLL eseguibili. Ce n'erano circa 150, il progetto è abbastanza pesante. L'unica soluzione armoniosa è stata confezionare questo insieme binario in RPM e già da lì distribuire l'applicazione.

Versioning. Dovevamo rilasciare molto frequentemente e bisognava decidere come formare il nome del pacchetto. Questa è una questione di integrazione con TFS. L'agente di build era su Linux. Quando TFS invia un compito al gestore — worker — sull'agente di build, gli passa anche un insieme di variabili che vanno nell'ambiente del processo del gestore. In queste variabili di ambiente vengono passati il nome della build, il numero di versione e altre variabili. Maggiori dettagli su questo nel capitolo "generazione del pacchetto RPM".

Configurazione TFS consisteva nella configurazione del Pipeline. Prima raccoglievamo tutti i progetti Windows sugli agenti Windows, e ora sta arrivando un agente Linux — l'agente di build, che deve essere incluso nel gruppo di build, arricchito con vari artefatti, specificare che tipo di progetti verranno costruiti su questo agente di build e in qualche modo modificare il Pipeline.

IdentityServer. ADFS non è la nostra strada, sosteniamo l'Open Source.

Esaminiamo i componenti.

Magic Box

È composto da quattro parti.

.NET Core su Linux, DevOps al top

Agente di build Linux. Linux, perché stiamo costruendo per esso — è logico. Questa parte è stata eseguita in tre fasi.

  • Configurare i worker e non uno solo, poiché si prevedeva un lavoro distribuito sul progetto.
  • Installare .NET Core 1.x. Perché proprio 1.x, quando già è disponibile la 2.0 nel repository standard? Perché, quando abbiamo iniziato lo sviluppo, la versione stabile era 1.09, e il progetto è stato deciso di realizzarlo per essa.
  • Git 2.x.

repository RPM. I pacchetti RPM dovevano essere memorizzati da qualche parte. Si prevedeva che avremmo usato lo stesso repository RPM aziendale, accessible da tutti gli host Linux. È così che abbiamo proceduto. Sul server del repository è stato configurato webhook che scaricava il pacchetto RPM richiesto dal luogo specificato. La versione del pacchetto era comunicata al webhook dall'agente di build.

GitLab. Attenzione! GitLab qui non è usato dagli sviluppatori, ma dal reparto operazioni per il controllo delle versioni dell'applicazione, delle versioni dei pacchetti, del monitoraggio dello stato di tutte le macchine Linux, e contiene la ricetta — tutti i manifesti Puppet.

Puppet gestisce tutte le questioni controverse e fornisce esattamente la configurazione che vogliamo, da GitLab.

Iniziamo a immergerci. Come avviene la consegna delle DLL in RPM?

Consegna delle DLL in RPM

Supponiamo di avere una rock star dello sviluppo su .NET. Usa Visual Studio e crea un ramo di rilascio. Poi lo carica su Git, e Git qui è un'entità TFS, cioè è il repository dell'applicazione con cui lavora lo sviluppatore.

.NET Core su Linux, DevOps al top

Dopo di che TFS vede che è arrivato un nuovo commit. Quale applicazione? Nelle impostazioni di TFS c'è un'etichetta che indica quali risorse ha ogni Build-agent. In questo caso, vede che stiamo compilando un progetto .NET Core e sceglie un Build-agent Linux dal pool.

Il Build-agent riceve i sorgenti, scarica i necessari dependencies dal repository .NET, npm, ecc. e dopo la compilazione dell'applicazione e il successivo imballaggio invia un pacchetto RPM nel repository RPM.

Dall'altra parte avviene quanto segue. L'ingegnere del dipartimento operativo si occupa direttamente del rilascio del progetto: cambia le versioni dei pacchetti in Hiera nel repository, dove è conservata la ricetta dell'applicazione, dopodiché Puppet attiva Yum, prende il nuovo pacchetto dal repository e la nuova versione dell'applicazione è pronta per l'uso.

.NET Core su Linux, DevOps al top

A parole tutto è semplice, ma cosa succede dentro il Build-agent?

Imballaggio DLL RPM

I sorgenti del progetto sono stati ricevuti e l'istruzione di compilazione da TFS. Il Build-agent inizia la compilazione del progetto stesso dai sorgenti.Il progetto compilato è accessibile sotto forma di numerosi file DLL, che sono stati imballati in un archivio zip per ridurre il carico sul file system.

L'archivio ZIP viene inviato nella directory di compilazione del pacchetto RPM. Successivamente, uno script Bash inizializza le variabili d'ambiente, trova la versione di Build, la versione del progetto, il percorso della directory di compilazione e avvia RPM-build. Una volta conclusa la compilazione, il pacchetto viene pubblicato in un repository locale, che si trova sul Build-agent.

Dopo di che, dal Build-agent al server nei repository RPM viene inviata una richiesta JSON indicando il nome della versione e del Build. Il Webhook di cui parlavo prima preleva questo pacchetto dal repository locale sul Build-agent e rende una nuova compilazione disponibile per l'installazione.

.NET Core su Linux, DevOps al top

Perché proprio questa schema di consegna del pacchetto nel repository RPM? Perché non è possibile inviare direttamente il pacchetto compilato nel repository? La questione è che questa è una condizione per garantire la sicurezza. Questo scenario limita la possibilità di caricamenti non autorizzati di pacchetti RPM da parte di persone esterne al server, che è accessibile a tutte le macchine Linux.

Versioning del DB

Durante il consiglio di sviluppo, è emerso che ai ragazzi piace di più MS SQL, ma nella maggior parte dei progetti non Windows stavamo già utilizzando PostgreSQL. Poiché abbiamo già deciso di rinunciare a tutto ciò che è a pagamento, abbiamo iniziato a usare PostgreSQL anche qui.

.NET Core su Linux, DevOps al top

In questa parte voglio raccontare come abbiamo realizzato la gestione delle versioni del database e come abbiamo scelto tra Flyway ed Entity Framework Core. Esaminiamo i loro pro e contro.

Contro

Flyway opera solo in un verso, noi non possiamo tornare indietro — questo è un difetto significativo. Con Entity Framework Core possiamo confrontarlo su altri parametri — dal punto di vista della comodità per il sviluppatore. Ricordate che abbiamo posto questo al centro della nostra attenzione, e il criterio principale era non modificare nulla per lo sviluppo su Windows.

Per Flyway avevamo bisogno di qualche tipo di wrapper , in modo che i ragazzi non scrivessero. È molto più vicino per loro operare in termini di OOP. Abbiamo scritto istruzioni per lavorare con gli oggetti del database, è stata formata una query SQL e eseguita. Una nuova versione del database è pronta, è stata testata — tutto va bene, tutto funziona. query SQLEntity Framework Core ha un difetto — con carichi elevati

crea query SQL non ottimali , e il calo delle prestazioni del database può essere significativo. Ma poiché il nostro servizio non è ad alta intensità, non misuriamo il carico in centinaia di RPS, abbiamo accettato questi rischi e abbiamo delegato il problema al nostro futuro.funziona out of the box ed è conveniente per lo sviluppo

Pro

Entity Framework Core , mentre Flywaysi integra facilmente nel CI esistente . Ma noi stiamo cercando di rendere la vita più facile per gli sviluppatori :)La procedura di applicazione

Puppet vede che arriva una modifica della versione dei pacchetti, tra cui quello che si occupa della migrazione. Per prima cosa installa il pacchetto che contiene gli script di migrazione e le funzionalità collegate al database. Dopo di ciò l'applicazione che interagisce con il database viene riavviata. Poi viene effettuata l'installazione degli altri componenti. L'ordine di installazione dei pacchetti e di avvio delle applicazioni è descritto nel manifesto Puppet.

Le applicazioni utilizzano dati sensibili, come token, password del database; tutto questo viene tirato dentro la configurazione dal Puppet master, dove sono conservati in forma crittografata.

Problemi TFS

Dopo che ci siamo definiti e abbiamo capito che tutto funziona davvero, ho deciso di controllare cosa stesse succedendo con le build in TFS in generale per il team di sviluppo Win su altri progetti — se riusciamo a costruire/rilasciare rapidamente o meno, e ho scoperto problemi significativi con la velocità.

Uno dei principali progetti ci mette 12-15 minuti per compilarsi — è troppo, non può andare avanti così. Un'analisi rapida ha mostrato un grave calo delle prestazioni I/O, e questo su array.

Analizzando a livello di componente, ho identificato tre focolaio. Il primo —

«Kaspersky antivirus» «Antivirus Kaspersky», che scansiona le sorgenti su tutti gli agenti di compilazione Windows. Secondo - Windows Indexer. Non è stato disabilitato e tutto è stato indicizzato in tempo reale sugli agenti di compilazione durante il processo di deployment.

Terzo - Npm install. Si è rivelato che in gran parte dei Pipeline abbiamo utilizzato proprio questo scenario. Qual è il problema? La procedura Npm install si avvia durante la formazione dell'albero delle dipendenze in package-lock.json, dove vengono registrate le versioni dei pacchetti che saranno utilizzate per la compilazione del progetto. Il difetto è che Npm install ogni volta scarica versioni aggiornate dei pacchetti da Internet, e questo richiede molto tempo nel caso di un progetto esteso.

Gli sviluppatori a volte sperimentano sulla macchina locale per verificare il funzionamento di una parte specifica o del progetto nel suo insieme. A volte accadeva che localmente tutto funzionasse bene, ma durante la compilazione e il deployment - nulla funzionava. Iniziamo a capire qual è il problema - ah, versioni diverse di pacchetti con dipendenze.

Soluzione

  • Sorgenti escluse AV.
  • Disabilitazione dell'indicizzazione.
  • Passaggio a npm ci.

I vantaggi di npm ci sono che noi costruiamo l'albero delle dipendenze una sola volta, e otteniamo la possibilità di fornire allo sviluppatore un elenco aggiornato di pacchetti, con cui può sperimentare quanto vuole in locale. Questo fa risparmiare tempo agli sviluppatori che scrivono codice.

Configurazione

Adesso parliamo un po' della configurazione del repository. Storicamente utilizziamo Nexus per la gestione dei repository, compreso Internal REPO. In questo repository interno vengono forniti tutti i componenti che utilizziamo per scopi interni, ad esempio monitoraggi personalizzati.

.NET Core su Linux, DevOps al top

Utilizziamo anche NuGet, poiché gestisce meglio la cache rispetto ad altri gestori di pacchetti.

Risultato

Dopo aver ottimizzato gli agenti di compilazione, il tempo medio di compilazione si è ridotto da 12 minuti a 7.

Se calcolassimo tutte le macchine che avremmo potuto utilizzare per Windows, ma che abbiamo trasferito su Linux in questo progetto, abbiamo risparmiato circa $10.000. E questo solo per le licenze, e se consideriamo i costi di mantenimento - di più.

Piani

Per il prossimo trimestre abbiamo pianificato il lavoro sull'ottimizzazione della consegna del codice.

Passaggio a un'immagine Docker precompilata. TFS è una cosa fantastica con molti plugin che consentono di integrare nel Pipeline, anche la compilazione tramite trigger, ad esempio, di un'immagine Docker. Questo trigger vogliamo realizzarlo per quello specifico package-lock.json. Se in qualche modo cambia la composizione dei componenti utilizzati per assemblare il progetto, viene creato un nuovo Docker image. Questo viene poi utilizzato per il dispiegamento del container con l'applicazione assemblata. Al momento non è così, ma pianifichiamo di passare a un’architettura a microservizi in Kubernetes, che si sta sviluppando attivamente nella nostra azienda e da tempo supporta soluzioni di produzione.

Riepilogo

Invito tutti a gettare via Windows, ma non perché non sappia usarlo. La ragione è che gran parte delle soluzioni Opensource sono stack Linux. Risparmierete notevolmente su risorse. A mio avviso, il futuro appartiene alle soluzioni Open Source su Linux con una comunità forte.

Profilo del relatore Aleksandr Sinchinov su GitHub.

DevOps Conf è una conferenza sull'integrazione dei processi di sviluppo, testing e operatività per professionisti da professionisti. Proprio per questo il progetto di cui ha parlato Aleksandr è stato realizzato e funziona, e nel giorno della sua presentazione sono state effettuate due release di successo. Al DevOps Conf a RIT++ il 27 e 28 maggio ci saranno ancora più casi simili da parte dei praticanti. È ancora possibile salire sull'ultimo treno e inviare una relazione o senza fretta prenotare un biglietto. Ci vediamo a Skolkovo!

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster