CI/CD

CI/CD-grunder

En praktisk introduktion till hur leveranspipelines flyttar kod från commit till release på ett kontrollerat och repeterbart sätt.

CI/CD , kontinuerlig integration och kontinuerlig leverans , är praktiken att automatisera vägen från en utvecklares kodändring till en körande driftsättning. Kontinuerlig integration innebär att varje ändring byggs, testas och integreras automatiskt; kontinuerlig leverans utökar detta för att göra varje godkänd ändring releasebar på begäran; kontinuerlig driftsättning går längre och releasear automatiskt utan manuell kontrollpunkt. För DevSecOps är pipelinen inte bara en leveransmekanism , det är också ett kontrollsystem där säkerhetskontroller, godkännandeportar och proveniensuppföljning är inbyggda i varje release.

Lärandemål

Det här ska du kunna efter genomläsning.
  • Förklara de viktigaste stegen i en leveranspipeline och hur de hänger ihop.
  • Känna igen hur miljöer, godkännanden och rollback formar säkra releaser.
  • Beskriva varför repeterbarhet och tydlig hantering av artefakter är viktigt.

I korthet

En snabb mental modell innan du går på djupet.
Pipelineflöde
  • Build
  • Test
  • Artefakt
Releasekontroll
  • Deploy
  • Miljöer
  • Godkännanden
Bra arbetssätt
  • Stabila indata
  • Synliga utdata
  • Tydligt ägarskap

Kärnidén

Shift-left-principen innebär att problem som hittas tidigare i utvecklingscykeln är billigare och snabbare att åtgärda. En bugg fångad av ett enhetstest före commit är snabbare att fixa än samma bugg som hittas i en staging-miljö. CI/CD-pipelines operationaliserar shift-left genom att köra tester, säkerhetsskanning och policykontroller automatiskt på varje ändring.

Reproducerbarhet är den grundläggande kvaliteten hos en trovärdig pipeline. En reproducerbar build producerar samma utdata givet samma indata. Samma källkod, samma beroendeversioner, samma byggverktyg. Pinnade beroenden, lockfiler och hermetiska byggmiljöer är de tekniska medlen för att uppnå detta.

Skillnaden mellan CI och CD förväxlas ofta. CI (kontinuerlig integration) fokuserar på integreringssteget. Att slå ihop ändringar ofta så att den kombinerade kodbasen alltid är i ett känt, testat tillstånd. CD (kontinuerlig leverans) fokuserar på driftsättningssidan. Att säkerställa att varje godkänd build producerar en artefakt som kan driftsättas till vilken miljö som helst, på begäran.

De starkaste pipelines behandlar pipeline-definitionen som applikationskod. YAML-filer, skript och konfiguration som definierar hur programvara byggs och driftsätts ska finnas i versionskontroll, granskas innan de ändras och testas i icke-produktionsmiljöer.

Releaseväg

  • Varje pipeline-steg har ett tydligt syfte, definierade indata och ett explicit framgångsvillkor.
  • Befordra samma buildartefakt genom miljöer snarare än att bygga om i varje steg, ombyggnad introducerar risk att den testade artefakten skiljer sig från den driftsatta.
  • Releasebeslut baseras på dokumenterade kriterier, testresultat, skanningsfynd, godkännandeposter, inte på subjektiv tilltro.

Basnivå

  • Pinna versioner av byggverktyg, beroenden och basimages så att samma commit producerar samma artefakt om ett år.
  • Behandla miljöer som distinkta risknivåer. Utveckling är där experiment sker, staging validerar produktionslikhet, produktion tar emot bara det som passerat alla portar.
  • Definiera återställning innan du behöver den. Vem som auktoriserar den, vad den innebär och hur lång tid den förväntas ta.

Signaler att bevaka

Mönster som är värda att undersöka vidare.
  • Samma commit ger olika utdata vid olika körningar.
  • Driftsättning kräver manuella steg som inte är dokumenterade.
  • Rollback kräver gissningar eftersom releaseläget är oklart.

