Drie jaar geleden was Docker nauwelijks bekend. Vandaag kent bijna iedere IT-professional deze toepassing voor het inzetten van zogenaamde containers. Joe Beda, senior software-engineer bij Google, vertelde op een conferentie dat vrijwel alles bij Google in containers draait. Veel bedrijven doen dit ondertussen ook, of willen het in ieder geval gaan doen. Containers bestaan al langer, maar sinds de komst van Docker lijken ze pas echt populair. Is Docker een hype of ‘here to stay’?

Docker is naar eigen zeggen een open platform bedoeld voor softwareontwikkelaars en softwarebeheerders. Met Docker kunnen op eenvoudige wijze applicaties worden gebouwd, gedraaid, vanaf softwareontwikkeling in productie worden genomen en van het ene naar het andere  systeem worden verplaatst. Het platform bestaat met andere woorden uit onderdelen die vrij beschikbaar zijn en waar iedereen aan kan bijdragen. Tevens kunnen gebruikers het platform naar eigen behoefte aanpassen of uitbreiden.

Wat doet Docker?

Iedereen die weleens zelf software heeft geschreven, zal hebben opgemerkt dat de stap van het daadwerkelijk beschikbaar stellen van die software aan anderen een lastig proces kan zijn. Je moet rekening houden met de mogelijke systemen waar de software op moet draaien. Vragen die moeten worden beantwoord, zijn onder andere: om welke Linux-distributie gaat het? Is deze 32-bit of 64-bit? Is de programmatuur afhankelijk van andere software? En welke zaken moeten er daarnaast geconfigureerd worden? Al gauw wordt je geconfronteerd met een breed scala aan mogelijkheden, waarvoor scripts, procedures en documentatie moeten worden geschreven.

Docker belooft het proces van softwareontwikkeling tot het in productie nemen te ondersteunen. Hierin ligt de sleutel tot de populariteit van Docker.

 

Container-based deployment

Om deze populariteit van Docker goed te begrijpen, zou je software kunnen beschouwen als goederen. Stel je eens een Ruby-applicatie voor als een doos bananen, een PHP-applicatie als een stalen plaat, of een C++-programma als een zak aardappelen. Het vervoer van deze goederen van de ene plek naar de andere staat symbool voor het beschikbaar stellen van deze software aan anderen. Voor de vervoerder is dat een lastige en inefficiënte opgave, omdat er allerlei verschillende soorten goederen zijn, met ieder een eigen formaat, gewicht en eisenlijst. De dozen kunnen met de hand worden overgestapeld. De stalen plaat moet met een hijskraan worden getild. De zakken aardappelen misschien wel met een lopende band. De vervoerder moet diverse specialisten in dienst hebben, waarvan ieder verantwoordelijk is voor zijn eigen soort goederen.

 

Zo gingvrachtvervoer er vroeger aan toe. Op schepen gingen allerlei soorten lading mee, en specialisten laadden kratten, vaten en zakken in. Dit was een heel gepuzzel dat veel tijd kostte, en onnodig veel ruimte innam. In 1956 besefte een Amerikaan, Malcolm McClean, dat dit efficiënter en beter kon. Hij bedacht toen de zeecontainer, zoals we die nu nog steeds kennen.

Door alle denkbare verschillende goederen in containers te stoppen, kon er veel sneller, goedkoper en efficiënter worden geladen. Er waren immers geen specialisten meer nodig en er ging geen ruimte meer verloren. Vanwege de standaardafmeting van de zeecontainer, beschikte iedere haven voortaan over dezelfde hijskranen en vrachtwagens om de zeecontainers over te slaan.

 

Wat McClean betekende voor het vrachtverkeer, betekent Docker voor het beschikbaar stellen van applicaties. Het introduceert de standaardcontainer. Door een applicatie in een Docker-container te stoppen (te ‘Dockerizen’), wordt de verspreiding en beschikbaarstelling van deze applicatie vereenvoudigd. Hoe de applicatie eruit ziet, maakt door de komst van de container niet meer uit. Docker is een even grote revolutie voor software deployments als de standaard zeecontainer van McClean was voor de scheepvaart, omdat het complexiteit verbergt in een eenvoudige standaard.

Virtualisatie versus Docker

De kritische lezer denkt misschien: “maar dit konden we zonder containers toch al dankzij virtualisatie?” Dat klopt tot op zekere hoogte. Je kunt een applicatie, inclusief onderliggend besturingssysteem, isoleren in een virtuele machine (VM). Daarmee kan door het kopiëren van een VM een applicatie worden verspreid. Deze werkwijze heeft ons dankzij hulpmiddelen als Vagrant (www.vagrantup.com) en Packer (www.packer.io) al heel ver gebracht. Waarom Docker-containers in veel gevallen aantrekkelijker zijn, heeft te maken met de verschillen tussen VM’s en containers.

