Hvad er en gentagelig build?
Deterministisk build eller reproducerbar build er lidt forskellige, men de kan forstås som det samme som denne artikel.
Reproducerbare builds refererer tilFlere udførelser af build-processen med samme input og build-miljø kan give præcis de samme resultater。 Denne teknologi er vigtig for softwareudvikling, distribution og sikkerhedsvalidering.
En build er reproducerbar, hvis den leverer præcis samme output uanset hvornår og hvor den køres. Uanset hvilken computer du kører på, hvilket tidspunkt på dagen, og hvilke eksterne tjenester du tilgår over netværket, producerer reproducerbare builds det samme byte-for-byte output. Dette er fantastisk både til udvikling (fordi reproducerbare builds er nemme at dele på tværs af forskellige udviklerenheder) og produktion (fordi det er nemt at sikre, at resultaterne af reproducerbare builds ikke er blevet manipuleret – bare kør buildet igen på din egen maskine og tjek, at resultaterne er konsistente!). er meget nyttige.
De tre søjler i gentagelige byggerier
Søjle 1: Gentagelige builds
Build-gentagbarhed refererer til, hvad der sker med selve build-maskinen. Hvis vi antager, at vores build-input er tilgængelige, og intet ændrer sig i verden omkring os, producerer vores build så det samme output, når det gentages?
Deterministisk installationsplan
Det første, simpleste og mest åbenlyse krav i en gentagelig konstruktion er en deterministisk afhængighedsinstallationsplan.
I de fleste sprog er det så simpelt som at tjekke en låst fil ind. Moderne build-værktøjer tillader ofte projekter at udtrykke direkte afhængighedskrav som begrænsninger og derefter løse disse begrænsninger for at generere en installationsplan (en liste over afhængighedsnavne og versionspar til installation). Mange af disse værktøjer genererer også låsefiler til serielle installationsplaner. Udviklere kan indsende disse låsefiler til versionskontrol, så fremtidige builds bruger de samme afhængighedsnavne og versioner.
Bemærk, at vi også har brug for deterministisk i selve afhængighedsbuildet (ikke kun versionsvalg), og en deterministisk installationsplan tillader os ikke at opnå dette!
Deterministisk konstruktion
Når vi ved, hvad vi skal bygge, skal vores build (inklusive vores egen kode og buildet af afhængighedskode) faktisk være deterministisk.
Det er måske ikke et problem for projekter uden et kompileringstrin! For eksempel er et Node-projekt med alle afhængigheder ren JavaScript, og der kræves ikke yderligere arbejde for at opnå effektiv deterministicitet.
For projekter, der inkluderer kompilerings- eller oversættelsestrin (kilde-til-kilde kompilering), er det klart sværeste at sikre determinisme i at bygge en reproducerbar build. Kompileringsprocessen kan implicit introducere ikke-determinisme på flere måder, herunder:
- Turing-komplette programbyggescripts kan ændre det kompilerede output efter behag.
- Post-installationsscripts, der er afhængige af eksekverbare filsystemopslag eller netværkskald.
- C-binding til en systeminstalleret pakke, hvor bindings på forskellige systemer med forskellige headere kan give forskellige output.
- Trin til at bygge en fil, der læses uden versionskontrol.
- Byg trin til at generere tidsstempler ved hjælp af systemtid.
- Trin til at bygge afhængigheder, som ikke er udtrykt i netværksdownload-installationsplanen (for eksempel download en NPM-afhængighed fra GitHub for en cachet binær build, der er C-bundet).
- Ændr adfærden baseret på den aktuelt satte miljøvariabel, men indsend ikke et build med miljøvariabelkonfigurationen.
Ikke alle disse adfærdsmønstre skaber nødvendigvis usikkerhed, når de er sat korrekt op, men korrekt konfiguration af byggeprocessen kan være komplekst og vanskeligt. For eksempel kan du læse dette blogindlæg om usikkerhed i Chromium-builds. Mange af disse problemer kan afbødes ved at kontrollere det lokale byggemiljø, som vi vil diskutere i næste afsnit.
Søjle 2: Uforanderligt miljø
Selv med gentagelige builds skal vi sikre, at buildinputtene ikke ændrer sig. Ofte betyder det, at vi vil sikre os, at vi bygger videre på et uforanderligt øjebliksbillede af vores omgivelser.
Uforanderligt lokalt miljø
Som vi diskuterede ovenfor, er en almindelig kilde til byggeusikkerhed at stole på "afhængigheder", som ikke fanges af byggeværktøjet. C-bundne systembiblioteker er de mest almindelige eksempler, men andre lokale miljøfaktorer såsom miljøvariableindstillinger og filer uden for versionskontrolens område kan også påvirke buildet.
En nem måde at afbøde dette problem på er at køre buildet i en kendt, uforanderlig container. For eksempel hjælper en container-runtime som Docker med at sikre, at alle bruger de samme systemafhængigheder, de samme miljøvariabler og kører på det samme filsystem. Derudover er det let at verificere, at indholdet af containeren matcher en kendt god build-container, og om nødvendigt kan containeren nemt fjernes helt fra det kendte gode billede og genskabes.
Bemærk, at vi er meget tydelige omkring kendte containere eller kendte containerbilleder. Det er ikke nok bare at indsende en Dockerfile! Hvorfor? Fordi Dockerfile ikke beskriver en fuldt reproducerbar byggeproces for Docker-images, fordi de ikke kører i et uforanderligt globalt miljø.
Uforanderligt globalt miljø
Build-systemer interagerer ofte med eksterne tjenester for at udføre opgaver som versionsopløsning og downloads af afhængigheder. Men eksterne tjenester ændrer sig ofte.
At køre apt install nodejs i dag vil give dig andre resultater end sidste år, og sandsynligvis vil næste år også give andre resultater. Derfor kan Dockerfiles ikke selv beskrive reproducerbare builds – at køre den samme Dockerfile på forskellige tidspunkter vil give forskellige build-output!
Den simple afbødning her er at konfigurere buildet, når det er muligt, og angive en nøjagtig version (helst også en nøjagtig content hash), så fremtidige builds bruger samme version som den nuværende build. Men eksterne tjenester kan også ændre deres adfærd uventet – en virkelig pessimistisk reproducerbar build kører et internt image med så mange af sine netværksressourcer som muligt.
Søjle 3: Ressourcetilgængelighed
Lad os sige, at vores build er gentagelig, og verden under vores fødder ikke ændrer sig. Alt, hvad vi mangler nu, er adgang til build-inputtet. Det virker simpelt, ikke? Brønd......
Registret fejler nogle gange
De fleste Node-udviklere har oplevet mindst ét NPM-udfald, hvor byggepipelinen uden caching eller spejling af NPM-pakker bliver forstyrret. Mange Node-udviklere har også oplevet fjernelser af venstre pad og falske enheder, hvilket har skadet NPM-økosystemet alvorligt og reelt udgjort en nedebrydelse.
Den eneste pålidelige måde at afbøde sådanne build-breaks på er at køre dit eget package registry mirror. Når eksterne tjenester ikke er tilgængelige, kan billedet forblive online; Når det officielle register sletter den gamle pakke, kan spejlet fortsætte med at levere tjenester. Det samme princip gælder for andre fjernservices: medmindre du kører dit eget image, er tilgængeligheden af en build pipeline kun sammenlignelig med tilgængeligheden af dens tjenester.
At vælge at køre et servicebillede er altid en delikat afvejning. På den ene side har registratorer som NPM dedikerede ingeniør- og driftsteams, der har ekspertisen til at holde disse systemer online. På den anden side er det meget nemmere at køre et lille billede for et lille sæt afhængigheder end at køre alle NPM-billeder. Du bør træffe mirroring-beslutninger baseret på specifikationerne for hver enkelt service, idet du tager højde for pålideligheden af historiske eksterne services og dit teams tilgængelighed og bemandingsbehov.
Leverandører sikrer maksimal tilgængelighed
En nem måde at sikre maksimal tilgængelighed af dit projekts afhængigheder på er at tilføje dem til din leverandør. De fleste pakkeadministratorer understøtter en form for "vendoring", hvilket betyder, at vi i stedet for at stole på downloads fra eksterne tjenester, gemmer afhængighedskildekoden i versionskontrol, hvor vi sameksisterer med vores kildekode. For eksempel kan det i Node ligne at committe node_modules til versionskontrol.
Selvom denne løsning ikke er perfekt (afhængigt af hvordan din leverandør og dit projekt er sat op, hvilket kan lægge meget pres på din versionskontrol), er det ofte den simpleste og letteste løsning for maksimal tilgængelighed.
Henvisning:
Hyperlink-login er synlig.
Hyperlink-login er synlig. |