
Jo raskere utviklingsprosessen er, jo raskere vokser teknologiselskapet.
Dessverre jobber moderne applikasjoner mot oss – systemene våre må oppdateres i sanntid uten å forstyrre noen eller forårsake nedetid eller avbrudd. Distribusjon til slike systemer blir utfordrende og krever komplekse kontinuerlige leveringsrørledninger selv for små team.
Disse rørledningene er vanligvis smale i omfang, trege og upålitelige. Utviklere må først bygge dem manuelt og deretter administrere dem, og selskaper ansetter ofte hele DevOps-team for å gjøre dette.
Utviklingshastigheten avhenger av hastigheten på disse rørledningene. De beste lagene distribuerer på 5-10 minutter, men vanligvis tar ting mye lengre tid, og krever flere timer per utplassering.
I mørke tar dette 50ms. Femti. Millisekunder. Mørkt - , bygget spesifikt for kontinuerlig levering, og alle aspekter av Dark, inkludert språket i seg selv, er bygget med sikker, umiddelbar distribusjon i tankene.
Hvorfor er rørledninger for kontinuerlig levering så trege?
La oss si at vi har en Python-nettapplikasjon og vi har allerede laget en fantastisk og moderne kontinuerlig leveringspipeline. For en utvikler som er opptatt med dette prosjektet hver dag, vil implementering av en mindre endring se omtrent slik ut:
Forandring
- Opprette en ny gren i git
- Gjør endringer bak funksjonsbryteren
- Enhetstesting for å teste endringer med og uten funksjonsbryter
Trekk forespørsel
- Forplikte endringer
- Sender endringer til et eksternt github-lager
- Trekk forespørsel
- CI bygger automatisk i bakgrunnen
- Kode anmeldelse
- Noen flere anmeldelser om nødvendig
- Slå sammen endringer til git master.
CI kjører på master
- Installere grensesnittavhengigheter via npm
- Montering og optimalisering av HTML+CSS+JS-ressurser
- Kjører enhet og funksjonstester i frontend
- Installere Python-avhengigheter fra PyPI
- Kjører enhet og funksjonstester i backend
- Tester integrasjon i begge ender
- Sender grensesnittressurser til CDN
- Bygge en beholder for et Python-program
- Sende en beholder til registeret
- Oppdater Kubernetes-manifestet
Bytter ut gammel kode med ny
- Kubernetes spinner opp flere forekomster av en ny beholder
- Kubernetes venter på at forekomster skal bli friske
- Kubernetes legger til forekomster til HTTP-lastbalanser
- Kubernetes venter til eldre forekomster ikke lenger er i bruk
- Kubernetes stopper gamle forekomster
- Kubernetes gjentar disse operasjonene til nye forekomster erstatter alle de gamle
Aktiverer en ny funksjonsbryter
- Den nye koden er inkludert kun for deg selv for å sikre at alt er bra
- Ny kode er aktivert for 10 % av brukerne, drifts- og forretningsverdier spores
- Ny kode er aktivert for 50 % av brukerne, drifts- og forretningsverdier spores
- Ny kode er aktivert for 100 % av brukerne, drifts- og forretningsverdier spores
- Til slutt gjentar du hele prosedyren for å fjerne den gamle koden og bytte
Prosessen avhenger av verktøyene, språket og bruken av tjenesteorienterte arkitekturer, men generelt ser det slik ut. Jeg har ikke nevnt distribusjon av databasemigrering fordi de krever mye planlegging, men jeg skal vise deg hvordan Dark håndterer dette nedenfor.
Det er mange komponenter her, og mange av dem kan lett bremse ned, krasje, forårsake midlertidig strid eller krasje det fungerende systemet.
Og siden disse rørledningene nesten alltid er laget for et spesielt tilfelle, er det vanskelig å stole på dem. Mange mennesker har dager da koden ikke kan distribueres fordi det er problemer i Dockerfilen, en av dusinvis av tjenester har krasjet, eller den nødvendige spesialisten er på ferie.
Enda verre, mange av disse trinnene gjør ikke noe nyttig i det hele tatt. Disse var nødvendig før når vi distribuerte kode direkte til brukere, men nå har vi veksler for ny kode og disse prosessene har blitt separert. Som et resultat har trinnet der koden distribueres (den gamle erstattes med den nye) nå rett og slett blitt en unødvendig risiko.
Selvfølgelig er dette en veldig gjennomtenkt rørledning. Teamet som opprettet det sparte ikke tid og penger på å distribuere det raskt. Vanligvis er utplasseringsrørledninger mye tregere og mer upålitelige.
Implementere kontinuerlig levering i Dark
Kontinuerlig levering er så viktig for Dark at vi siktet oss inn på undersekunderstider fra starten. Vi gikk gjennom alle stegene i rørledningen for å fjerne alt unødvendig, og polerte resten. Slik fjernet vi trinnene.
Jesse Frazel () laget et nytt ord deployless (krever ikke distribusjon) på Future of Software Development-konferansen i Reykjavik
Vi bestemte oss umiddelbart for at Dark ville være basert på konseptet "distribusjonsløst" (takk for neologisme). Utplasseringsløs betyr at enhver kode blir distribuert umiddelbart og klar til bruk i produksjon. Selvfølgelig vil vi ikke tillate ødelagt eller ufullstendig kode å passere gjennom (jeg skal beskrive sikkerhetsprinsippene nedenfor).
På Dark-demoen ble vi ofte spurt om hvordan vi kunne distribuere så raskt. Rart spørsmål. Folk tror nok at vi har kommet opp med en slags superteknologi som sammenligner kode, kompilerer den, pakker den inn i en container, starter en virtuell maskin, kaldstarter en container, og alt sånt – alt på 50 ms. Det er neppe mulig. Men vi opprettet en spesiell distribusjonsmotor som ikke trenger alt dette.
Mørk kjører tolker i skyen. La oss si at du skriver kode i en funksjon eller HTTP eller hendelsesbehandler. Vi sender diffen til det abstrakte syntakstreet (kodeimplementeringen som redaktøren og serverne våre bruker internt) til serverne våre, og kjører deretter den koden når forespørsler kommer inn. Så utrulling ser ut som bare en beskjeden oppføring i databasen - øyeblikkelig og elementær. Utrullingen er så rask fordi den involverer et minimum.
I fremtiden planlegger vi å gjøre Dark til en infrastrukturkompilator som vil skape og kjøre den ideelle infrastrukturen for høy ytelse og pålitelighet til applikasjoner. Umiddelbar distribusjon er selvfølgelig kommet for å bli.
Sikker distribusjon
Strukturert redaktør
Code in Dark er skrevet i Dark-editoren. Den strukturerte editoren tillater ikke syntaksfeil. Faktisk har Dark ikke engang en analysator. Mens du skriver, jobber vi direkte med abstrakt syntakstreet (AST), som , , , и .
Enhver uferdig kode i Dark har gyldig utførelsessemantikk, noe sånt som . Hvis du for eksempel endrer et funksjonskall, lagrer vi den gamle funksjonen til den nye er brukbar.
Hvert program i Dark har sin egen betydning, så uferdig kode forhindrer ikke at fullført kode fungerer.
Redigeringsmoduser
Du skriver kode i Dark i to situasjoner. Først: du skriver ny kode og du er den eneste brukeren. For eksempel er den i en REPL og andre brukere vil aldri få tilgang til den, eller det er en ny HTTP-rute som du ikke refererer til noe sted. Her kan du jobbe uten noen forholdsregler, og nå er det stort sett slik du jobber i utviklingsmiljøet.
Andre situasjon: koden er allerede i bruk. Hvis det er trafikk som går gjennom koden din (funksjoner, hendelsesbehandlere, databaser, typer), må du være forsiktig. For å gjøre dette blokkerer vi all brukt kode og krever bruk av mer strukturerte verktøy for å redigere den. Jeg skal dekke de strukturelle verktøyene nedenfor: funksjonsbrytere for HTTP og hendelsesbehandlere, en kraftig migreringsplattform for databaser og en ny versjonsmetode for funksjoner og typer.
Funksjonsbrytere
En vei i mørkt - fiks flere problemer med én løsning. Funksjonsbrytere utfører mange forskjellige oppgaver: erstatte det lokale utviklingsmiljøet, git-grener, distribuere kode, og selvfølgelig den tradisjonelle sakte og kontrollerte utgivelsen av ny kode.
Oppretting og distribusjon av en funksjonsbryter gjøres i én operasjon i redaktøren vår. Den skaper et hvitt rom for ny kode og gir tilgangskontroller for gammel og ny kode, samt knapper og kommandoer for å fase inn eller eliminere ny kode.
Funksjonsbrytere er innebygd i det mørke språket, og selv ufullstendige brytere tjener sin hensikt - hvis betingelsen i bryteren ikke er oppfylt, vil den gamle blokkerte koden bli utført.
Utviklingsmiljø
Funksjonsbrytere erstatter det lokale utviklingsmiljøet. I dag finner team det vanskelig å sikre at alle bruker de samme versjonene av verktøy og biblioteker (kodeformatere, linters, pakkebehandlere, kompilatorer, forprosessorer, testverktøy osv.) Med Dark er det ikke nødvendig å installere avhengigheter lokalt, administrere en lokal Docker-installasjon, eller iverksette andre tiltak for å sikre minst en likhet mellom utviklings- og produksjonsmiljøene. , vi vil ikke engang late som om vi streber etter det.
I stedet for å lage et klonet lokalmiljø, skaper brytere i Dark en ny sandkasse i produksjon som erstatter utviklingsmiljøet. I fremtiden planlegger vi også å sandboxe andre deler av applikasjonen (f.eks. øyeblikkelige databasekloner), selv om dette ikke virker så viktig akkurat nå.
Filialer og distribusjoner
Det er nå flere måter å introdusere ny kode i systemer: git-grener, distribusjonsfase og funksjonsvekslinger. De løser det samme problemet i forskjellige deler av arbeidsflyten: git i pre-distribusjonsstadiene, distribusjon i overgangen fra gammel kode til ny, og funksjonsbrytere for kontrollert utgivelse av ny kode.
Den mest effektive måten er funksjonsbrytere (samtidig den enkleste å forstå og bruke). Med dem kan du helt forlate de to andre metodene. Det er spesielt nyttig å fjerne distribusjonen - hvis vi bruker funksjonsbrytere for å aktivere kode uansett, skaper trinnet med å migrere servere til den nye koden bare unødvendige risikoer.
Git er vanskelig å bruke, spesielt for nybegynnere, og er svært begrensende, men det har praktiske grener. Vi har ryddet ut mange av manglene ved git. Mørk kan redigeres i sanntid og muliggjør samarbeid i Google Docs-stil slik at du ikke trenger å sende inn kode og du kan rebase og slå sammen sjeldnere.
Funksjonsbrytere er kjernen i sikker distribusjon. Sammen med umiddelbare distribusjoner lar de deg raskt teste konsepter i små, lavrisikobiter, i stedet for å forplikte deg til en stor endring som kan ødelegge systemet.
Versjonskontroll
Vi bruker versjonskontroll for å endre funksjoner og typer. Hvis du vil endre en funksjon, lager Dark en ny versjon av den funksjonen. Du kan deretter kalle denne versjonen ved å bruke en bryter i HTTP- eller hendelsesbehandleren. (Hvis det er en funksjon dypt inne i samtalegrafen, opprettes en ny versjon av hver funksjon underveis. Dette kan virke som overkill, men funksjonene kommer ikke i veien med mindre du bruker dem, så du vil ikke engang legge merke til.)
Av samme grunner versjonstyper vi. Vi snakket i detalj om typesystemet vårt .
Ved å versjonere funksjoner og typer kan du gjøre endringer i applikasjonen din gradvis. Du kan sjekke at hver enkelt behandler fungerer med den nye versjonen, uten å måtte gjøre alle endringene i applikasjonene dine på en gang (men vi har verktøy for raskt å gjøre dette hvis du vil).
Dette er mye tryggere enn å fullstendig distribuere alt på en gang, slik det skjer nå.
Nye pakkeversjoner og standardbibliotek
Når du oppdaterer en pakke i Dark, erstatter vi ikke umiddelbart bruken av hver funksjon eller type i hele kodebasen. Det er ikke trygt. Koden fortsetter å bruke samme versjon som den brukte, og du oppdaterer bruken av funksjoner og typer til den nye versjonen fra sak til sak ved hjelp av brytere.
Skjermbilde av en del av den automatiske prosessen i Dark, som viser to versjoner av Dict::get-funksjonen. Dict::get_v0 returnerte en Any-type (som vi forkaster), og Dict::get_v1 returnerte en Option-type.
Vi tilbyr ofte en ny funksjon i standardbiblioteket og ekskluderer eldre versjoner. Brukere med eldre versjoner i kode vil fortsatt ha tilgang til dem, men nye brukere vil ikke få tilgang til dem. Vi kommer til å tilby verktøy for å migrere brukere fra gamle versjoner til nye i ett trinn, igjen ved å bruke funksjonsbrytere.
Dark har også en unik funksjon: siden vi kjører produksjonskoden din, kan vi teste nye versjoner selv, sammenligne utdataene fra nye og gamle spørringer for å informere deg om endringer. Som et resultat utgjør pakkeoppdateringer som ofte gjøres blindt (eller krever omfattende sikkerhetstesting) mye mindre risiko og kan skje automatisk.
Nye versjoner av Dark
Overgangen fra Python 2 til Python 3 strakte seg over et tiår og er fortsatt en utfordring. Siden vi bygger Dark for kontinuerlig levering, må vi ta disse språkendringene i betraktning.
Når vi gjør små endringer i språket, lager vi en ny versjon av Dark. Den gamle koden forblir i den gamle versjonen av Dark, og den nye koden brukes i den nye versjonen. Du kan bruke brytere eller funksjonsversjoner for å oppgradere til en ny versjon av Dark.
Dette er spesielt nyttig med tanke på at Dark først nylig har blitt introdusert. Mange endringer i et språk eller et bibliotek kan mislykkes. Inkrementell språkversjon lar oss gjøre mindre oppdateringer, noe som betyr at vi kan ta oss god tid og utsette mange avgjørelser om språket til vi har flere brukere, og derfor mer informasjon.
Database migreringer
For sikker databasemigrering finnes det :
- Omskriv kode for å støtte nye og gamle formater
- Konverter alle data til nytt format
- Fjern gammel datatilgang
Som et resultat tar databasemigrering lang tid og krever mye ressurser. Og vi ender opp med at utdaterte skjemaer hoper seg opp fordi selv enkle oppgaver som å fikse et tabell- eller kolonnenavn ikke er verdt innsatsen.
Dark har en effektiv databasemigreringsplattform som (håper vi) vil gjøre prosessen så enkel at du ikke vil være redd for den. Alle datalagre i Dark (nøkkelverdilagre eller vedvarende hashtabeller) har en type. For å migrere et datalager, tilordner du det ganske enkelt en ny type og en rollback og rollforward-funksjon for å konvertere verdiene mellom de to typene.
Datalagre i Dark er tilgjengelig gjennom versjonsvariablenavn. For eksempel vil Users-datalageret i utgangspunktet hete Users-v0. Når en ny versjon med en annen type opprettes, endres navnet til Users-v1. Hvis data lagres via Users-v0 og du får tilgang til dem via Users-v1, brukes roll forward-funksjonen. Hvis data lagres gjennom Users-v1 og du får tilgang til dem gjennom Users-v0, brukes tilbakerullingsfunksjonen.
Databasemigreringsskjerm som viser gamle databasefeltnavn, rollover- og rollback-uttrykk og instruksjoner for å aktivere migrering.
Bruk funksjonsbrytere for å rute anrop til Users-v0 til Users-v1. Dette kan gjøres én HTTP-behandler om gangen for å redusere risikoen, og bryterne fungerer på en per-bruker-basis slik at du kan bekrefte at alt fungerer som forventet. Når det ikke er noen Users-v0 igjen, vil Dark konvertere alle gjenværende data i bakgrunnen fra det gamle formatet til det nye. Du vil ikke engang merke det.
Testing
Mørkt er og uforanderlige verdier, slik at testoverflaten er betydelig mindre sammenlignet med dynamisk skrivede objektorienterte språk. Men du må fortsatt teste.
I Dark kjører redaktøren automatisk enhetstester i bakgrunnen for koden du redigerer, og kjører disse testene for alle funksjonsbrytere som standard. I fremtiden ønsker vi å bruke statiske typer for å automatisk fuzze kode for å finne feil.
I tillegg kjører Dark infrastrukturen din i produksjon, noe som åpner for nye muligheter. Vi lagrer automatisk HTTP-forespørsler i Dark-rammeverket (vi lagrer alle forespørsler foreløpig, men da vil vi bytte til henting). Vi tester ny kode mot dem og kjører enhetstester, og hvis du vil kan du enkelt konvertere interessante spørringer til enhetstester.
Hva ble vi kvitt?
Siden vi ikke har distribusjon, men vi har funksjonssvitsjer, er omtrent 60 % av distribusjonsrørledningen igjen. Vi trenger ikke git-grener eller pull-forespørsler, bygge backend-ressurser og containere, skyve ressurser og containere til registre, eller distribusjonstrinn til Kubernetes.
Sammenligning av en standard kontinuerlig leveringsrørledning (venstre) og mørk kontinuerlig levering (høyre). Dark-leveringen består av 6 trinn og en syklus, mens den tradisjonelle versjonen inkluderer 35 trinn og 3 sykluser.
I Dark har en distribusjon kun 6 trinn og 1 syklus (trinn som gjentas flere ganger), mens en moderne kontinuerlig leveringspipeline består av 35 trinn og 3 sykluser. I Dark kjører tester automatisk uten at du en gang ser det; avhengigheter installeres automatisk; alt relatert til git eller Github er ikke lenger nødvendig; Det er ikke nødvendig å bygge, teste og sende Docker-containere; distribusjon til Kubernetes er ikke lenger nødvendig.
Selv de resterende trinnene i Dark ble enklere. Fordi funksjonsvekslere kan kontrolleres med en enkelt handling, er det ikke nødvendig å gå gjennom hele distribusjonsrørledningen en gang til for å fjerne gammel kode.
Vi forenklet kodelevering så godt vi kunne, og reduserte tiden og risikoen for kontinuerlig levering. Vi har også gjort det mye enklere å oppdatere pakker, databasemigreringer, testing, versjonskontroll, avhengighetsinstallasjon, likhet mellom utviklings- og produksjonsmiljøer, og raske og sikre språkversjonsoppgraderinger.
Jeg svarer på spørsmål om dette .
For å lære mer om Dark-enheten, les , (eller på ) eller . Hvis du skal til StrangeLoop i september, .
Kilde: www.habr.com