Bij virtualisatie is er altijd sprake van een fysieke host (gastheer). Dit is het systeem waarop alle VM’s draaien. Deze VM’s heten guests (gasten). Een host heeft een eigen besturingssysteem (OS) en virtualisatiesoftware (een hypervisor, zoals VMware, Xen of KVM). Iedere guest heeft ook een eigen besturingssysteem met daarop de applicatie geïnstalleerd.

 

Docker werkt ook met een host. Deze kan zowel fysiek als virtueel zijn. In plaats van een hypervisor heeft Docker de Docker-engine. Deze engine is een server die in plaats van VM’s containers start. De Docker-engine gebruikt de Linux kernel van de host en alle guest-containers krijgen als het ware een geïsoleerd stukje van de host tot hun beschikking. De containers hebben geen eigen besturingssysteem, alleen de functiebibliotheken (libraries; libs) en commando’s (binaries; bins) die relevant zijn voor de Linux-distributie, waarop de applicatie is gebouwd en draait. De applicatie kan prima draaien op de Linux kernel van de host, omdat deze in principe onafhankelijk van de Linux distributie is.

 

Je hebt met een container geen guest-OS meer nodig, waardoor containers veel kleiner zijn. Deze kleinere omvang zorgt ervoor dat de container gemakkelijker te verplaatsen is, minder het systeem belast en sneller start. Daarnaast zorgt het standaardformaat van containers ervoor dat je vrijwel onafhankelijk bent van het host-platform.

 

Docker onderdelen

De basis van Docker bestaat uit een executable en de Docker Hub. De executable heeft twee functies: het starten van de Docker-engine (de Docker daemon) en het beschikbaar stellen van het Docker-commando, waarmee Docker wordt gebruikt. Dit commando communiceert met de Docker-engine, die het feitelijke werk uitvoert. De Docker Hub is een online dienst, die zowel gratis als betaalde functionaliteit bevat voor het bouwen, opslaan en beschikbaar stellen van ‘Docker images’. Daarnaast levert Docker een aantal hulpmiddelen om eenvoudig hosts te installeren (Docker-machine), clusters te maken en te beheren (Docker-swarm) of meerdere containers tegelijkertijd te starten en te beheren (Docker-compose).

 

Images en containers

Een Docker-image bevat het onderliggende bestandssysteem van een container en de bijbehorende informatie (metadata) over deze image. Wanneer een image eenmaal is gemaakt, kan deze niet meer worden gewijzigd. Een image is een “dode” afspiegeling van een container. Een image heeft geen draaiende processen, gebruikt geen geheugen en verkeert altijd in dezelfde staat, ofwel is stateless. Een image is wat je deelt en verspreidt, bijvoorbeeld via Docker Hub. Een container is een draaiende instantie van een image. Daarmee kan een gebruiker dus interacteren. In een container draaien processen die geheugen gebruiken. Je kunt ook zeggen dat een container zich op ieder moment in een bepaalde staat verkeert, ofwel stateful is. Een container is een draaiende, stateful, instantie van de stateless image.

 

Iedere keer dat een container wordt gestart vanuit een bepaalde image zal deze zich initieel in dezelfde staat bevinden. Alle wijzigingen die worden gedaan in de container zullen dan ook verloren gaan, tenzij we Docker vertellen om een nieuwe image te maken uit de draaiende container.

 

Wil je meer weten over Docker? Lees het uitgebreide vervolg op dit artikel met hands-on online op www.linuxmag.nl!

 

 

 

 


 

<Vanaf hier niet in Linux Magazine maar online plaatsen. Ik heb de rest van deze tekst verder niet bekeken>

 

Met docker images vraag je allereerst de lijst met images op die op dat moment beschikbaar zijn op de host. In deze kersverse Docker-installatie zitten nog geen images. Met docker ps vraag je vervolgens de lijst op met draaiende containers. Er zijn geen images, en daarmee logischerwijs ook nog geen containers. Met docker run start je daarna een nginx webserver. Dit commando doet een aantal dingen. Ten eerste kijkt de Docker-engine of er een image met de naam nginx reeds beschikbaar is op de host. Zoals blijkt uit het voorbeeld is dit niet het geval, en besluit Docker om de image te downloaden in de Docker Hub. Dit downloaden wordt een pull genoemd. Nadat de image is gedownload start Docker de image. Door de opties –d en –P mee te geven stuurt Docker de draaiende container naar de achtergrond, en stelt de netwerkpoorten van nginx beschikbaar op willekeurige vrije netwerkpoorten van de host.

 