FÖRDJUPNING

Build

Ett build är processen att transformera källkod och dess beroenden till en körbar eller distribuerbar artefakt. En tillförlitlig build ska vara deterministisk. Samma indata ger alltid samma utdata. Determinism kräver att varje indata kontrolleras. Källkod (pinnad till en commit), beroenden (låsta till specifika versioner), byggverktyg (versionshanterade) och själva byggmiljön.

Icke-determinism i builds är ett vanligt och underskattad problem. Källor inkluderar. Hämtning av 'senaste' version av ett beroende, tidsstämplar inbäddade i artefakter, parallellt exekveringsordning och implicita miljövariabler som läcker in i bygget. Vart och ett av dessa skapar ett gap mellan vad som testades och vad som kan byggas vid en framtida körning.

Bygge-caching är en prestandaoptimering som lagrar resultaten av dyra steg och återanvänder dem när indata inte har ändrats. Caching kräver dock noggrant design. En cache som är nycklad på fel indata kan servera ett inaktuellt eller felaktigt resultat. Regeln är. Cachenycklar måste fånga allt som kan ändra resultatet av det cachade steget.

Distinktionen mellan felsöknings- och releasebuilds spelar roll för säkerheten. Felsökningsbuilds innehåller ofta ytterligare loggning, kontroller, felsökningssymboler och ibland hårdkodade testautentiseringsuppgifter. En produktionsdriftsättning ska alltid komma från en releasebuild, och pipelinen ska tillämpa detta.

Test

Testpyramiden beskriver rätt fördelning av testtyper i en leveranspipeline. I botten finns snabba enhetstester som validerar enskilda funktioner och klasser isolerat, dessa körs på millisekunder och bör vara de talrikaste. Integrationstester i mitten validerar att komponenter fungerar korrekt tillsammans. Längst upp finns end-to-end-tester som validerar hela systemet ur användarens perspektiv, dessa är långsammast och dyrast att underhålla.

Testhastighet spelar roll i CI/CD eftersom långsamma tester fördröjer återkoppling. En pipeline som tar 45 minuter att köra avskräcker utvecklare från att committa ofta och minskar värdet av kontinuerlig integration. Parallellisering, att köra oberoende testsviter samtidigt, är ett av de mest effektiva sätten att hålla pipelines snabba när testsviter växer.

Instabila tester är tester som ibland godkänns och ibland misslyckas utan någon förändring av koden. De är ett stort tillförlitlighetsproblem i pipelines. De urholkar förtroendet för testsviten (utvecklare lär sig att köra om misslyckanden snarare än att undersöka dem) och de maskerar ibland verkliga misslyckanden.

Röktester i produktion är en specifik testtyp. En minimal uppsättning automatiserade kontroller som körs omedelbart efter driftsättning för att bekräfta att kärnfunktionerna fungerar. Om en driftsättning bryter något grundläggande, tjänsten startar inte, inloggningssidan returnerar 500, API:t kan inte nå databasen, detekterar röktester det inom sekunder.

Artefakt

En artefakt är den versionshanterade, oföränderliga resultaten av ett build. Den kan vara en containerimage, en kompilerad binär, en Java JAR, ett Helm-paket eller en zip-fil med statiska tillgångar. Nyckelegenskaperna hos en bra artefakt är. Den är oföränderlig (en gång byggd ändras den aldrig), den är versionshanterad (varje version är distinkt och identifierbar) och den är spårbar.

Versionshanteringsstrategi kommunicerar avsikt. Semantisk versionshantering (MAJOR.MINOR.PATCH) är lämplig för bibliotek och API:er. Git commit-SHA:er är bra för interna tjänstedriftsättningar där spårbarhet är viktigare än mänskligt läsbara versioner. Fel val för kontexten leder till antingen förvirring om vad som ändrades eller förlorad spårbarhet.

