Containers!

Ik ben me al een tijdje aan het verdiepen in Docker, het eenvoudig bruikbare container-platform waar tous les hippe developers op is overgestapt nadat Vagrant toch wat veel schijfruimte bleek te kosten op al die (van SSD's voorziene) MacBooks. Okee, dat is natuurlijk een beetje kort door de bocht, maar het lijkt toch wel zo te zijn dat waar men eerst, via Vagrant, een virtuele server optrok om in te webdevelopen, men er nu Docker voor gebruikt. En natuurlijk is dat lang niet het enige waar Docker handig voor is, of waar het in eerste instantie voor bedoeld was, maar hey.

Ik begin nu op een punt te komen dat ik het idee heb dat ik het hele principe een beetje snap, en dat ik bepaalde beginnersfouten niet zelf ook nog eens zou maken. Hoog tijd om dat eens te delen met u, de Gewaardeerde en Trouwe Lezer. Het is geen "van nul tot held"-tutorial, daarvan zijn er al genoeg en bovendien is de documentatie van Docker zelf vrij OK. Het is gewoon een lijstje van dingen die mij zijn opgevallen.

Waarom Docker? Je kunt toch gewoon Apache en PHP en zo op je ontwikkelmachine installeren?

Dat kan, maar een virtuele machine (VM) op je lokale omgeving is handiger dan "lokaal" een programmeertaalgebeuren + webserver + database + aanhangende zaken draaien, want het is veel minder gevoelig voor software-updates van het onderliggende OS, of eigenlijk überhaupt welk OS er onder ligt. Je kunt makkelijk (in een tekstbestandje, dat je met je code mee kunt inchecken) opgeven hoe je ontwikkelomgeving in elkaar steekt, en verder maakt het dan niet uit of die omgeving op Mac OS X, Windows of Linux wordt gedraaid. Dat was voor veel mensen dus ook de reden om Vagrant te draaien, maar een hele VM, met een volledig (meestal Linux-) OS erin, dat was nogal wat overhead. En het duurt zo lang eer ze opgestart zijn. En je duwt ze niet even over een trage wifi-verbinding heen. En het vreet sloten RAM als je er meer dan één tegelijk wil draaien.

Docker lost dat op door geen virtuele machine te draaien, maar gebruik te maken van containers in Linux. Die geven isolatie, net als een VM, maar hoeven niet voor elke container een heel OS te starten, wat gigantisch in de overhead scheelt. Het enige nadeeltje van Linux-containers is dat ze, je raad het al, alleen onder Linux beschikbaar zijn. Je kunt Docker op een Mac of Windows-machine draaien, maar dan draait het alsnog in een VM. Gelukkig wel een hele kleine, dus die start snel op en vult niet heel je SSD. Daardoor kun je veel sneller zorgen dat je op meerdere plekken, op meerdere hosts, toch je applicatie in exact dezelfde omgeving kunt laten draaien, van je ontwikkelmachine tot de uiteindelijke server(s).

Ik werk natuurlijk van oudsher op een Mac, dus...

...dus had Roeleveld braaf de Docker Toolbox binnengelepeld, die een Boot2Docker VM laten aanmaken (onder VirtualBox, want in tegenstelling tot lunches zijn virtualizers tegenwoordig wél gratis), en was ermee aan het spelen gegaan. Docker-terminal starten, docker run hello-world proberen, kraaien van plezier toen dat nog werkte ook. In een ander terminal docker run hello-world nog eens draaien, levert dan echter een lelijke foutmelding op.

Les: Docker heeft wat environment-variabelen nodig om goed te werken, en die worden niet automatisch globaal gezet. Daar gebruik je docker-machine env default voor, en de uitvoer daarvan kun je met eval $(docker-machine env default) in je shell laten injecteren.

Dat is dan wél weer aan een shell-profile toe te voegen zodat dat bij elke nieuwe shell die variabelen worden gezet, en ik de hele dag door terminalvensters kon openen en daar docker run hello-world in kon laten draaien. Jottem.

Maar ik doe ook wel eens aan Linux en Windows...

...en dat is verder prima. Onder Linux hoef je uiteraard geen VM te draaien en hoef je alleen Docker te installeren. Onder Windows gebruik je, net als op een Mac, een Boot2Docker VM waarbinnen alles zich afspeelt.

Overigens heb ik gelijk van de gelegenheid gebruik gemaakt om onder Windows eens met Hyper-V te spelen, in plaats van VirtualBox.

Les: Virtuele netwerken heten in Hyper-V virtual switches. Je maakt een switch aan, en om te voorkomen dat Docker later moeilijk doet, zoek je in je Netwerkcentrum die adapter op en zet je IPv6 uit...

Ik ben er nog niet achter of dat nou echt een harde beperking is van Docker, of van de shell tools van Windows, of dat ik het gewoon fout heb zitten doen, maar een lokale verbinding over IPv6 wil niet, en over IPv4 wel. En Hyper-V kun je alleen als Administrator besturen, dus alle Docker-commando's zullen moeten worden uitgevoerd in een shell die Administrator-rechten heeft.

En ik verbaasde zich over al die images en containers die maar rond leken te blijven slingeren...

...en leerde gaandeweg over images en containers en hoe die zich verhouden tot elkaar: een image is de blauwdruk voor wat er in een container komt te draaien. Images definieer je in een Dockerfile, en ze kunnen elkaar uitbreiden. Iemand heeft bijvoorbeeld ooit een image gemaakt met een Debian-installatie, en andere mensen kunnen op basis daarvan weer nieuwe images maken. Het leuke daarvan is, dat die nieuwe images alleen maar hoeven te onthouden waarin ze verschillen van dat eerste Debian-image; het wordt in laagjes over elkaar heen gelegd. Haal je met docker pull debian:jessie op bijvoorbeeld, dan krijg je de hele zwik van ongeveer 125MB. Haal je daarna een op datzelfde image gebaseerde image op (zeg met docker pull php:7), dan hoeft die 125MB alvast niet meer opgehaald te worden.

Les: Iedereen en zijn moeder maakt images aan op basis van Ubuntu. Ubuntu is behoorlijk groot, en over het algemeen overkill. Je kunt voor veel gevallen met een veel kleinere basis aan de gang, waardoor je container minder schijfruimte en RAM slurpt, sneller start, en veel minder software aan boord heeft die een attack vector kan vormen.

Met docker run <image> start je uiteindelijk zo'n image op en draait het, voor zolang het duurt, in een container, en tenzij je je container expliciet een naam hebt gegeven, verzint Docker er eentje. Containers kunnen kort draaien en daarna stoppen, of ze kunnen blijven draaien, afhankelijk van hoe je ze start. Containers die gestopt zijn, zijn met docker ps -a nog wel te zien, en daarna ook weer opnieuw te (her) starten met docker start -a <naam>. Dat verklaart meteen waarom alle containers een naam nodig hebben, zelfs al is het iets op het oog zinloos als romantic_goodall of furious_torvalds. Drie keer docker run hello-world levert drie aparte containers op. Met docker ps -a kun je ze zien, met docker rm <ID> gooi je ze weer weg. Als je van te voren al weet dat je je container niet wil bewaren, start je hem met --rm op, dan wordt-ie meteen na gebruik weggekieperd.

Containers zijn geen bestanden; images zijn dat natuurlijk wel. Met docker images zie je welke images je allemaal al hebt binnen gehaald en hoeveel ruimte ze uiteindelijk innemen - met alle basislagen op elkaar gestapeld, dus dat is niet hoeveel alles op schijf aan ruimte kost. Dat kun je wel zien met docker history <image-ID>, waarmee je bijvoorbeeld kunt zien dat het php:7-image begon met een debian-image van 125MB, maar een paar apt-get update && apt-get upgrades daar al zo'n 200MB aan hebben toegevoegd.

Les: Iedereen en zijn moeder begint met een basis-image en voegt daar met een package manager een heel bult softare aan toe. Dat is lang niet altijd nodig, en levert onnodig grote images op. Aangezien elke RUN-regel in je Dockerfile een toevoeging oplevert, moet je er spaarzaam mee omgaan.

Sleutelwoord is microcontainers. Zoals het in de best practices van Docker zelf wordt beschreven: run only one process per container en minimize the number of layers. De kunst is om je applicatie zo minimaal mogelijk draaiende te krijgen.

En ik ga in een volgend artikel nog wel een keer verder...

...want los van de basisbeginselen heb ik nog genoeg te ontdekken. Ik begin al wel een beeld te krijgen hoe ik een PHP-applicatie zou containeriseren en draaien, en er valt nog genoeg te beschrijven over hoe netwerken in zijn werk gaat, en hoe je zorgt dat je data tussen containers kunt delen, of kunt laten bewaren als een container wordt verwijderd. En ik wil eens spelen met een zwerm containers, want als je er ééntje zo gemakkelijk kunt opstarten, kun je er ook een cluster van starten. Om maar eens wat te noemen.

Wordt dus absoluut vervolgd.