Docker-containers en services: Docker begrijpen met hamburgers

Wat betekent “één hamburgermenu graag” eigenlijk?

De vorige keer hebben we met een Dockerfile ons eigen “Docker-image (Image)” gemaakt. Dat is als een “recept dat nooit verandert”, waarin precies staat in welke volgorde je de patty op het broodje legt en daarna de sla toevoegt.

Nu is het tijd om met dat recept de echte zaak te beginnen, oftewel de service. Maar zodra je Docker gaat gebruiken, duiken er verwarrende termen op. Container, Service, Stack… ze lijken allemaal op elkaar, dus wat is nu precies het verschil?

Als je deze begrippen niet goed begrijpt, raak je later bij het schrijven van deployscripts verdwaald en vraag je je af: “Waarom heb ik de container gestart, maar komt de service nog steeds niet omhoog?” Om die stamboom vandaag op orde te krijgen, heb ik de meest universele taal ter wereld meegenomen: de hamburgerzaak-analogie.

Je maakt een hamburger, de container, volgens het recept, het image, beheert hetzelfde menu als service en verkoopt het gebundeld als combo, de stack.

1. Container: de hamburger die na de bestelling wordt gemaakt

  • Definitie: het “echte exemplaar” dat ontstaat wanneer je een image uitvoert en in het geheugen laadt.
  • Kenmerk: je kunt de klant geen receptbriefje (het image) geven. Je moet een gloeiend hete “hamburger (container)” serveren die volgens het recept is bereid.
  • Commando: docker run my-burger

Als ik één serverinstantie opstart, betekent dat dat ik “één container” heb uitgevoerd.

2. Service: het voorraadbeheer van hetzelfde menu

Het is lunchtijd en de klanten stromen binnen. Met het tempo van één hamburger per keer kom je er niet meer. Dus besluiten we meer keukenpersoneel in te zetten en alvast drie “cheeseburgers” klaar te maken. Vanaf dat moment beheer ik niet langer alleen “één cheeseburger”, maar de hele “cheeseburgerlijn”, oftewel de Service.

  • Definitie: een “verzameling containers” die uit hetzelfde image zijn gemaakt.
  • Kenmerk: je kunt er een “beleid” aan koppelen, zoals “houd van dit menu altijd 3 stuks op voorraad (Replicas=3)”. Laat je een hamburger vallen (een fout), dan wordt er vanzelf een nieuwe gebakken.
  • Doel: “horizontaal schalen (Scale-out)”. Verkeer spreiden en stabiliteit.

3. Stack: het combomenu, oftewel het volledige pakket

Alleen een hamburger is niet genoeg. Je hebt ook friet, de DB, en cola, Redis, nodig. Pas als al die onderdelen samenkomen, krijg je een “volledige maaltijd”, oftewel de applicatie.

  • Definitie: de “overkoepelende groep” waarin verschillende services (web, DB, cache) zijn gebundeld.
  • Kenmerk: een hamburger en cola eten het makkelijkst als ze op één dienblad staan (netwerkverbinding). Zodra ze in een stack zijn gebundeld, ontstaat er een eigen ruimte (Network) waarin ze met elkaar kunnen communiceren.

Het probleem: ik raak al buiten adem van alleen al bestellen

Het concept is duidelijk, maar zodra je dit echt wilt draaien, krijg je hoofdpijn. De webservice, oftewel de stack, die we willen bouwen, heeft meestal componenten zoals de volgende nodig.

  1. Spring Boot (Burger): 2 hoofdgerechten (redundantie)
  2. PostgreSQL (Fries): 1 bijgerecht
  3. Redis (Coke): 1 drankje

Dit allemaal starten met ruwe Docker-commando’s, docker run, is alsof je aan de balie op deze manier bestelt.

“Goedemiddag, ik wil graag een ‘cheeseburger (Spring)’, maar bak de patty medium-well, zonder augurk en met extra veel saus. O ja, geef me ook ‘friet (DB)’, maar zonder zout en met drie zakjes ketchup. En tot slot de ‘cola (Redis)’ als cola zero, met maar half zoveel ijs. En leg die drie dingen alstublieft op ‘één dienblad (Network)’.”

# In de praktijk moet je elke keer dit soort commandos typen
$ 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 ...

Zou je deze lange bestelling echt elke keer foutloos kunnen opdreunen wanneer je deployt? En wat als de volgorde verkeerd gaat en de friet later komt dan de hamburger? De klant, oftewel de gebruiker, zal niet blij zijn.

De redder: Docker Compose – “Het gebruikelijke graag!”

Daarom dachten ontwikkelaars: “Kunnen we deze ingewikkelde bestelling niet vooraf opschrijven op een ‘menukaart, document’, zodat we gewoon kunnen zeggen: ‘Doe mij menu nummer 1’?”

Zo is “Docker Compose” ontstaan. Als we in een bestand met de naam docker-compose.yml ons eigen “aangepaste combomenu” definiëren, zorgt Docker ervoor dat de bestelling naar de keuken gaat en alles netjes in de juiste volgorde op het dienblad terechtkomt.

Zelfs een ingewikkelde maatwerkbestelling is met één zin geregeld, “ik neem deze”, als die al op de menukaart staat.

[Action] Je eigen combomenu maken

Laten we het niet bij woorden laten; laten we echt in één keer de lokale Spring-server en DB opstarten. Het enige wat je hoeft te doen, is in de hoofdmap van het project een bestand docker-compose.yml maken en de onderstaande inhoud plakken.

version: '3.8'

services:
  # 1. Friet (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 # "De friet moet eerst klaar zijn voordat de burger gaat" (volgorde gegarandeerd)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

Spreek daarna in de terminal de magische woorden uit.

# "Een combo 1 (docker-compose.yml), graag!"
$ docker-compose up -d

Resultaat: Docker leest dat menubestand, start eerst de DB op en start daarna de Spring-server zodra de DB klaar is. Dit is precies het begin van het concept IaC, Infrastructure as Code, oftewel infrastructuur beheren met code. Vanaf nu is zelfs een complexe serverconfiguratie opgelost door gewoon één “menubestand” te delen.

Tot slot: Works on My Machine? Works on ANY Machine!

Het excuus waarmee deze serie begon, “maar op mijn machine werkt het…”, houdt nu geen stand meer. We hebben Linux, het OS, begrepen, omgevingen geïsoleerd met Docker, de Container, en complexe services in één bestand gedefinieerd met Docker Compose.

Nu kan ik, zolang ik alleen het door mij geschreven docker-compose.yml-bestand heb, exact hetzelfde hamburgermenu op mijn computer, die van mijn teamlead of op een AWS-server neerzetten in slechts één minuut.

We zijn nu niet langer “gewoon coders”. We zijn “engineers” geworden die niet alleen de code kunnen ontwerpen die ze schrijven, maar ook de wereld, de omgeving, waarin die code zal draaien. Daarmee eindigt de serie [Works on My Machine].

Vanaf de volgende serie trekken we de wereld van [The Pipeline: CI/CD-automatisering] in, waarin we het vervelende deployproces op deze stevige infrastructuur aan machines overlaten.

Plaats een reactie