.NET Core su Linux, DevOps a cavallo

Abbiamo sviluppato DevOps nel miglior modo possibile. Eravamo in 8 e Vasya era la più bella di Windows. All'improvviso Vasya se ne andò e io avevo il compito di lanciare un nuovo progetto fornito dallo sviluppo di Windows. Quando ho messo sul tavolo l'intero stack di sviluppo di Windows, mi sono reso conto che la situazione era una seccatura...

Così inizia la storia Alessandra Sinchinova su DevOpsConf. Quando il principale specialista di Windows lasciò l'azienda, Alexander si chiese cosa fare adesso. Passa a Linux, ovviamente! Alexander ti racconterà come è riuscito a creare un precedente e trasferire parte dello sviluppo di Windows su Linux usando l'esempio di un progetto completato per 100 utenti finali.

.NET Core su Linux, DevOps a cavallo

Come consegnare facilmente e senza sforzo un progetto a RPM utilizzando TFS, Puppet, Linux .NET core? Come supportare il controllo delle versioni di un database di progetto se il team di sviluppo sente per la prima volta le parole Postgres e Flyway e la scadenza è dopodomani? Come integrarsi con Docker? Come motivare gli sviluppatori .NET ad abbandonare Windows e i frullati in favore di Puppet e Linux? Come risolvere i conflitti ideologici se non c'è né la forza, né il desiderio, né le risorse per mantenere Windows in produzione? A proposito di questo, così come di Web Deploy, test, CI, sulle pratiche di utilizzo di TFS nei progetti esistenti e, ovviamente, sulle stampelle rotte e sulle soluzioni funzionanti, nella trascrizione del rapporto di Alexander.


Quindi, Vasya se n'è andato, il compito spetta a me, gli sviluppatori stanno aspettando con impazienza con i forconi. Quando finalmente ho capito che Vasya non poteva essere restituito, mi sono messo al lavoro. Per cominciare, ho valutato la percentuale di VM Win nel nostro parco macchine. Il punteggio non era a favore di Windows.

.NET Core su Linux, DevOps a cavallo

Poiché stiamo sviluppando attivamente DevOps, mi sono reso conto che è necessario cambiare qualcosa nell'approccio alla fornitura di una nuova applicazione. C'era solo una soluzione: se possibile, trasferire tutto su Linux. Google mi ha aiutato: a quel tempo .Net era già stato portato su Linux e ho capito che questa era la soluzione!

Perché .NET core insieme a Linux?

C'erano diverse ragioni per questo. Tra “pagare soldi” e “non pagare”, la maggioranza sceglierà la seconda, come me. Una licenza per MSDB costa circa 1 dollari; il mantenimento di un parco di macchine virtuali Windows costa centinaia di dollari. Per una grande azienda questa è una grande spesa. Ecco perché risparmi - primo motivo. Non il più importante, ma uno di quelli significativi.

Le macchine virtuali Windows occupano più risorse rispetto ai loro fratelli Linux - sono pesanti. Date le dimensioni della grande azienda, abbiamo scelto Linux.

Il sistema è semplicemente integrato nel CI esistente. Ci consideriamo DevOps progressisti, utilizziamo Bamboo, Jenkins e GitLab CI, quindi la maggior parte del nostro lavoro viene eseguito su Linux.

L'ultimo motivo è comodo accompagnamento. Avevamo bisogno di abbassare la barriera all'ingresso per gli "accompagnatori", i ragazzi che comprendono la parte tecnica, garantiscono un servizio ininterrotto e mantengono i servizi dalla seconda linea. Avevano già familiarità con lo stack Linux, quindi è molto più facile per loro comprendere, supportare e mantenere un nuovo prodotto piuttosto che spendere risorse aggiuntive per comprendere le stesse funzionalità del software per la piattaforma Windows.

Requisiti

