Come Dark distribuisce il codice in 50 ms

Come Dark distribuisce il codice in 50 ms

Più veloce è il processo di sviluppo, più rapidamente cresce un'azienda tecnologica.

Purtroppo, le applicazioni moderne lavorano contro di noi: i nostri sistemi devono aggiornarsi in tempo reale senza disturbare nessuno e senza causare interruzioni. Il deployment in tali sistemi diventa una sfida complessa e richiede pipeline di continuous delivery sofisticate anche in piccoli team.

Queste pipeline hanno generalmente un'applicazione ristretta, funzionano lentamente e non sono molto affidabili. Gli sviluppatori devono prima crearle manualmente e poi gestirle, e le aziende spesso assumono interi team DevOps per questo.

La velocità di queste pipeline determina la rapidità dello sviluppo. Nei migliori team, il deployment richiede 5–10 minuti, ma di solito tutto impiega molto più tempo, e un singolo deployment può richiedere diverse ore.

In Dark ci vogliono 50 ms. Cinquanta. Millisecondi. Dark — è una soluzione integrata con un linguaggio di programmazione, un editor e un'infrastruttura., creato appositamente per la fornitura continua, e tutti gli aspetti di Dark, compreso il linguaggio stesso, sono costruiti per un deployment sicuro e immediato.

Perché i pipeline di delivery continua sono così lenti?

Immaginiamo di avere un'applicazione web in Python e di aver già creato una fantastica e moderna pipeline di delivery continua. Per uno sviluppatore che è impegnato in questo progetto ogni giorno, il deployment di una piccola modifica apparirà più o meno così:

Apportare modifiche

  • Creazione di un nuovo branch in git
  • Apportare modifiche dietro un switch di funzionalità
  • Test modulare per verificare le modifiche con e senza lo switch di funzionalità

Pull request

  • Commit delle modifiche
  • Invio delle modifiche al repository remoto su github
  • Pull request
  • La build CI viene eseguita automaticamente in background
  • Revisione del codice
  • Altre revisioni se necessario
  • Unione delle modifiche con il master di git.

CI viene eseguita sul master

  • Installazione delle dipendenze frontend tramite npm
  • Build e ottimizzazione delle risorse HTML+CSS+JS
  • Esecuzione di test modulari e funzionali nel frontend
  • Installazione delle dipendenze Python da PyPI
  • Esecuzione di test modulari e funzionali nel backend
  • Test di integrazione su entrambe le estremità
  • Invio delle risorse front-end nel CDN
  • Creazione del container per il programma Python
  • Invio del container nel registro
  • Aggiornamento del manifesto di Kubernetes

Sostituzione del codice obsoleto con uno nuovo

  • Kubernetes avvia più istanze del nuovo container
  • Kubernetes attende che le istanze siano operative
  • Kubernetes aggiunge le istanze al bilanciatore di carico HTTP
  • Kubernetes attende che le vecchie istanze smettano di essere utilizzate
  • Kubernetes ferma le vecchie istanze
  • Kubernetes ripete queste operazioni fino a quando le nuove istanze non sostituiscono tutte le vecchie

Attivazione del nuovo flag di funzionalità

  • Il nuovo codice è attivato solo per sé stesso per assicurarsi che tutto funzioni correttamente
  • Il nuovo codice è attivato per il 10% degli utenti, vengono monitorati le metriche operative e di business
  • Il nuovo codice è attivato per il 50% degli utenti, vengono monitorati le metriche operative e di business
  • Il nuovo codice è attivato per il 100% degli utenti, vengono monitorati le metriche operative e di business
  • Infine, ripeti l'intera procedura per rimuovere il vecchio codice e il flag

Il processo dipende dagli strumenti, dal linguaggio e dall'uso di architetture orientate ai servizi, ma in generale si presenta così. Non ho menzionato i deployment con migrazione di database, perché richiedono una pianificazione accurata, ma di seguito spiegherò come Dark gestisce questa situazione.

Ci sono molti componenti, e molti di essi possono facilmente rallentare, guastarsi, causare competizione temporanea o far crashare il sistema in produzione.