Kijk je met docker ps naar de lijst met draaiende containers, dan zie je de draaiende nginx-container. Hier is ook te zien welke netwerkpoorten op de host zijn gekoppeld aan nginx-poorten. Zo is poort 80 van nginx te vinden op poort 32769 van de host, en poort 32768 van de host verwijst door naar poort 443 van nginx. Start je een browser, en kijk je met een browser op poort 32769 van de host, dan zie je het volgende resultaat:

== afbeelding chrome_nginx.png ==

De grote kracht van Docker is dat je in circa 30 seconden een complete nginx-webserver hebt gedownload, gestart en beschikbaar hebt gesteld.

 

Layers

Bekijk je de output van het commando docker run, dan valt je misschien op dat er bij het downloaden van de image meerdere bestanden worden gedownload. Een Docker image bestaat voornamelijk uit het bestandssysteem met meerdere lagen (layers). De volgende illustratie toont hoe deze lagen werken.

 

== afbeelding image_filesystem.png ==

Iedere laag is een eigen image, gebaseerd op de onderliggende laag. Een laag is echter geen volledige kopie van de onderliggende laag. Een laag bevat slechts informatie over de wijzigingen ten opzichte van de onderliggende laag. In de afbeelding wordt de onderste laag base genoemd. In de laag daar bovenop (java) zie je dat bestand C is aangepast, en dat er een nieuw bestand (D) is toegevoegd. De java-laag bevat virtueel de bestanden B, C, D en E, terwijl de laag in werkelijkheid slechts bestanden C en D bevat. In de tomcat-laag is bestand D weer verwijderd, is bestand A toegevoegd en is er wederom een wijziging op bestand E. De tomcat-laag bevat dus virtueel bestanden A, B, C en E. Door het gebruik van overerving (inheritance), overschrijven (override), ‘uitgrijzen’ (whiteout) en copy-on-write worden Docker-images zo klein en efficiënt mogelijk gehouden. Het gebruik van lagen, ofwel de stapeling van images is noodzakelijk voor het maken van een nieuwe image.

 

 

== tekstvak ==

Copy-on-write

Copy-on-write is het principe dat pas wanneer een proces in een Docker-container een bestand wijzigt, er een kopie van het bestand wordt gemaakt in de huidige laag. Deze kopie bevat alle wijzigingen die de container maakt.

 

Op de achtergrond maakt Docker iedere keer wanneer een container wordt gestart automatisch een nieuwe toplaag aan. Deze laag is gebaseerd op de Docker-image waarmee de container werd gestart. Wanneer de container wordt gestopt verdwijnt deze laag, tenzij je de laag expliciet bewaart als image. Dit kan met het docker commit commando.

 

== Einde tekstvak ==

 

Een eigen image

Een Docker-image maken kan op twee manieren. Je kunt ten eerste de interactieve manier gebruiken. Omdat Docker-images gebaseerd zijn op Linux, kan je een interactieve shell starten in een container. Stel dat je bijvoorbeeld de extra package genaamd ssl-cert zou willen installeren in de image met nginx:

== afbeelding docker_bash_2.png ==

Hier start de container op een andere manier dan in het vorige voorbeeld. In plaats van –d zie je de optie –it. Hiermee start de container op de voorgrond in interactieve modus. Verder zie je de naam van de gewenste image, en het commando waarmee de container moet starten. Nu heb je een bash shell waarin je zaken kan doen zoals op alle Linuxsystemen. Omdat de base image van de nginx-container een Ubuntu-image is gebruiken we hier apt-get. Een container die op de voorgrond is gestart, stopt zodra het proces eindigt waarmee de container is gestart. Met de exit uit de bash shell is de container ook gestopt. Je kan de container die heeft gedraaid vervolgens weer vinden en opslaan als nieuwe image:

== afbeelding docker_bash_3.png ==

Het commando docker ps kan je de optie –a meegeven zodat je niet alleen de nu draaiende containers ziet, maar ook de containers die tot dan toe hebben gedraaid. Met behulp van het container-ID kan je de betreffende container met de opdracht docker commit opslaan als image. In het voorbeeld krijgt de image de naam ‘linuxmag/nginx’. Met het commando docker images kan je controleren of de image daadwerkelijk is gemaakt.

 

De tweede manier waarop je een image kunt maken is door middel van een Dockerfile. Dit is een tekstbestand met daarin instructies. De opdracht docker build begrijpt deze file, en kan de instructies uitvoeren. Het resultaat is een nieuwe image. Het eindresultaat van het vorige voorbeeld kan worden gereproduceerd met de volgende Dockerfile:

== afbeelding Dockerfile.png ==