Innanzitutto - comodità della nuova soluzione per gli sviluppatori. Non tutti erano pronti al cambiamento, soprattutto dopo che fu pronunciata la parola Linux. Gli sviluppatori desiderano il loro Visual Studio preferito, TFS con test automatici per assiemi e frullati. Per loro non è importante il modo in cui avviene la consegna alla produzione. Pertanto, abbiamo deciso di non modificare il processo abituale e di lasciare tutto invariato per lo sviluppo di Windows.

Serve un nuovo progetto integrare nell'IC esistente. I binari c'erano già e tutto il lavoro doveva essere svolto tenendo conto dei parametri del sistema di gestione della configurazione, degli standard di consegna accettati e dei sistemi di monitoraggio.

Facilità di supporto e funzionamento, come condizione per la soglia minima di ingresso per tutti i nuovi partecipanti provenienti da diverse divisioni e dal dipartimento di supporto.

Scadenza - ieri.

Vinci il gruppo di sviluppo

Con cosa stava lavorando il team di Windows allora?

.NET Core su Linux, DevOps a cavallo

Ora posso dirlo con sicurezza IdentityServer4 è una fantastica alternativa gratuita ad ADFS con funzionalità simili o cosa Entità Framework Core - un paradiso per uno sviluppatore, dove non devi preoccuparti di scrivere script SQL, ma descrivere le query nel database in termini OOP. Ma poi, durante la discussione del piano d'azione, ho guardato questo stack come se fosse cuneiforme sumero, riconoscendo solo PostgreSQL e Git.

A quel tempo stavamo utilizzando attivamente Fantoccio come sistema di gestione della configurazione. Nella maggior parte dei nostri progetti abbiamo utilizzato CI GitLab, Elastico, servizi bilanciati ad alto carico utilizzando HAProxy monitorato tutto con Zabbix, legamenti graminacee и Prometeo, Jaeger, e tutto questo girava su pezzi di ferro HPESXi su VMware. Lo sanno tutti: un classico del genere.

.NET Core su Linux, DevOps a cavallo

Guardiamo e proviamo a capire cosa è successo prima che iniziassimo tutti questi interventi.

Quello che era

TFS è un sistema abbastanza potente che non solo fornisce il codice dallo sviluppatore alla macchina di produzione finale, ma dispone anche di un set per l'integrazione molto flessibile con vari servizi, per fornire CI a livello multipiattaforma.

.NET Core su Linux, DevOps a cavallo
In precedenza, queste erano finestre solide. TFS utilizzava diversi agenti di compilazione, utilizzati per assemblare molti progetti. Ogni agente ha 3-4 lavoratori per parallelizzare le attività e ottimizzare il processo. Quindi, secondo i piani di rilascio, TFS ha consegnato la build appena sfornata al server delle applicazioni Windows.

Cosa volevamo ottenere?

Usiamo TFS per la consegna e lo sviluppo ed eseguiamo l'applicazione su un server di applicazioni Linux e c'è una sorta di magia tra loro. Questo scatola magica e c'è il sale del lavoro da fare. Prima di smontarlo, farò un passo da parte e dirò alcune parole sull'applicazione.

Progetto

L'applicazione fornisce funzionalità per la gestione delle carte prepagate.

.NET Core su Linux, DevOps a cavallo

.

C'erano due tipi di utenti. Prima ottenuto l'accesso effettuando l'accesso utilizzando un certificato SSL SHA-2. U secondo c'era accesso utilizzando un login e una password.

HAProxy

Quindi la richiesta del client è andata a HAProxy, che ha risolto i seguenti problemi:

  • autorizzazione primaria;
  • Terminazione SSL;
  • ottimizzazione delle richieste HTTP;
  • richieste di trasmissione.

Il certificato client è stato verificato lungo la catena. Noi - autorità e possiamo permettercelo, poiché noi stessi rilasciamo certificati per servire i clienti.

Presta attenzione al terzo punto, ci torneremo un po' più tardi.

BACKEND

