# Deployment

## Hvad det er

Deployment er de trin, der tager en testet version af en applikation ud af
byggesystemet og sætter den i drift: det byggede image eller de byggede filer
lægges på de maskiner eller den platform, der skal køre dem, versionen får
konfigurationen til netop det miljø, den startes, og rigtige forespørgsler
flyttes over til den. På dansk bruges både det engelske ord og udrulning. Ordet
dækker både handlingen og resultatet, så man kan tale om en deployment, der gik
i stå ved databasetrinnet, og om de deployments, der kører i hvert miljø lige
nu. Det, der rulles ud, kan være et website, et API, et planlagt job, en
databaseændring eller backenden bag en mobilapp.

At deploye og at frigive er ofte to forskellige ting. At deploye lægger den nye
kode der, hvor den kan køre; at frigive afgør, hvem der får den at se. En
funktion bag et feature flag kan deployes om mandagen og slås til for kunderne
om torsdagen, uden at nogen rører serverne igen. Adskillelsen lader et team
sende kode ud i små, hyppige skridt, mens beslutningen om timing bliver hos dem,
der ejer produktet.

Mange udrulninger i dag sender et containerimage af sted. Open Container
Initiative blev oprettet i juni 2015 under Linux Foundation og udgiver tre
specifikationer omkring det: hvordan et image er bygget op, hvordan en runtime
kører det, når det er pakket ud, og hvordan registries gemmer og udleverer
images. Et image identificeres ved sit digest, en hashværdi af indholdet, så det
samme digest altid betyder de samme bytes. Et tag som `1.4.2` er en læsbar
henvisning til et image, og pusher man igen under samme tag, peger det på noget
nyt.