Artefaktoföränderlighet är en stark garanti som förhindrar en klass av driftsättningsfel. Om en artefakt byggs om för produktion med något annorlunda indata är den driftsatta artefakten inte densamma som den testade. Att använda innehållsadresserad lagring (Docker image-digests, artefaktchecksummor) för att referera artefakter garanterar att exakt de bytes som testades är de som driftsätts.

Artefaktproveniensen, det dokumenterade registret över hur en artefakt producerades, är alltmer viktig för supply chain-säkerhet. SLSA (Supply-chain Levels for Software Artifacts) är ett ramverk som definierar nivåer av proveniengarantier, från grundläggande källkodsgranskning upp till ett hermetiskt, reproducerbart build med signerade proveniensintyg.

Driftsätt

Driftsättning är handlingen att flytta en releaseartefakt till en målmiljö och göra den att servera trafik. Den viktigaste distinktionen är mellan 'driftsätt' (den tekniska handlingen att lägga den nya versionen på servrar) och 'release' (affärshandlingen att dirigera trafik till den nya versionen). Att separera driftsättning från release möjliggör tekniker som dark launches.

Driftsättningsstrategier definierar hur övergången från gammalt till nytt hanteras. Rullande uppdateringar ersätter instanser inkrementellt och upprätthåller tillgänglighet under hela processen. Blue/green-driftsättningar upprätthåller två identiska miljöer och växlar trafik från en till den andra atomärt, vilket möjliggör omedelbar återställning. Canary-releaser dirigerar en liten procentandel av trafiken till den nya versionen först och ökar gradvis.

Funktionsflaggor är ett alternativ till riskfyllda driftsättningar. Istället för att driftsätta en stor funktion på en gång distribueras koden med funktionen bakom en flagga som initialt är av. Flaggan kan aktiveras inkrementellt. För interna användare först, sedan en procentandel av produktionsanvändare, sedan alla.

Hälsokontroller och beredskapssignaler är den återkopplingsloop som gör driftsättningar säkra. Innan någon orkestratör dirigerar trafik till en ny instans måste den instansen godkänna sin beredskapscheck. Innan en driftsättning förklaras lyckad väntar pipelinen under en definierad period under vilken felfrekvenser, latens och hälsokontrollresultat bekräftar att den nya versionen beter sig korrekt.

Miljöer

Miljöer representerar ökande exponeringsnivåer och tilltro i leveranspipelinen. Utveckling är där enskilda ändringar testas isolerat, ofta med mockade beroenden och lösa behörigheter. Staging är så nära produktion som teamet kan göra den. Samma infrastrukturmönster, samma konfigurationshanteringsmetod, samma dataskala. Produktion är det verkliga.

Frasen 'staging är produktionslik' döljer mycket svårighet i praktiken. Staging-miljöer har ofta annan data, annan trafikvolym, andra tredjepartsintegrationer och andra åtkomstkontroller. Varje skillnad är ett gap där ett problem kan gömma sig i staging och bara visa sig i produktion.

Efemerala miljöer, tillfälliga miljöer som skapas för en specifik pull request och rivs ned när den slås ihop, är ett alltmer vanligt mönster. De möjliggör integrationstestning mot verklig infrastruktur för varje PR snarare än bara i en delad staging-miljö.

Miljöbefordran bör vara ett avsiktligt steg, inte en automatisk kaskad. Att flytta från staging till produktion bör kräva explicit godkännande. Befordringssteget är också rätt plats att tillämpa konsekvens. Exakt samma artefakt som körde i staging är det som går till produktion.

Godkännanden

Godkännandeportar lägger till en mänsklig beslutspunkt i pipelinen vid stadier där risken eller osäkerheten motiverar det. För de flesta ändringar i en mogen pipeline ger automatiserade portar, testresultat, säkerhetsskanningsgränsvärden, tillräckligt förtroende utan mänsklig granskning. Godkännanden är mest värdefulla där automatiserade kontroller har begränsningar. Nya ändringar, högriskinfrastrukturmodifieringar.