Avevano pianificato di realizzare il backend su Linux. Il backend interagisce con il database, carica l'elenco necessario di privilegi e quindi, a seconda dei privilegi dell'utente autorizzato, fornisce l'accesso per firmare documenti finanziari e inviarli per l'esecuzione o generare qualche tipo di report.

Risparmio con HAProxy

Oltre ai due contesti in cui ogni cliente navigava, c'era anche un contesto di identità. IdentityServer4 ti consente solo di accedere, questo è un analogo gratuito e potente per ADFS - Servizi federativi di Active Directory.

La richiesta di identità è stata elaborata in più passaggi. Primo passo - cliente è entrato nel backend, che comunicava con questo server e verificava la presenza di un token per il client. Se non veniva trovata, la richiesta veniva restituita al contesto da cui proveniva, ma con un reindirizzamento, e con il reindirizzamento andava all'identità.

Secondo passo: la richiesta è stata ricevuta alla pagina di autorizzazione in IdentityServer, dove il client si è registrato e il token tanto atteso è apparso nel database IdentityServer.

Terzo passo - il client è stato reindirizzato al contesto da cui proviene.

.NET Core su Linux, DevOps a cavallo

IdentityServer4 ha una funzionalità: restituisce la risposta alla richiesta di reso tramite HTTP. Non importa quanto abbiamo faticato con la configurazione del server, non importa quanto ci siamo illuminati con la documentazione, ogni volta che ricevevamo una richiesta iniziale del client con un URL arrivato tramite HTTPS e IdentityServer restituiva lo stesso contesto, ma con HTTP. Siamo rimasti scioccati! E abbiamo trasferito tutto questo attraverso il contesto dell'identità su HAProxy e nelle intestazioni abbiamo dovuto modificare il protocollo HTTP in HTTPS.

Qual è il miglioramento e dove hai risparmiato?

Abbiamo risparmiato denaro utilizzando una soluzione gratuita per autorizzare un gruppo di utenti, risorse, poiché non abbiamo posizionato IdentityServer4 come nodo separato in un segmento separato, ma lo abbiamo utilizzato insieme al backend sullo stesso server su cui viene eseguito il backend dell'applicazione .

Come dovrebbe funzionare

Quindi, come avevo promesso, Magic Box. Comprendiamo già che il passaggio a Linux è garantito. Formuliamo compiti specifici che richiedono soluzioni.

.NET Core su Linux, DevOps a cavallo

Il burattino si manifesta. Per fornire e gestire la configurazione di servizi e applicazioni, è stato necessario scrivere ricette interessanti. Un rotolo di matita mostra in modo eloquente quanto sia stato fatto in modo rapido ed efficiente.

Metodo di consegna. Lo standard è RPM. Tutti capiscono che in Linux non puoi farne a meno, ma il progetto stesso, dopo l'assemblaggio, era un insieme di file DLL eseguibili. Erano circa 150, il progetto era piuttosto difficile. L'unica soluzione armoniosa è impacchettare questo binario in RPM e distribuire l'applicazione da esso.

Controllo delle versioni. Dovevamo rilasciare molto spesso e dovevamo decidere come formare il nome del pacchetto. Questa è una questione del livello di integrazione con TFS. Avevamo un agente di compilazione su Linux. Quando TFS invia un'attività a un gestore, ovvero al lavoratore, all'agente Build, gli passa anche un insieme di variabili che finiscono nell'ambiente del processo del gestore. Queste variabili di ambiente contengono il nome della build, il nome della versione e altre variabili. Maggiori informazioni a riguardo nella sezione "Creazione di un pacchetto RPM".

Configurazione di TFS si è trattato di impostare Pipeline. In precedenza, raccoglievamo tutti i progetti Windows sugli agenti Windows, ma ora appare un agente Linux: un agente di build, che deve essere incluso nel gruppo di build, arricchito con alcuni artefatti e indicato quale tipo di progetti verranno creati su questo agente di build e modificare in qualche modo il file Pipeline.