E poiché queste pipeline sono quasi sempre create per casi specifici, non è facile fare affidamento su di esse. Molti hanno giorni in cui il codice non può essere distribuito, a causa di problemi nel Dockerfile, di un guasto in uno dei tanti servizi o perché il tecnico necessario è in ferie.

Peggio ancora, molti di questi passaggi non fanno affatto nulla di utile. Erano necessari in passato, quando distribuivamo il codice direttamente agli utenti, ma ora abbiamo interruttori per il nuovo codice, e questi processi si sono separati. Di conseguenza, il passaggio in cui viene distribuito il codice (il vecchio sostituito con il nuovo) è diventato solo un ulteriore rischio.

Certo, è un pipeline molto ben concepito. Il team che l'ha creato ha investito tempo e risorse per un rapido deploy. Di solito, i pipeline di distribuzione sono molto più lenti e inaffidabili.

Implementazione della consegna continua in Dark

La consegna continua è così importante per Dark che sin dall'inizio ci siamo posti l'obiettivo di meno di un secondo. Abbiamo esaminato ogni passaggio della pipeline per rimuovere il superfluo e rifinire ciò che rimane. Ecco come abbiamo eliminato i passaggi.

Jessie Frazelle (Jessie Frazelle) ha coniato la parola nuovo deployless (senza deploy) durante la conferenza Future of Software Development a Reykjavik

Abbiamo immediatamente deciso che Dark sarebbe stato basato sul concetto di "deployless" (grazie a Jessie Frazelle per il neologismo). Deployless significa che qualsiasi codice viene immediatamente distribuito e pronto per l'uso in produzione. Certo, non bypasseremo codice difettoso o incompleto (i principi di sicurezza li descriverò più avanti).

Durante la dimostrazione di Dark, ci hanno spesso chiesto come siamo riusciti a velocizzare così tanto il processo di deployment. È una domanda strana. Le persone potrebbero pensare che abbiamo inventato qualche supertecnologia che confronta il codice, lo compila, lo incapsula in un contenitore, avvia una macchina virtuale, avvia il contenitore a freddo, e così via, il tutto in 50 ms. Difficilmente è possibile. Ma abbiamo creato un motore di deployment speciale, che non ha bisogno di tutto questo.