En bra godkännandeport specificerar tydligt vad godkännaren förväntas verifiera. En användbar godkännandeinformation inkluderar. Vad som driftsätts, vad som har ändrats sedan den senaste driftsättningen, vilka tester som godkändes och vad återställningsplanen är. Godkännaren ska skriva under på något specifikt, inte stämpla ett pipelinesteg.

Efterlevnadsdrivna godkännanden (krävs för SOC 2, PCI DSS och liknande ramverk) kräver ofta att den person som godkänner en ändring inte är samma person som gjorde den (separation av plikter) och att godkännandet registreras med en tidsstämpel och godkännarens identitet.

Felmoden för godkännandeportar är stämpling. Godkännanden sker snabbt och utan verklig granskning eftersom godkännaren litar på de automatiserade kontrollerna. Stämplingsgodkännanden ger falsk trygghet. Om godkännanden inte tillför värde är lösningen bättre information för godkännaren eller bättre automatiserade kontroller.

Återställning

Återställning är förmågan att återgå till ett känt bra tillstånd efter en misslyckad driftsättning. Det är en grundläggande releaseteknikförmåga, inte en nödimprovisering. De två huvudstrategierna är återställning (att återgå till föregående version) och rullar framåt (att åtgärda problemet genom att snabbt driftsätta en ny version).

Kubernetes Deployments stöder återställning inbyggt. Kommandot 'kubectl rollout undo deployment/myapp' ersätter den aktuella Pod-mallen med den föregående och utför en rullande uppdatering i omvänd ordning. Detta är snabbt och tillförlitligt för tillståndslösa applikationer. För applikationer som hanteras med Helm är den motsvarande operationen 'helm rollback'.

Databasschemamigrationer är den svåraste delen av återställning. Om en driftsättning inkluderar både en kodändring och en databasmigration, kanske återställning av koden inte räcker om databasen nu är i ett tillstånd som den gamla koden inte förstår. Det bästa praxismönstret är att göra migrationer bakåtkompatibla.

Blue/green-driftsättningar gör återställning trivialt snabb. Om den nya (gröna) miljön har ett problem är det en enda routingändring att växla trafik tillbaka till den gamla (blå) miljön. Detta fungerar eftersom den gamla miljön fortfarande körs.

Releaseflöde

GitOps är ett releaseflödesmönster där det önskade tillståndet för driftsättningen lagras i ett Git-repositorium, och ett automatiserat system kontinuerligt stämmer av det faktiska driftsättningstillståndet för att matcha det. Infrastruktur och applikationskonfiguration deklareras i Git, ett verktyg som ArgoCD eller Flux bevakar repositoryt och tillämpar ändringar på klustret.

Trunk-baserad utveckling och feature branching representerar olika CI/CD-filosofier. I trunk-baserad utveckling committar alla utvecklare direkt till huvudgrenen (eller använder mycket kortlivade grenar som slås ihop inom en dag), och huvudgrenen är alltid möjlig att driftsätta. Feature branching fördröjer integration tills en funktion är 'klar', vilket kan leda till stora sammanslagningar och långsam återkoppling.

Releasetåg är ett mönster där driftsättningar sker på ett fast schema (veckovis, varannan vecka) oavsett vilka funktioner som är klara. Funktioner som missar tåget väntar på nästa. Detta mönster minskar koordinationsomkostnader i stora organisationer och gör driftsättningstidpunkten förutsägbar för intressenter.

Den viktigaste egenskapen hos ett hälsosamt releaseflöde är att huvudgrenen alltid är i ett driftsättbart tillstånd. Trasiga huvudgrenar saktar ner alla, urholkar förtroendet för pipelinen och gör det svårare att snabbt releasea fixar när det behövs.