IdentityServer. ADFS non è la nostra strada, stiamo optando per l'Open Source.

Esaminiamo i componenti.

scatola magica

È composto da quattro parti.

.NET Core su Linux, DevOps a cavallo

Agente di compilazione Linux. Linux, perché lo costruiamo, è logico. Questa parte è stata eseguita in tre passaggi.

  • Configura i lavoratori e non solo, poiché era previsto un lavoro distribuito sul progetto.
  • Installare .NET Core 1.x. Perché 1.x quando 2.0 è già disponibile nel repository standard? Perché quando abbiamo iniziato lo sviluppo, la versione stabile era la 1.09 e si è deciso di realizzare il progetto basandosi su di essa.
  • Git 2.x.

Repository RPM. I pacchetti RPM dovevano essere archiviati da qualche parte. Si presumeva che avremmo utilizzato lo stesso repository RPM aziendale disponibile per tutti gli host Linux. Questo è quello che hanno fatto. Il server di archivio è configurato webhook che ha scaricato il pacchetto RPM richiesto dalla posizione specificata. La versione del pacchetto è stata segnalata al webhook dall'agente di compilazione.

GitLab. Attenzione! GitLab qui non viene utilizzato dagli sviluppatori, ma dal reparto operativo per controllare le versioni dell'applicazione, le versioni dei pacchetti, monitorare lo stato di tutte le macchine Linux e archivia la ricetta: tutti i manifest di Puppet.

Fantoccio — risolve tutte le questioni controverse e fornisce esattamente la configurazione che desideriamo da Gitlab.

Iniziamo ad immergerci. Come funziona la consegna delle DLL a RPM?

Consegna DDL a RPM

Diciamo che abbiamo una rock star dello sviluppo .NET. Utilizza Visual Studio e crea un ramo di rilascio. Successivamente, lo carica su Git e Git qui è un'entità TFS, ovvero è il repository dell'applicazione con cui lavora lo sviluppatore.

.NET Core su Linux, DevOps a cavallo

Dopodiché TFS vede che è arrivato un nuovo commit. Quale applicazione? Nelle impostazioni TFS è presente un'etichetta che indica quali risorse ha un particolare agente di build. In questo caso, vede che stiamo creando un progetto .NET Core e seleziona un agente di compilazione Linux dal pool.

L'agente Build riceve i sorgenti e scarica il necessario dipendenze dal repository .NET, npm, ecc. e dopo aver creato l'applicazione stessa e il successivo confezionamento, invia il pacchetto RPM al repository RPM.

D'altra parte accade quanto segue. L'ingegnere del reparto operativo è direttamente coinvolto nell'implementazione del progetto: modifica le versioni dei pacchetti hier nel repository in cui è archiviata la ricetta dell'applicazione, dopodiché viene attivato Puppet yum, recupera il nuovo pacchetto dal repository e la nuova versione dell'applicazione è pronta per l'uso.

.NET Core su Linux, DevOps a cavallo

Tutto è semplice a parole, ma cosa succede all'interno dell'agente Build stesso?

RPM DLL di confezionamento

Ricevute origini del progetto e attività di creazione da TFS. Costruisci agente inizia a costruire il progetto stesso dai sorgenti. Il progetto assemblato è disponibile come set File DLL, che sono confezionati in un archivio zip per ridurre il carico sul file system.

L'archivio ZIP viene buttato via nella directory di creazione del pacchetto RPM. Successivamente, lo script Bash inizializza le variabili di ambiente, trova la versione Build, la versione del progetto, il percorso della directory build ed esegue RPM-build. Una volta completata la compilazione, il pacchetto viene pubblicato su deposito locale, che si trova nell'agente di compilazione.