Dark esegue gli interpreti nel cloud. Supponiamo che tu stia scrivendo codice in una funzione o in un gestore HTTP o per eventi. Inviamo il diff a un albero sintattico astratto (l'implementazione del codice che il nostro editor e i nostri server utilizzano internamente) ai nostri server, e poi eseguiamo questo codice quando arrivano le richieste. Quindi, il deployment appare semplicemente come un'annotazione modesta nel database — immediato e elementare. Il deployment avviene così rapidamente perché include il minimo necessario.

In futuro, intendiamo trasformare Dark in un compilatore di infrastruttura, che creerà e avvierà un'infrastruttura ideale per le alte prestazioni e l'affidabilità delle applicazioni. Il deployment istantaneo, ovviamente, rimarrà.

Deployment sicuro

Editor strutturato

Il codice in Dark viene scritto nell'editor Dark. L'editor strutturato non consente errori di sintassi. In effetti, in Dark non c'è nemmeno un parser. Mentre inserisci del testo, lavoriamo direttamente con l'albero sintattico astratto (AST), così come Paredit, Sketch-n-Sketch, Tofu, Prune e MPS.

Qualsiasi codice incompleto in Dark ha una semantica esecutiva valida, più o meno come typed holes in Hazel. Ad esempio, se cambi una chiamata di funzione, conserviamo la vecchia funzione finché la nuova non diventa utilizzabile.

Ogni programma in Dark ha un proprio significato, quindi il codice incompleto non ostacola l'esecuzione di quello completato.

Modalità di modifica

Scrivi codice in Dark in due casi. Primo: stai scrivendo nuovo codice e sei l'unico utente. Ad esempio, si trova in REPL, e altri utenti non avranno mai accesso, oppure è un nuovo percorso HTTP a cui non fai riferimento da nessuna parte. Qui puoi lavorare senza alcuna precauzione, e attualmente operi più o meno in questo modo nell'ambiente di sviluppo.

Seconda situazione: il codice è già in uso. Se il codice gestisce traffico (funzioni, gestori di eventi, database, tipi), è necessario procedere con cautela. A questo scopo, blocchiamo tutto il codice utilizzato e richiediamo l'uso di strumenti più strutturati per la sua modifica. Di seguito parlerò degli strumenti strutturali: i flip switch per i gestori HTTP e degli eventi, una potente piattaforma di migrazione per i database e un nuovo metodo di gestione delle versioni per funzioni e tipi.

Flip switch

Uno dei modi eliminare la complessità superflua In Dark — risolvi diversi problemi con una sola soluzione. Gli switch delle funzionalità svolgono molte funzioni diverse: sostituzione dell'ambiente di sviluppo locale, branch git, distribuzione del codice e, naturalmente, il tradizionale rilascio lento e controllato di nuovo codice.

La creazione e la distribuzione di uno switch delle funzionalità avviene nel nostro editor in un'unica operazione. Crea uno spazio vuoto per il nuovo codice e fornisce controlli di accesso al vecchio e al nuovo codice, così come pulsanti e comandi per un passaggio graduale al nuovo codice o per escluderlo.

Gli switch delle funzionalità sono integrati nel linguaggio Dark, e anche gli switch non completi svolgono la loro funzione: se la condizione nello switch non è soddisfatta, viene eseguito il vecchio codice bloccato.

Ambiente di sviluppo

I pulsanti delle funzionalità sostituiscono l'ambiente di sviluppo locale. Oggi i team faticano a garantire che tutti utilizzino le stesse versioni degli strumenti e delle librerie (come strumenti di formattazione del codice, linters, gestori di pacchetti, compilatori, preprocessori, strumenti di testing, ecc.). Con Dark non è necessario installare le dipendenze localmente, gestire un'installazione locale di Docker o intraprendere altre azioni per garantire almeno una parvenza di uguaglianza tra l'ambiente di sviluppo e quello di produzione. Dato che tale uguaglianza è comunque impossibile, non ci faremo nemmeno illusione di aspirare a essa.

Invece di creare un ambiente locale clonato, i pulsanti in Dark creano un nuovo sandbox in produzione che sostituisce l'ambiente di sviluppo. In futuro prevediamo anche di creare sandbox per altre parti dell'applicazione (come cloni istantanei del database), anche se al momento non sembra così importante.

Rami e distribuzioni

Ora ci sono diversi modi per inserire un nuovo codice nei sistemi: rami git, fasi di deployment e feature toggle. Risolvono un problema in diverse parti del flusso di lavoro: git — nelle fasi prima del deployment, il deployment — nel momento in cui si passa dal vecchio codice al nuovo, e i feature toggle — per un rilascio controllato di nuovo codice.

Il modo più efficace sono i feature toggle (e sono anche il più semplice da capire e utilizzare). Con essi, possiamo completamente abbandonare gli altri due metodi. È particolarmente utile eliminare il deployment — se stiamo comunque usando i feature toggle per attivare il codice, allora il passaggio dei server al nuovo codice crea solo rischi inutili.

Git è difficile da usare, specialmente per i principianti, e questo ne limita notevolmente l'uso, ma ha rami molto comodi. Abbiamo attenuato molti dei difetti di git. Dark è modificabile in tempo reale e offre funzionalità di collaborazione in stile Google Documenti, così non è necessario inviare codice e possiamo eseguire meno frequentemente rebase e merge.

I commutatori delle funzioni sono alla base di un'implementazione sicura. Insieme alle distribuzioni istantanee, consentono di testare rapidamente concetti in piccoli frammenti a basso rischio, piuttosto che applicare un'unica grande modifica che potrebbe far crollare il sistema.

Versioning

Per modificare funzioni e tipi, utilizziamo il versioning. Se desideri cambiare una funzione, Dark crea una nuova versione di essa. Puoi poi richiamare questa versione utilizzando un interruttore nel gestore HTTP o degli eventi. (Se si tratta di una funzione profonda nell'albero delle chiamate, verrà creata una nuova versione di ogni funzione lungo il percorso. Potrebbe sembrare eccessivo, ma le funzioni non si sovrapporranno se non le utilizzi, quindi non te ne accorgerai nemmeno.)

Per le stesse ragioni, versioniamo anche i tipi. Abbiamo parlato in dettaglio del nostro sistema di tipi. nel post precedente.

Grazie al versioning di funzioni e tipi, puoi apportare modifiche all'applicazione in modo graduale. Puoi verificare che ogni singolo gestore funzioni con la nuova versione, senza dover apportare tutte le modifiche all'applicazione contemporaneamente (ma abbiamo strumenti per farlo rapidamente, se lo desideri).

È molto più sicuro rispetto al rilascio totale di tutto in una volta, come avviene attualmente.

Nuove versioni dei pacchetti e della libreria standard

Quando aggiorni un pacchetto in Dark, non sostituiamo subito l'utilizzo di ogni funzione o tipo nell'intero codice sorgente. Questo non è sicuro. Il codice continua a utilizzare la stessa versione che utilizzava, mentre aggiorni l'uso di funzioni e tipi alla nuova versione per ogni singolo caso tramite switch.

Come Dark distribuisce il codice in 50 ms
Screenshot di parte del processo automatico in Dark, che mostra due versioni della funzione Dict::get. Dict::get_v0 restituiva il tipo Any (da cui ci stiamo distaccando), mentre Dict::get_v1 restituisce il tipo Option.

Spesso introduciamo una nuova funzionalità nella libreria standard ed escludiamo le versioni obsolete. Gli utenti con versioni vecchie nel codice manterranno l'accesso, ma i nuovi utenti non potranno utilizzarle. Prevediamo di fornire strumenti per migrare gli utenti dalle versioni precedenti a quelle nuove con un solo passaggio, e ancora tramite switch delle funzionalità.

Dark offre anche un'opportunità unica: poiché eseguiamo il vostro codice di lavoro, possiamo testare noi stessi le nuove versioni, confrontando i risultati per nuove e vecchie richieste, per informarvi sulle modifiche. In questo modo, l'aggiornamento dei pacchetti, che spesso avviene alla cieca (o richiede test approfonditi per motivi di sicurezza), comporta molti meno rischi e può avvenire automaticamente.

Nuove versioni di Dark

La transizione da Python 2 a Python 3 si è protratta per un decennio ed è ancora un problema. Dal momento che stiamo creando Dark per la consegna continua, è necessario considerare questi cambiamenti nel linguaggio.

Quando apportiamo piccole modifiche al linguaggio, creiamo una nuova versione di Dark. Il vecchio codice rimane nella vecchia versione di Dark, mentre il nuovo codice viene utilizzato nella nuova versione. Per passare alla nuova versione di Dark, è possibile utilizzare gli interruttori o le versioni delle funzionalità.

Questo è particolarmente utile, considerando che Dark è emerso da poco. Molte modifiche nel linguaggio o nella libreria potrebbero rivelarsi inefficaci. Il versionamento graduale del linguaggio ci consente di effettuare aggiornamenti minimi, quindi possiamo permetterci di non affrettarci e rinviare molte decisioni riguardanti il linguaggio finché non avremo un numero maggiore di utenti, e quindi più informazioni.

Migrazioni dei database

Per una migrazione dei database sicura, esiste una formula standard:

  • Ridereferenziare il codice per supportare i nuovi e i vecchi formati
  • Trasformare tutti i dati nel nuovo formato
  • Rimuovere l'accesso ai dati obsoleti

Di conseguenza, la migrazione del database si protrae e richiede molte risorse. E accumuliamo schemi obsoleti, poiché anche semplici compiti, come correggere il nome di una tabella o di una colonna, non giustificano lo sforzo speso.

Dark offre una piattaforma di migrazione dei database efficace, che (si spera) semplificherà così tanto il processo che smetterete di averne paura. Tutti gli archivi dati in Dark (archivio di coppie chiave-valore o tabelle hash persistenti) hanno un tipo. Per migrare un archivio dati, basta assegnargli un nuovo tipo e una funzione di rollback e rollforward per convertire i valori tra i due tipi.

L'accesso agli archivi dati in Dark avviene tramite nomi di variabili versionati. Ad esempio, l'archivio dati Users sarà inizialmente chiamato Users-v0. Quando viene creata una nuova versione con un tipo diverso, il nome cambia in Users-v1. Se i dati sono stati salvati tramite Users-v0 e vi si accede tramite Users-v1, viene applicata la funzione di rollforward. Se i dati sono stati salvati tramite Users-v1 e vi si accede tramite Users-v0, viene applicata la funzione di rollback.

Come Dark distribuisce il codice in 50 ms
Schermata di migrazione del database con i nomi dei campi del vecchio database, espressioni di rollback e rollforward e istruzioni per abilitare la migrazione.

Utilizza i comandi delle funzionalità per indirizzare le chiamate da Users-v0 a Users-v1. Questo può essere fatto un gestore HTTP alla volta per ridurre i rischi, e i comandi funzionano anche per singoli utenti, così puoi verificare che tutto funzioni come previsto. Quando non ci saranno più utenti di Users-v0, Dark convertirà in background tutti i dati rimanenti dal vecchio formato al nuovo. Non te ne accorgerai nemmeno.

Test

Dark è un linguaggio di programmazione funzionale con tipizzazione statica e valori immutabili, pertanto la superficie di test è significativamente più piccola rispetto ai linguaggi orientati agli oggetti con tipizzazione dinamica. Ma i test sono comunque necessari.
In Dark, l'editor esegue automaticamente i test modulari in background per il codice in fase di modifica e per impostazione predefinita esegue questi test per tutti i comandi delle funzionalità. In futuro, vogliamo utilizzare i tipi statici per eseguire automaticamente il fuzzing del codice per individuare bug.

Inoltre, Dark gestisce la vostra infrastruttura in produzione, aprendo così nuove opportunità. Salviamo automaticamente le richieste HTTP nell'infrastruttura di Dark (attualmente salviamo tutte le richieste, ma in futuro intendiamo passare a una selezione). Testiamo il nuovo codice sulla base di queste richieste e eseguiamo test modulari, e se lo desiderate, potete facilmente trasformare le richieste interessanti in test modulari.

Da cosa ci siamo liberati

Poiché non abbiamo distribuzioni, ma funzionalità di attivazione, circa il 60% del pipeline di distribuzione rimane fuori. Non abbiamo bisogno di branch git o pull request, costruzione di risorse backend e contenitori, invio di risorse e contenitori nei registri o passaggi di distribuzione in Kubernetes.

Come Dark distribuisce il codice in 50 ms
Confronto tra un pipeline standard di continuous delivery (a sinistra) e la continuous delivery di Dark (a destra). In Dark, la consegna consiste in 6 passaggi e un ciclo, mentre la versione tradizionale include 35 passaggi e 3 cicli.

In Dark, il processo di distribuzione comprende solo 6 passaggi e 1 ciclo (passaggi che si ripetono più volte), mentre un moderno pipeline di continuous delivery comprende 35 passaggi e 3 cicli. In Dark i test vengono eseguiti automaticamente, senza che tu te ne accorga; le dipendenze vengono installate automaticamente; qualsiasi cosa riguardante git o Github non è più necessaria; non è necessario costruire, testare e inviare contenitori Docker; il deploy su Kubernetes non è più necessario.

Anche i passaggi rimanenti in Dark sono diventati più semplici. Poiché i flag delle funzionalità possono essere gestiti con un'unica azione, non è necessario percorrere di nuovo l'intero pipeline di distribuzione per rimuovere il codice obsoleto.

Abbiamo semplificato la fornitura del codice, riducendo i tempi e i rischi associati alla continuous delivery. Inoltre, abbiamo notevolmente semplificato l'aggiornamento dei pacchetti, le migrazioni del database, i test, la gestione delle versioni, l'installazione delle dipendenze, l'equità tra l'ambiente di sviluppo e quello di produzione e aggiornamenti rapidi e sicuri delle versioni del linguaggio.

Rispondo alle domande su questo su HackerNews.

Per saperne di più sul dispositivo Dark, leggi l'articolo su Dark, seguici su Twitter (o su me)) o iscriviti alla beta e ricevi aggiornamenti sui prossimi post. Se vai a StrangeLoop a settembre, unisciti a noi per il lancio.

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