De eerste instructie is altijd de FROM instructie. Een image bestaat uit lagen, en iedere laag is ook weer een image. Op het laagste niveau is er de zogenaamde base image. Dit zijn de meest elementaire images die vaak zijn gebaseerd op een Linuxdistributie. Zo is er bijvoorbeeld een Ubuntu en een CentOS base image. In het voorbeeld zie je dat de nieuwe image is gebaseerd op de nginx image uit Docker Hub. Op de tweede regel zie je een RUN instructie. Deze werkt net zoals een reguliere Linux shell, zoals je die ook zelf uit kunt voeren. Met dit bestand kan je vervolgens de nieuwe image bouwen door middel van het commando docker build:

== afbeelding docker_bash_4.png ==

Het resultaat is een wederom een nieuwe image waarin ook alle lagen van de onderliggende images zitten. In de praktijk wordt een Docker-build het meeste gebruikt. Het is namelijk zeer gemakkelijk om de Dockerfile samen met je broncode in een versiebeheersysteem te bewaren. Het bouwproces van de applicatie kan eenvoudig zo gemaakt worden dat de gebouwde applicatie automatisch in een image wordt gestopt.

 

Stateful versus Stateless

Omdat images stateless zijn lenen stateful applicaties zich minder makkelijk voor container-based deployments. Wanneer een container stopt, is de staat van de applicatie verdwenen. Wat overblijft is een image. Docker biedt echter wel voorzieningen die een applicatie kan gebruiken om zijn staat op te slaan. Je kan bijvoorbeeld in een Docker-container het bestandssysteem van de host delen, gebruiken, en daar data opslaan. Echter een modern datacentrum heeft duizenden servers, waar applicaties ten behoeve van hoge beschikbaarheid meervoudig zijn uitgevoerd. Dat leidt tot uitdagingen. Je moet dan nadenken hoe je deze data tussen de verschillende instanties van je applicatie, en de verschillende Docker hosts, gaat delen. Om deze uitdagingen (deels) uit de weg te gaan is het een actuele trend in software ontwikkeling om zogenaamde microservices te bouwen. Deze services zijn web applicaties die slechts éénspecifieke taak hebben en stateless zijn. Dit is een toepassing die uitermate geschikt is voor containers.

== tekstvak ==

Microservices

Er is geen formele definitie van wat een microservice is. Microservices zijn eigenlijk onderdeel van een stijl van software ontwikkelen waarin bepaalde eigenschappen centraal staan. Microservices zijn componenten in een groter geheel die één specifieke functie of taak hebben. Daarnaast moet het mogelijk zijn een microservice onafhankelijk te vernieuwen of verwijderen zonder dat omringende services of systemen daar hinder van ondervinden. Dit betekent in de praktijk dat de microservice zelf volledig stateless moet zijn, of zijn staat kwijt mag raken.

 

Verder typeren microservices zich door het feit dat ze een duidelijke goed omschreven interface hebben, die gebaseerd is op webtechnologie (met name HTTP). Een voorbeeld van een microservice zou een service kunnen zijn die een PDF-document genereert en teruggeeft uit een stuk tekst dat door middel van een HTTP POST naar de service wordt verzonden.

 

Microservices zijn interessant voor cloudtechnologie omdat ze zich door deze eigenschappen bij uitstek lenen voor horizontaal schalen (veel simpele kleine instanties) in plaats van verticaal schalen (enkele complexe grote instanties).

 

 == einde tekstvak ==

 

 

Er is veel mogelijk met Docker, maar je moet wel bekijken of de toepassing die je wilt gebruiken zich goed leent voor containers. Het Docker-‘ecosysteem’ ontwikkelt zich zeer snel. Toepassingen die nu minder goed geschikt zijn voor containers passen volgende week misschien wel. Crate bijvoorbeeld, is een database-applicatie die speciaal gemaakt is om juist in containers te kunnen draaien (https://crate.io). Dat terwijl een database toch een typisch voorbeeld is van een stateful applicatie.

 

Het juiste gereedschap

De populariteit van Docker is met name te danken aan het open platform, de standaardcontainer, het gebruiksgemak en de flexibiliteit. Je kunt met Docker zeer snel een nieuwe applicatie in productie nemen op een eenvoudige, voorspelbare en herhaalbare manier. Alhoewel met Docker veel mogelijk is, moet je je beseffen dat niet iedere toepassing zich even goed leent voor Docker. Met de huidige ontwikkeling van toepassingen in de cloud, bijvoorbeeld op basis van microservices, is Docker het juiste gereedschap en niet meer weg te denken.

 

Je kunt Docker eenvoudig zelf online uitproberen. Ga daarvoor naar https://www.docker.com/tryit/