Successivamente, dall'agente di compilazione al server nel repository RPM La richiesta JSON viene inviata indicando il nome della versione e build. Webhook, di cui ho parlato in precedenza, scarica proprio questo pacchetto dal repository locale sull'agente Build e rende il nuovo assembly disponibile per l'installazione.

.NET Core su Linux, DevOps a cavallo

Perché questo particolare schema di consegna dei pacchetti al repository RPM? Perché non posso inviare immediatamente il pacchetto assemblato al repository? Il fatto è che questa è una condizione per garantire la sicurezza. Questo scenario limita la possibilità che persone non autorizzate carichino pacchetti RPM su un server accessibile a tutte le macchine Linux.

Versionamento del database

Dopo un consulto con il team di sviluppo, si è scoperto che i ragazzi erano più vicini a MS SQL, ma nella maggior parte dei progetti non Windows utilizzavamo già PostgreSQL con tutte le loro forze. Dato che avevamo già deciso di abbandonare tutto a pagamento, anche qui abbiamo iniziato a utilizzare PostgreSQL.

.NET Core su Linux, DevOps a cavallo

In questa parte voglio raccontarti come abbiamo versionato il database e come abbiamo scelto tra Flyway ed Entity Framework Core. Diamo un'occhiata ai loro pro e contro.

Contro

La Flyway va solo in una direzione, noi non possiamo tornare indietro - questo è uno svantaggio significativo. Puoi confrontarlo con Entity Framework Core in altri modi, in termini di comodità per gli sviluppatori. Ricordi che lo abbiamo messo in primo piano e il criterio principale era non cambiare nulla per lo sviluppo di Windows.

Per Flyway noi era necessaria una sorta di involucroin modo che i ragazzi non scrivano query SQL. Sono molto più vicini a operare in termini OOP. Abbiamo scritto istruzioni per lavorare con gli oggetti del database, generato una query SQL e l'abbiamo eseguita. La nuova versione del database è pronta, testata: va tutto bene, tutto funziona.

Entity Framework Core ha un aspetto negativo: sotto carichi pesanti crea query SQL non ottimalie il prelievo nel database può essere significativo. Ma poiché non abbiamo un servizio ad alto carico, non calcoliamo il carico in centinaia di RPS, abbiamo accettato questi rischi e delegato il problema a noi futuri.

Pro

Entità Framework Core funziona fuori dagli schemi ed è facile da svilupparee Flyway Si integra facilmente nell'IC esistente. Ma lo rendiamo conveniente per gli sviluppatori :)

Procedura di roll-up

Puppet vede che è in arrivo un cambiamento nella versione del pacchetto, incluso quello responsabile della migrazione. Innanzitutto installa un pacchetto che contiene script di migrazione e funzionalità relative al database. Successivamente, l'applicazione che funziona con il database viene riavviata. Segue l'installazione dei componenti rimanenti. L'ordine in cui i pacchetti vengono installati e le applicazioni vengono avviate è descritto nel manifest Puppet.

Le applicazioni utilizzano dati sensibili, come token, password del database, tutto questo viene inserito nella configurazione da Puppet master, dove vengono archiviati in forma crittografata.

Problemi di TFS

Dopo aver deciso e realizzato che tutto funzionava davvero per noi, ho deciso di esaminare cosa stava succedendo con gli assemblaggi in TFS nel suo insieme per il dipartimento di sviluppo di Win su altri progetti, sia che stessimo costruendo/rilasciando rapidamente o meno, e scoperto problemi significativi con la velocità.

Uno dei progetti principali richiede 12-15 minuti per essere assemblato: è molto tempo, non puoi vivere così. Una rapida analisi ha mostrato un terribile calo di I/O, e questo riguardava gli array.

Dopo averlo analizzato componente per componente, ho identificato tre punti focali. Primo - "Antivirus Kaspersky", che analizza le origini su tutti gli agenti Windows Build. Secondo - Windows Indicizzatore. Non è stato disabilitato e tutto è stato indicizzato in tempo reale sugli agenti di build durante il processo di distribuzione.

