Wat een container precies is
Je hebt een applicatie die lokaal draait, maar op de server omvalt met een fout die je nog nooit hebt gezien. Dat is het moment waarop je naar containers gaat kijken.
Docker verpakt je applicatie met alle dependencies in een image. Die image draait overal hetzelfde: op je laptop, op een testserver, in productie. Het probleem "maar op mijn machine werkte het wel" verdwijnt daarmee, niet omdat Docker toverij is, maar omdat je de omgeving zelf meelevert.
De vraag is niet of je Docker wilt. De vraag is wat het je kost, en of je die rekening wilt betalen.
Wat een container precies is
Een container is geen kleine virtuele machine. Dat onderscheid bepaalt alles wat daarna komt.Een VM heeft een eigen kernel, eigen drivers, eigen alles. Dat kost geheugen en CPU voordat je applicatie ook maar één regel heeft gedraaid. Een container deelt de kernel van de host en isoleert alleen wat eromheen zit: het bestandssysteem, de processen, het netwerk, de gebruikers.
Dat maakt containers licht. Je start er tientallen op een machine waar je er met VM's drie kwijt was. De prijs: je container en je host zitten aan elkaar vast via die kernel. Een verkeerde syscall, een kernelmodule die je nodig hebt, een afwijkende versie — en je container doet het niet. Een VM trekt zich daar niets van aan.
Op Linux werkt dit zoals het bedoeld is, met namespaces en cgroups. Op macOS en Windows draait Docker in een verborgen VM, want daar zijn die kernel functies niet. Het werkt, maar je betaalt in performance en in verrassingen die je op Linux nooit ziet.
De image is het echte werk
Een container is vluchtig. Een image is wat je bewaart.Een image bouw je uit een Dockerfile: een tekstbestand met instructies, laag voor laag. Elke laag is een diff ten opzichte van de vorige. Dat is efficiënt zolang je de volgorde goed kiest — zet wat vaak verandert onderaan, dan blijft de cache van de bovenste lagen staan.
Wil je weten wat er in een image zit, dan vraag je het op:
```bash docker image inspect nginx:1.27 ```
Je krijgt de lagen, de metadata, de labels, de configuratie. Met een Go-template of JSON filter je eruit wat je nodig hebt, wat handig is als je dit in een script stopt. Wil je weten hoe de image is opgebouwd, dan bekijk je de geschiedenis:
```bash docker image history nginx:1.27 ```
Elke regel is een instructie uit de Dockerfile, met de laag die eruit is gerold. Grote lagen onderaan betekenen meestal dat iemand iets heeft geïnstalleerd wat er niet hoort.
Delen via een registry
Een image deel je via een registry. Docker Hub is de standaard, met miljoenen publieke images. Voor privé-images of om zelf te publiceren meld je je aan:```bash docker login ```
Zonder serverargument gaat Docker naar `https://registry-1.docker.io/`. Op de meeste systemen heb je sudo of root nodig, tenzij je daemon anders is geconfigureerd. Daarna haal je images op met `docker pull` of bouw je ze zelf met `docker build`.
En dan het punt dat mensen vergeten: meld je weer af.
```bash docker logout ```
Op je eigen laptop maakt dat weinig uit. Op een gedeelde server of in een CI-omgeving laat je een sessie achter die iemand anders kan gebruiken. De credentials staan in `~/.docker/config.json`, en dat bestand leest iedereen met toegang tot die gebruiker.
Images verplaatsen zonder registry
Soms is er geen registry. Een air-gapped omgeving, een offline deployment, een back-up die je op een USB-stick zet.Dan sla je een image op als tar:
```bash docker image save -o nginx-1.27.tar nginx:1.27 ```
Zonder `-o` streamt Docker naar STDOUT, wat je direct door een pijp kunt sturen. In multi-architectuur omgevingen kun je één platformvariant pakken:
```bash docker image save --platform linux/arm64 -o nginx-arm64.tar nginx:1.27 ```
Terugladen gaat met:
```bash docker image load -i nginx-1.27.tar ```
Dit leest van STDIN of van een bestand en herstelt de image met de bijbehorende tags. Wat je niet terugkrijgt, is de geschiedenis van hoe de image is gebouwd — je krijgt het eindresultaat.
Heb je een bestaande tarball met een rootfs en wil je daar een image van maken, dan gebruik je `docker image import`. Dat is een ander pad dan `load`: je begint niet met een Docker-image maar met een bestandssysteem. Met `--change` voeg je Dockerfile-instructies toe, met `--message` een commitbericht, en op multi-platform systemen geef je het platform expliciet mee.
Logs zijn je enige venster
Een container draait, je applicatie doet iets, en je wilt weten wat. `docker logs` is dan je eerste en vaak je enige bron:```bash docker logs --follow --timestamps --tail 100 mijn-container ```
`--follow` houdt de stream open, `--timestamps` zet er tijdstempels voor, `--tail` beperkt het tot de laatste regels. Zonder die laatste optie krijg je alles vanaf het begin, wat bij een langlopende container een muur tekst is.
Dit werkt alleen als je applicatie naar STDOUT en STDERR schrijft. Schrijft hij naar een logbestand binnen de container, dan zie je hier niets. Dat is geen Docker-probleem, dat is een ontwerpkeuze die je terugkrijgt op het moment dat je iets moet debuggen.
Eén naam voor meerdere architecturen
Je bouwt een image voor `linux/amd64` en `linux/arm64`. Zonder meer moet iedereen zelf kiezen welke versie hij trekt. Met een manifestlijst niet:```bash docker manifest create mijnapp:1.0 \ mijnapp:1.0-amd64 \ mijnapp:1.0-arm64 ```
Daarna push je hem:
```bash docker manifest push mijnapp:1.0 ```
Nu trekt een x86-server en een Apple Silicon laptop dezelfde tag en krijgt elk het juiste beeld. Het commando heeft subcommando's voor aanmaken, inspecteren en pushen; de details staan in de registry v2 specificatie. Het is geen magie, het is een lijst met verwijzingen die de client leest voordat hij downloadt.
Plugins: alleen als je echt moet
Docker laat je uitbreiden met plugins. Een plugin maak je uit een rootfs-map en een `config.json`:```bash docker plugin create mijn-plugin ./plugin-data ```
De map moet die twee onderdelen bevatten. Met `--compress` gzip je de context tijdens het aanmaken. Plugins zijn er voor netwerkdrivers en volume-aandrijvingen — dingen die het gedrag van Docker zelf veranderen.
De eerlijke versie: de meeste teams hebben nooit een plugin nodig. Het is een uitweg voor wie tegen de grenzen van de standaardfunctionaliteit aanloopt, en die grens ligt verder dan je denkt.
Wat je hiermee op je bord haalt
Docker zelf orchestreert niets. Het start containers, het stopt ze, het beheert images. Meer niet. Zodra je meerdere containers over meerdere machines wilt verdelen, met herstart bij falen en schaalvergroting onder load, heb je iets anders nodig: Kubernetes, Docker Swarm, Nomad.Dat is de rekening. Enkelvoudige containers op één server zijn eenvoudig te begrijpen en te beheren. Zodra je gaat orchestreren komt er een laag bij die je moet leren, moet monitoren en moet upgraden. Kubernetes is daar de zwaarste in, en dat is geen verwijt — het kan ook het meeste.
Er is ook een gesloten alternatief dat goed werk levert. De managed containerplatforms van de grote clouds nemen je het beheer van de control plane uit handen. Je levert in op controle en op portabiliteit, je krijgt er gemak en SLA voor terug. Wie geen zin heeft om op zondagavond zijn etcd-cluster te redden, is daar beter af.
Zelf hosten klinkt prachtig, tot de eerste keer dat je `docker manifest` moet debuggen omdat iemand de verkeerde architectuur heeft gepusht. Dan ben je geen gebruiker meer die een ticket indient. Dan ben je de persoon die het uitzoekt.
Waar het van afhangt: hoeveel containers je draait, of je ze zelf wilt beheren, en of je een team hebt dat de orchestratielaag kan onderhouden. Eén applicatie op één server met `docker run` en een systemd-unit eromheen is prima. Twintig microservices over drie nodes is een ander gesprek, en dan is de vraag niet meer of je Docker gebruikt, maar wat je eromheen bouwt.