Man kan læse utallige tutorials om at køre sin første container. Fejlsøgning af netværk i Docker Compose er et helt karrierespor for sig. Men mellem det legeplads-lab og den dag, hvor applikationen rent faktisk skal betjene kunder på en server et sted, er der et mellemrum. Det er ikke et teknisk mellemrum, men et mentalt et. Det handler om at stoppe med at tænke på containere som en måde at pakke noget ned på, og begynde at tænke på dem som infrastruktur. Og infrastruktur skal planlægges, selv når den er flygtig.
Jeg var selv skyldig i at overse dette skift. Min første ‘produktions’ container kørte på en udtjent virtuel maskine, og dens eneste livstegn var et opslag jeg satte på min egen skriveborden: ‘Husk at genstarte Docker hvis serveren crasher’. Det var ikke holdbart. Efter at have snakket med flere, der rent faktisk administrerer containere i større omfang – inklusive folkene fra www.dockendk.com – begyndte det at gå op for mig, hvor systemisk denne tilgang skal være. Det er ikke bare et spørgsmål om at få hjælp til opsætning, men om at indrette processer.
Her er de fem planlægningsmæssige forsnævringer, jeg ser igen og igen – og som jeg selv har begået.
Den stille krig om hukommelsen
På din lokale maskine er RAM nærmest en uendelig ressource. Du starter en container, den bruger 2 GB, og du tænker ikke mere over det. Men på en produktionsserver, hvor du kører ti, tolv eller halvtreds containere, bliver regnestykket anderledes. Det klassiske problem er ikke at containeren kræver for meget, men at den ikke frigiver hukommelsen korrekt. Jeg har set Java-applikationer i containere, der langsomt gnavede sig op på 90% af deres tildelte grænse og forblev der, fordi garbage collection ikke blev udløst som forventet.
Planlægningen her går på at forstå, hvordan dit specifikke sprog og framework opfører sig inden for en containers hukommelsesgrænser. Du er nødt til at sætte eksplicitte grænser med `-m` og teste, om applikationen fejler pænt, når den rammer dem. Ellers risikerer du, at containeren bliver dræbt af kernelens OOM killer, uden en klar fejlmeddelelse i dine logs. Det er en dårlig måde at finde ud af det på.
Logningen, der forsvandt ind i sig selv
I udviklingsfasen er logning nemt: du kører `docker logs myapp` og ser outputtet strømme. I produktion er den kommando værdiløs. Containere er designet til at være midlertidige; når de genstarter, er de standard-logs som udgangspunkt væk. Det er første og vigtigste pointe. Den anden er skala. Hvordan samler du logs fra 20 containere på én måde, så du kan se et sammenhængende billede af en fejl? Og hvad med log-rotation? En container, der loggger ustandseligt til sin egen `stdout`, kan fylde en disk, hvis den kører længe nok.
Løsningen er en centraliseret logningsstrategie fra dag ét. Det betyder at sende alle container-logs til et system udenfor Docker, som f.eks. en EFK-stak (Elasticsearch, Fluentd, Kibana) eller en tilsvarende skybaseret tjeneste. Det er en infrastrukturbeslutning, der kommer før den første produktionscontainer kører. At lade den vente, er at bede om at skulle genopbygge historik under en krise.
Jeg har min tvivl om, hvor mange virksomheder rent faktisk gør dette ordentligt fra starten. Presset for at få noget live er stort, og logning føles som et ‘nice-to-have’. Indtil det ikke gør.
- Hukommelsesadfærd under pres: Test ikke kun funktionalitet, test ressourceforbrug under belastning og ved længere levetid.
- Centraliseret logging: Ingen undtagelser. Alle container `stdout` og `stderr` skal videre til et eksternt system før opsendelse.
- Image-størrelse og sikkerhed: Det store, praktiske baseimage er en sikkerheds- og deploymentsbyrde. Skræl det ned.
- Health Checks med mening: De skal teste om applikationen er funktionel, ikke bare om processen kører.
- Rollback-planen: Hvordan ruller du én version tilbage hurtigt og sikkert uden at rode med hele opsætningen?
Det forkerte billede af et image
Der er en fristelse. Du bruger `ubuntu:latest` eller et andet omfattende baseimage, fordi det har alle værktøjerne, du kender. Det fungerer fint. Indtil du skal overføre dit 1.5 GB store image over netværket for tiende gang, og deployment tager ti minutter i stedet for to. Eller indtil en sikkerhedsanalyse viser hundredvis af sårbare pakker i dit lag, som din applikation aldrig rørte.
Planlægningen handler om at gøre et bevidst valg om image-størrelse og indhold. Brug officielle, slanke baseimages som `node:alpine` eller `python:slim`. Fjern build-afhængigheder fra det endelige produktionsimage. Dette reducerer overfladen for angreb og hastigheden for dine deployment markant. Det kræver ekstra arbejde i Dockerfilen, men det er arbejde, der betaler sig hver eneste gang du skal bygge eller distribuere.
Nogle gange føles det meningsløst at bruge timer på at skære et image ned med 300 MB, når serverne har terrabytes af plads. Men det handler ikke om plads. Det handler om hastighed, sikkerhed og disciplin. En lille, veldefineret container er lettere at forstå og fejlfinde.
At flytte til Docker i produktion er ikke bare en teknisk migration. Det er et skift i hvordan man tænker drift. De tekniske detaljer er vigtige, men de afgørende fejl bliver ofte begået på planlægningsniveauet, længe før den første `docker run` kommando bliver tastet. Det handler om at erkende, at selv de mest forgængelige enheder i vores systemer har brug for en varig plan.