Terzo - Installazione NPM. Si è scoperto che nella maggior parte delle pipeline abbiamo utilizzato esattamente questo scenario. Perché è cattivo? La procedura di installazione di Npm viene eseguita quando viene formato l'albero delle dipendenze Pacchetto-lock.json, dove vengono registrate le versioni dei pacchetti che verranno utilizzate per compilare il progetto. Lo svantaggio è che Npm install scarica ogni volta le versioni più recenti dei pacchetti da Internet e ciò richiede molto tempo nel caso di un progetto di grandi dimensioni.

Gli sviluppatori a volte sperimentano su un computer locale per testare il funzionamento di una parte particolare o dell'intero progetto. A volte si è scoperto che tutto andava bene a livello locale, ma lo hanno assemblato, steso e niente ha funzionato. Iniziamo a capire qual è il problema: sì, diverse versioni di pacchetti con dipendenze.

Soluzione

  • Fonti nelle eccezioni AV.
  • Disabilita indicizzazione.
  • Vai a npm ci.

I vantaggi di npm ci sono che we Raccogliamo l'albero delle dipendenze una voltae abbiamo l'opportunità di fornire allo sviluppatore elenco corrente dei pacchetti, con il quale può sperimentare localmente quanto vuole. Questo risparmia tempo sviluppatori che scrivono codice.

Configurazione

Ora qualcosa sulla configurazione del repository. Storicamente usiamo Nexus per la gestione dei repository, inclusi REPO interno. Questo repository interno contiene tutti i componenti che utilizziamo per scopi interni, ad esempio il monitoraggio autoprodotto.

.NET Core su Linux, DevOps a cavallo

Usiamo anche NuGet, poiché ha una memorizzazione nella cache migliore rispetto ad altri gestori di pacchetti.

risultato

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

Se contiamo tutte le macchine che avremmo potuto usare per Windows, ma che in questo progetto siamo passati a Linux, abbiamo risparmiato circa 10 dollari, solo sulle licenze, e anche di più se consideriamo il contenuto.

Piani

Per il prossimo trimestre, abbiamo pianificato di lavorare sull'ottimizzazione della consegna del codice.

Passaggio a un'immagine Docker precompilata. TFS è una cosa interessante con molti plugin che ti consentono di integrarti in Pipeline, incluso l'assemblaggio basato su trigger, ad esempio, di un'immagine Docker. Vogliamo creare questo trigger per lo stesso Pacchetto-lock.json. Se la composizione dei componenti utilizzati per costruire il progetto cambia in qualche modo, costruiamo una nuova immagine Docker. Successivamente viene utilizzato per distribuire il contenitore con l'applicazione assemblata. Adesso non è così, ma stiamo progettando di passare a un'architettura di microservizi in Kubernetes, che si sta sviluppando attivamente nella nostra azienda e che serve soluzioni di produzione da molto tempo.

Riassunto

Incoraggio tutti a buttare via Windows, ma non è perché non so come cucinarlo. Il motivo è che la maggior parte delle soluzioni Opensource lo sono Pila Linux. stai bene risparmiare sulle risorse. Secondo me il futuro appartiene alle soluzioni Open Source su Linux con una potente comunità.

Profilo del relatore di Alexander Sinchinov su GitHub.

Conf. DevOps è una conferenza sull'integrazione dei processi di sviluppo, test e funzionamento per professionisti da parte di professionisti. Ecco perché il progetto di cui ha parlato Alexander? implementato e funzionante, e il giorno dello spettacolo ci sono stati due rilasci di successo. SU Conferenza DevOps al RIT++ Il 27 e 28 maggio ci saranno ancora più casi simili da parte dei praticanti. Puoi ancora saltare nell'ultima carrozza e Invia un rapporto oppure prenditi il ​​tuo tempo prenotare biglietto. Ci vediamo a Skolkovo!

Fonte: habr.com

Aggiungi un commento