Metoden [The Twelve-Factor App](https://12factor.net/), som mange henviser til,
når talen falder på webtjenester, deler vejen fra kode til kørende tjeneste i
tre trin, der holdes skarpt adskilt. Byggetrinnet gør et commit til en
eksekverbar pakke. Udgivelsestrinnet kombinerer pakken med ét miljøs
konfiguration. Kørselstrinnet starter processer fra en valgt udgivelse. Hver
udgivelse får sit eget id og ændres aldrig, når den først findes; enhver ændring
giver en ny udgivelse, og derfor handler en tilbagerulning blot om at vælge en
tidligere.

Selve skiftet kan ske på flere måder, alt efter hvad tjenesten kan tåle. Ved
recreate stoppes den gamle version, før den nye startes, og man accepterer et
hul imellem. En rullende udrulning skifter instanser ud nogle få ad gangen, så
begge versioner betjener trafik et stykke tid; det er standarden for et
Kubernetes Deployment. Ved blue-green kører den nye version ved siden af den
gamle, al trafik flyttes på én gang, og den gamle står klar til at tage over
igen. En canary sender først en lille del af trafikken til den nye version og
udvider, så længe fejlrate og svartider holder sig pæne.

## Hvorfor det betyder noget

Det er ved udrulningen, at en ændring begynder at gøre gavn, og det er også her,
en fejl begynder at nå brugerne. Teams, der sjældent deployer, samler gerne
mange ændringer i hver udgivelse, så hver udrulning bærer mere risiko og, når
den går galt, flere mulige syndere. Små, hyppige udrulninger holder hvert skridt
let at overskue og let at fortryde.

En udrulning i hånden er kun så ensartet som den, der udfører den. Trin springes
over, tages i en anden rækkefølge eller gøres lidt anderledes af kollegaen, der
dækker en ferie. En udrulningspipeline tager de samme trin hver gang og
efterlader et spor: hvilket commit, hvilket artefakt, hvilket miljø og hvornår.
Det spor er stedet, hvor en gennemgang af en hændelse begynder, og ofte den
hurtigste vej til at svare på, hvad der ændrede sig.

Den vigtigste vane er at bygge én gang og forfremme resultatet. Artefaktet, der
blev testet i staging, skal være præcis det samme, som når produktion, og kun
konfigurationen må variere; den leverer miljøet, når artefaktet ankommer. Gemmer
man digestet ved bygningen og deployer efter digest, kan løftet efterprøves:

```sh
podman build -t registry.example.com/web:1.4.2 .
podman push --digestfile web.digest registry.example.com/web:1.4.2
kubectl -n staging set image deployment/web \
  web=registry.example.com/web@$(cat web.digest)
# samme digest går til produktion, når staging er godkendt
kubectl -n production set image deployment/web \
  web=registry.example.com/web@$(cat web.digest)
```

Tilbagerulning hviler på samme disciplin. Når udgivelser er uforanderlige og har
et id, betyder det at gå tilbage blot at pege på den forrige, og det tager
sekunder. Når hvert miljø bygger fra kildekoden, betyder det at bygge et gammelt
commit igen og håbe, at afhængighederne stadig opløses på samme måde.

## Sådan ser det ud i praksis

[Udrulningen af én platform til Azure blev ejet fra start til slut](/da/portfolio/owned-end-to-end-deployments-of-the-platform-71/),
og hver ændring tog den samme vej gennem udvikling, staging og produktion, uden
at nogen skubbede direkte ud i et levende miljø. Udrulningspipelinen bygger
images og sender dem ud, og Bicep-skabeloner holder målinfrastrukturen ens i
alle miljøer, så hver forfremmelse lander på matchende infrastruktur.
Indstillinger, der varierer fra miljø til miljø, holdes adskilt fra
hemmelighederne, og derfor kan ét build rejse gennem alle trin og samle hvert
miljøs indstillinger op ved ankomsten uden at blive bygget om undervejs. Siden
fortæller, at ændringer når hvert miljø ad den faste vej uden manuelle ad
hoc-deploys, og at udgivelser blev et kontrolleret trin, der forløber ens hver
gang.

En tidligere opgave viser, hvad den slags ensartethed er værd i timer.
[Automatisering af bygge-, test- og udrulningspipelines sparede omkring 4.000 timer](/da/portfolio/streamlined-ci-cd-processes-saving-4-000-hours-42/)
ved at gøre vejen fra ændring til produktion til en standard og fjerne manuelle
trin, der afhang af, hvem der udførte dem. Siden melder, at udgivelser blev både
hurtigere og mere pålidelige.

På virksomhedens egen platform holdes udrulning adskilt fra provisionering.
[Et containerplan bygget på Podman og Quadlet](/da/portfolio/deployed-a-container-plane-on-podman-and-quadlet-122/)
lader root forberede udrulningsvejen én gang. Derefter er hver udrulning ét fast
script, som en uprivilegeret bruger kalder gennem en snævert afgrænset regel,
der kun tillader den kommando uden argumenter. Scriptet bygger imaget og
genstarter tjenesten, så en almindelig udgivelse hverken kræver en root-shell
eller vilkårlige kommandoer.

## Hvor det går galt

De fleste problemer med udrulning opstår i forskellen mellem det, der blev
tjekket, og det, der faktisk ændrede sig.

**Produktion kører noget, staging aldrig har set.** Når hvert miljø bygger sin
egen kopi, opløses afhængigheder til nyere versioner, et base-image flytter sig
mellem to builds, eller et byggeflag er sat anderledes, og artefaktet, der
bestod testen, er et andet end det, kunderne rammer. Byg én gang, gem digestet,
forfrem det digest, og læg alt miljøspecifikt i konfigurationen frem for i
artefaktet.

**Et tag flytter sig, og en tilbagerulning lander på den forkerte version.** En
udrulning, der nævner `latest` eller genbruger et versionstag efter et nyt
build, afhænger af, hvad tagget peger på den dag, den kører.
Kubernetes-dokumentationen fraråder `:latest` i produktion, fordi det bliver
sværere at se, hvilken version der kører, og sværere at rulle ordentligt
tilbage. Brug versionstags, der aldrig genbruges, og lås digestet fast, hvor
platformen tillader det.

**Pipelinen melder succes, før den nye version virker.** Mange deploy-kommandoer
vender tilbage, så snart platformen har modtaget anmodningen, mens de nye
instanser stadig starter og måske går ned et minut senere. Et trin, der venter
på, at den nye version melder klar, og derefter sender nogle få rigtige
forespørgsler og tjekker svarene, gør en stille fejl til et rødt build, mens den
forrige udgivelse stadig er lige ved hånden.

**Ny kode og det gamle skema mødes midt i en udrulning.** Under en rullende
udrulning eller en canary betjener gamle og nye versioner trafik side om side
mod én database. En migrering, der omdøber eller sletter en kolonne, vælter de
gamle instanser, der stadig kører, og gør tilbagerulning usikker, fordi den
gamle kode forventer det, der blev fjernet. Fordel den slags ændringer over
flere udrulninger: tilføj den nye struktur først, send kode ud, der virker med
begge, og fjern først den gamle struktur, når ingen kørende version har brug for
den.

## Læs mere

- [Build, release, run](https://12factor.net/build-release-run) —
  Twelve-Factor-metodens argument for tre adskilte trin og udgivelser, der
  aldrig ændres, når de først er lavet.
- [Open Container Initiative](https://opencontainers.org/) — projektet under
  Linux Foundation bag specifikationerne for images, runtimes og distribution,
  som de fleste containerudrulninger hviler på.
- [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/main/spec.md)
  — manifester, lag, konfiguration og de digests, der identificerer et image.
- [Kubernetes Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
  — rullende opdateringer, recreate-strategien, fremdriftsfristen og
  tilbagerulning.
- [Kubernetes images](https://kubernetes.io/docs/concepts/containers/images/) —
  tags, digests, pull-politikker og hvorfor `:latest` gør tilbagerulning
  sværere.

<https://engineer.company/da/glossary/deployment/>
