Vad betyder egentligen “ett hamburgermål, tack”?
Förra gången använde vi en Dockerfile för att skapa vår egen “Docker-image (Image)”. Det är som ett “recept som aldrig förändras”, där det står exakt i vilken ordning man lägger biffen på brödet och salladen ovanpå.
Nu är det dags att använda det receptet för att starta själva verksamheten, alltså tjänsten. Men när man börjar använda Docker dyker det upp förvirrande termer. Container, Service, Stack… allt låter ganska likt, så vad är egentligen skillnaden?
Om man inte får grepp om de här begreppen kommer man senare att irra runt när man skriver deployskript och undra: “Varför startade jag containern, men tjänsten gick ändå inte upp?” För att reda ut det här släktträdet i dag har jag tagit med världens mest universella språk: analogin med hamburgerrestaurangen.

1. Container: hamburgaren som görs efter beställning
- Definition: den “faktiska varan” som uppstår när en image körs och läses in i minnet.
- Kännetecken: Du kan inte ge kunden receptlappen (imagen). Du måste servera en rykande varm “hamburgare (container)” som lagats enligt receptet.
- Kommando:
docker run my-burger
Om jag startar en serverinstans betyder det att jag har kört “en container”.
2. Service: lagerhantering för samma meny
Det blir lunchrusning och kunderna strömmar in. Med takten att laga en hamburgare i taget går det inte längre att hinna med. Därför bestämmer vi oss för att utöka kökspersonalen och förbereda tre “cheeseburgare” i förväg. Från den stunden hanterar jag inte längre bara “en cheeseburgare”, utan hela “cheeseburgarlinjen”, alltså Service.
- Definition: en “samling containrar” som skapats från samma image.
- Kännetecken: Du kan ge den en “policy”, till exempel “Ha alltid tre av den här menyn i lager (Replicas=3)”. Tappar du en hamburgare i golvet (fel) steker den själv en ny.
- Syfte: “utskalning (Scale-out)”. Fördelning av trafiken och stabilitet.
3. Stack: målet, alltså hela paketet
Enbart en hamburgare räcker inte. Du behöver också pommes frites, DB, och cola, Redis. Först när allt det där finns tillsammans får du en “fullständig måltid”, alltså applikationen.
- Definition: den “översta gruppen”, där olika tjänster (webb, DB, cache) har buntats ihop.
- Kännetecken: Hamburgaren och colan måste stå på samma bricka för att det ska vara smidigt att äta (nätverksanslutning). När de buntas ihop till en stack skapas ett eget utrymme (Network) där de kan kommunicera med varandra.
Problemet: jag tappar nästan andan bara av att beställa
Konceptet är tydligt, men när man faktiskt ska driva det i verkligheten börjar det göra ont i huvudet. Webbtjänsten, alltså stacken, som vi vill bygga behöver vanligtvis komponenter som följande.
- Spring Boot (Burger): 2 huvudrätter (redundans)
- PostgreSQL (Fries): 1 tillbehör
- Redis (Coke): 1 dryck
Att starta allt detta med råa Docker-kommandon, docker run, är som att beställa vid disken på det här sättet.
“Hej, jag vill ha en ‘cheeseburgare (Spring)’, men stek biffen medium-well, utan pickles och med extra mycket sås. Och ge mig också ‘pommes frites (DB)’, men utan salt och med tre ketchup-påsar. Till sist vill jag ha ‘cola (Redis)’ som cola zero och med bara halva mängden is. Och lägg de här tre sakerna på ‘en enda bricka (Network)’, tack.”
# I praktiken maste du skriva sadana kommandon varje gang
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...
Skulle du verkligen kunna rabbla upp den här långa beställningen utan ett enda fel varje gång du deployar? Och vad händer om ordningen blir fel och pommesen kommer senare än hamburgaren? Kunden, alltså användaren, kommer inte att bli glad.
Räddaren: Docker Compose – “Det vanliga, tack!”
Därför tänkte utvecklarna: “Tänk om vi kunde skriva ner den här komplicerade beställningen i förväg på en ‘meny, ett dokument’, och bara säga: ‘Ge mig nummer 1-målet’?”
Så föddes “Docker Compose”. Om vi definierar vårt eget “anpassade mål” i en fil som heter docker-compose.yml, ser Docker till att skicka beställningen till köket och ställa allt snyggt på brickan i rätt ordning.

[Action] Skapa ditt eget mål
Låt oss inte stanna vid ord; låt oss faktiskt starta den lokala Spring-servern och DB:n i ett enda svep. Allt du behöver göra är att skapa en docker-compose.yml-fil i projektets rotmapp och klistra in innehållet nedan.
version: '3.8'
services:
# 1. Pommes (DB)
my-database:
image: postgres:14
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
# 2. Cheeseburger (Web)
my-web-server:
image: my-spring-app:latest
ports:
- "8080:8080"
depends_on:
- my-database # "Pommes maste fritas forst innan burgaren gar ut" (ordning garanterad)
environment:
DB_URL: jdbc:postgresql://my-database:5432/mydb
Nu uttalar du den magiska formeln i terminalen.
# "En meny 1 (docker-compose.yml), tack!"
$ docker-compose up -d
Resultat: Docker läser den här menyfilen, startar DB:n först och startar sedan Spring-servern när DB:n är redo. Det här är precis början på konceptet IaC, Infrastructure as Code, alltså att hantera infrastruktur med kod. Från och med nu räcker det att dela en enda “menyfil” även för en komplex serverkonfiguration.
Avslutning: Works on My Machine? Works on ANY Machine!
Ursäkten som den här serien började med, “men det funkar på min maskin…”, håller inte längre. Vi har förstått Linux, alltså OS:et, isolerat miljöer med Docker, containern, och definierat komplexa tjänster i en enda fil med Docker Compose.
Nu kan jag, så länge jag har den docker-compose.yml-fil jag själv skrev, servera exakt samma hamburgermål på min dator, min teamledares dator eller på en AWS-server på bara en minut.
Vi är inte längre “bara kodare”. Vi har blivit “ingenjörer” som inte bara kan utforma koden vi skriver, utan också världen, miljön, där den koden ska köras. Därmed avslutas serien [Works on My Machine].
Från nästa serie går vi vidare till världen [The Pipeline: CI/CD-automation], där vi överlåter det tröttsamma deployarbetet till maskiner ovanpå den här stabila infrastrukturen.


